Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Hacky support for strict mode #175

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/js/instrument/astUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ if (typeof J$ === 'undefined') {
// inside a setter
SETTER:7,

TYPEOF:8
TYPEOF:8,
// inside a directive ("use strict")
DIRECTIVE:9,
};

/**
Expand Down Expand Up @@ -132,12 +134,16 @@ if (typeof J$ === 'undefined') {
newContext = CONTEXT.IGNORE;
} else if (object.key && key === 'value' && object.kind === 'get') {
newContext = CONTEXT.GETTER;
child["__jalangi__getter__prop__name"] = object.key.name;
} else if (object.key && key === 'value' && object.kind === 'set') {
newContext = CONTEXT.SETTER;
child["__jalangi__setter__prop__name"] = object.key.name;
} else if (type === 'CallExpression' && key === 'callee' && child.type === 'Identifier' && child.name === 'eval') {
newContext = CONTEXT.IGNORE;
} else if (type === 'UnaryExpression' && key === 'argument' && object.operator === 'typeof' && child.type === 'Identifier') {
newContext = CONTEXT.TYPEOF;
} else if (object.directive && type === 'ExpressionStatement' && key === 'expression') {
newContext = CONTEXT.DIRECTIVE;
} else {
newContext = CONTEXT.RHS;
}
Expand Down
59 changes: 47 additions & 12 deletions src/js/instrument/esnstrument.js
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,9 @@ if (typeof J$ === 'undefined') {

function getFnIdFromAst(ast) {
var entryExpr = ast.body.body[0];
if (entryExpr.directive === 'use strict') {
entryExpr = ast.body.body[1];
}
if (entryExpr.type != 'ExpressionStatement') {
console.log(JSON.stringify(entryExpr));
throw new Error("IllegalStateException");
Expand Down Expand Up @@ -873,8 +876,21 @@ if (typeof J$ === 'undefined') {

function createCallAsFunEnterStatement(node) {
printIidToLoc(node);
var funValueExpr;
if (node.id !== null && node.id.name !== null) {
funValueExpr = node.id.name;
} else if (HOP(node, "__jalangi__getter__prop__name")) {
var propDescCall = "Object.getOwnPropertyDescriptor(this,'" + node["__jalangi__getter__prop__name"] + "')";
funValueExpr = logIFunName + "((" + propDescCall + ") ? " + propDescCall + ".get : arguments.callee)";
} else if (HOP(node, "__jalangi__setter__prop__name")) {
var propDescCall = "Object.getOwnPropertyDescriptor(this,'" + node["__jalangi__setter__prop__name"] + "')";
funValueExpr = logIFunName + "((" + propDescCall + ") ? " + propDescCall + ".set : arguments.callee)";
} else {
throw new Error("no function name found! " + JSON.stringify(node));
//funValueExpr = "arguments.callee";
}
var ret = replaceInStatement(
logFunctionEnterFunName + "(" + RP + "1,arguments.callee, this, arguments)",
logFunctionEnterFunName + "(" + RP + "1," + funValueExpr + ", this, arguments)",
getIid()
);
transferLoc(ret[0].expression, node);
Expand Down Expand Up @@ -971,12 +987,17 @@ if (typeof J$ === 'undefined') {

function wrapFunBodyWithTryCatch(node, body) {
if (!Config.INSTR_TRY_CATCH_ARGUMENTS || Config.INSTR_TRY_CATCH_ARGUMENTS(node)) {
var hasUseStrict = body[0].directive === "use strict";
if (hasUseStrict) {
// we'll insert it earlier so get rid of it from body
body.shift();
}
printIidToLoc(node);
var iid1 = getIid();
printIidToLoc(node);
var l = labelCounter++;
var ret = replaceInStatement(
"function n() { jalangiLabel" + l + ": while(true) { try {" + RP + "1} catch(" + JALANGI_VAR +
"function n() { " + (hasUseStrict ? "'use strict';\n" : "") + "jalangiLabel" + l + ": while(true) { try {" + RP + "1} catch(" + JALANGI_VAR +
"e) { //console.log(" + JALANGI_VAR + "e); console.log(" +
JALANGI_VAR + "e.stack);\n " + logUncaughtExceptionFunName + "(" + RP + "2," + JALANGI_VAR +
"e); } finally { if (" + logFunctionReturnFunName + "(" +
Expand All @@ -999,12 +1020,12 @@ if (typeof J$ === 'undefined') {
if (!isScript) {
if (!Config.INSTR_TRY_CATCH_ARGUMENTS || Config.INSTR_TRY_CATCH_ARGUMENTS(node)) {
if (!Config.INSTR_INIT || Config.INSTR_INIT(node)) {
ident = createIdentifierAst("arguments");
ret = ret.concat(createCallInitAsStatement(node,
createLiteralAst("arguments"),
ident,
true,
ident, false, true));
// ident = createIdentifierAst("arguments");
// ret = ret.concat(createCallInitAsStatement(node,
// createLiteralAst("arguments"),
// ident,
// true,
// ident, false, true));
}
}
}
Expand Down Expand Up @@ -1073,6 +1094,9 @@ if (typeof J$ === 'undefined') {
} else {
body = [];
}
if (node.body && node.body.body && node.body.body.length > 0 && node.body.body[0].directive === 'use strict') {
body = [node.body.body[0]].concat(body);
}
body = body.concat(syncDefuns(node, scope, false)).concat(ast);
return body;
}
Expand Down Expand Up @@ -1460,6 +1484,9 @@ if (typeof J$ === 'undefined') {
return ret1;
},
"FunctionExpression": function (node, context) {
if (node.id === null && !(context === astUtil.CONTEXT.GETTER || context === astUtil.CONTEXT.SETTER)) {
node.id = createIdentifierAst("__jalangi__anon__" + memIid);
}
node.body.body = instrumentFunctionEntryExit(node, node.body.body);
var ret1;
if (context === astUtil.CONTEXT.GETTER || context === astUtil.CONTEXT.SETTER) {
Expand Down Expand Up @@ -1861,6 +1888,7 @@ if (typeof J$ === 'undefined') {
// console.time("parse")
// var newAst = esprima.parse(code, {loc:true, range:true});
var newAst = acorn.parse(code, {locations: true, ecmaVersion: 6 });
var hasUseStrict = newAst.type === 'Program' && newAst.body && newAst.body.length > 0 && newAst.body[0].directive === 'use strict';
// console.timeEnd("parse")
// StatCollector.suspendTimer("parse");
// StatCollector.resumeTimer("transform");
Expand All @@ -1873,7 +1901,7 @@ if (typeof J$ === 'undefined') {
// console.timeEnd("transform")
// StatCollector.suspendTimer("transform");
// console.log(JSON.stringify(newAst,null," "));
return newAst;
return { ast: newAst, hasUseStrict: hasUseStrict};
}

// if this string is discovered inside code passed to instrumentCode(),
Expand Down Expand Up @@ -1932,16 +1960,19 @@ if (typeof J$ === 'undefined') {
}
}

var hasUseStrict = false;
if (!skip && typeof code === 'string' && code.indexOf(noInstr) < 0) {
try {
code = removeShebang(code);
iidSourceInfo = {};
var newAst;
var transformStringResult;
if (Config.ENABLE_SAMPLING) {
newAst = transformString(code, [visitorCloneBodyPre, visitorRRPost, visitorOps, visitorMergeBodyPre], [undefined, visitorRRPre, undefined, undefined]);
transformStringResult = transformString(code, [visitorCloneBodyPre, visitorRRPost, visitorOps, visitorMergeBodyPre], [undefined, visitorRRPre, undefined, undefined]);
} else {
newAst = transformString(code, [visitorRRPost, visitorOps], [visitorRRPre, undefined]);
transformStringResult = transformString(code, [visitorRRPost, visitorOps], [visitorRRPre, undefined]);
}
var newAst = transformStringResult.ast;
hasUseStrict = transformStringResult.hasUseStrict;
// post-process AST to hoist function declarations (required for Firefox)
var hoistedFcts = [];
newAst = hoistFunctionDeclaration(newAst, hoistedFcts);
Expand Down Expand Up @@ -1977,6 +2008,10 @@ if (typeof J$ === 'undefined') {
instCode = JALANGI_VAR + ".iids = " + JSON.stringify(tmp) + ";\n" + code;
}

if (hasUseStrict) {
instCode = "'use strict';\n" + instCode;
}

if (isEval && sandbox.analysis && sandbox.analysis.instrumentCode) {
aret = sandbox.analysis.instrumentCode(thisIid, instCode, newAst, options.isDirect);
if (aret) {
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/strict-mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

function hi() {
'use strict';
return "hi";
}

console.log(hi());
1 change: 1 addition & 0 deletions tests/unit/unitTests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ prototype_property
scope_rr
shadow-arguments-real
str_proto
strict-mode
switch
switch2
string
Expand Down