709 lines
22 KiB
JavaScript
709 lines
22 KiB
JavaScript
|
//All kinds of conversion passes over the source code
|
||
|
var jsp = require("acorn");
|
||
|
var walk = require("acorn/util/walk.js");
|
||
|
var rnonIdentMember = /[.\-_$a-zA-Z0-9]/g;
|
||
|
var global = new Function("return this")();
|
||
|
|
||
|
function equals( a, b ) {
|
||
|
if( a.type === b.type ) {
|
||
|
if( a.type === "MemberExpression" ) {
|
||
|
return equals( a.object, b.object ) &&
|
||
|
equals( a.property, b.property );
|
||
|
}
|
||
|
else if( a.type === "Identifier" ) {
|
||
|
return a.name === b.name;
|
||
|
}
|
||
|
else if( a.type === "ThisExpression" ) {
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
console.log("equals", a, b);
|
||
|
unhandled();
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function getReceiver( expr ) {
|
||
|
if( expr.type === "MemberExpression" ) {
|
||
|
return expr.object;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
function nodeToString( expr ) {
|
||
|
if( expr == null || typeof expr !== "object" ) {
|
||
|
if( expr === void 0 ) {
|
||
|
return "void 0";
|
||
|
}
|
||
|
else if( typeof expr === "string" ) {
|
||
|
return '"' + safeToEmbedString(expr) + '"';
|
||
|
}
|
||
|
return ("" + expr);
|
||
|
}
|
||
|
if( expr.type === "Identifier" ) {
|
||
|
return expr.name;
|
||
|
}
|
||
|
else if( expr.type === "MemberExpression" ) {
|
||
|
if( expr.computed )
|
||
|
return nodeToString( expr.object ) + "[" + nodeToString( expr.property ) + "]";
|
||
|
else
|
||
|
return nodeToString( expr.object ) + "." + nodeToString( expr.property );
|
||
|
}
|
||
|
else if( expr.type === "UnaryExpression" ) {
|
||
|
if( expr.operator === "~" ||
|
||
|
expr.operator === "-" ||
|
||
|
expr.operator === "+" ||
|
||
|
expr.operator === "-" ) {
|
||
|
return expr.operator + nodeToString( expr.argument );
|
||
|
}
|
||
|
return "(" + expr.operator + " " + nodeToString( expr.argument ) + ")";
|
||
|
}
|
||
|
else if( expr.type === "Literal" ) {
|
||
|
return expr.raw;
|
||
|
}
|
||
|
else if( expr.type === "BinaryExpression" || expr.type === "LogicalExpression" ) {
|
||
|
return "("+nodeToString(expr.left) + " " +
|
||
|
expr.operator + " " +
|
||
|
nodeToString(expr.right) + ")";
|
||
|
}
|
||
|
else if( expr.type === "ThisExpression" ) {
|
||
|
return "this";
|
||
|
}
|
||
|
else if( expr.type === "ObjectExpression") {
|
||
|
var props = expr.properties;
|
||
|
var ret = [];
|
||
|
for( var i = 0, len = props.length; i < len; ++i ) {
|
||
|
var prop = props[i];
|
||
|
ret.push( nodeToString(prop.key) + ": " + nodeToString(prop.value));
|
||
|
}
|
||
|
return "({"+ret.join(",\n")+"})";
|
||
|
}
|
||
|
else if( expr.type === "NewExpression" ) {
|
||
|
return "new " + nodeToString(expr.callee) + "(" + nodeToString(expr.arguments) +")";
|
||
|
}
|
||
|
//assuming it is arguments
|
||
|
else if( Array.isArray( expr ) ) {
|
||
|
var tmp = [];
|
||
|
for( var i = 0, len = expr.length; i < len; ++i ) {
|
||
|
tmp.push( nodeToString(expr[i]) );
|
||
|
}
|
||
|
return tmp.join(", ");
|
||
|
}
|
||
|
else if( expr.type === "FunctionExpression" ) {
|
||
|
var params = [];
|
||
|
for( var i = 0, len = expr.params.length; i < len; ++i ) {
|
||
|
params.push( nodeToString(expr.params[i]) );
|
||
|
}
|
||
|
}
|
||
|
else if( expr.type === "BlockStatement" ) {
|
||
|
var tmp = [];
|
||
|
for( var i = 0, len = expr.body.length; i < len; ++i ) {
|
||
|
tmp.push( nodeToString(expr.body[i]) );
|
||
|
}
|
||
|
return tmp.join(";\n");
|
||
|
}
|
||
|
else if( expr.type === "CallExpression" ) {
|
||
|
var args = [];
|
||
|
for( var i = 0, len = expr.arguments.length; i < len; ++i ) {
|
||
|
args.push( nodeToString(expr.arguments[i]) );
|
||
|
}
|
||
|
return nodeToString( expr.callee ) + "("+args.join(",")+")";
|
||
|
}
|
||
|
else {
|
||
|
console.log( "nodeToString", expr );
|
||
|
unhandled()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function DynamicCall( receiver, fnDereference, arg, start, end ) {
|
||
|
this.receiver = receiver;
|
||
|
this.fnDereference = fnDereference;
|
||
|
this.arg = arg;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
}
|
||
|
|
||
|
DynamicCall.prototype.toString = function() {
|
||
|
return nodeToString(this.fnDereference) + ".call(" +
|
||
|
nodeToString(this.receiver) + ", " +
|
||
|
nodeToString(this.arg) +
|
||
|
")";
|
||
|
};
|
||
|
|
||
|
function DirectCall( receiver, fnName, arg, start, end ) {
|
||
|
this.receiver = receiver;
|
||
|
this.fnName = fnName;
|
||
|
this.arg = arg;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
}
|
||
|
DirectCall.prototype.toString = function() {
|
||
|
return nodeToString(this.receiver) + "." + nodeToString(this.fnName) +
|
||
|
"(" + nodeToString(this.arg) + ")"
|
||
|
};
|
||
|
|
||
|
|
||
|
function ConstantReplacement( value, start, end ) {
|
||
|
this.value = value;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
}
|
||
|
|
||
|
ConstantReplacement.prototype.toString = function() {
|
||
|
return nodeToString(this.value);
|
||
|
};
|
||
|
|
||
|
function Empty(start, end) {
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
}
|
||
|
Empty.prototype.toString = function() {
|
||
|
return "";
|
||
|
};
|
||
|
|
||
|
function Assertion( expr, exprStr, start, end ) {
|
||
|
this.expr = expr;
|
||
|
this.exprStr = exprStr;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
}
|
||
|
Assertion.prototype.toString = function() {
|
||
|
return 'ASSERT('+nodeToString(this.expr)+',\n '+this.exprStr+')';
|
||
|
};
|
||
|
|
||
|
function BitFieldRead(mask, start, end, fieldExpr) {
|
||
|
if (mask === 0) throw new Error("mask cannot be zero");
|
||
|
this.mask = mask;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.fieldExpr = fieldExpr;
|
||
|
}
|
||
|
|
||
|
BitFieldRead.prototype.getShiftCount = function() {
|
||
|
var b = 1;
|
||
|
var shiftCount = 0;
|
||
|
while ((this.mask & b) === 0) {
|
||
|
b <<= 1;
|
||
|
shiftCount++;
|
||
|
}
|
||
|
return shiftCount;
|
||
|
};
|
||
|
|
||
|
BitFieldRead.prototype.toString = function() {
|
||
|
var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField";
|
||
|
var mask = this.mask;
|
||
|
var shiftCount = this.getShiftCount();
|
||
|
return shiftCount === 0
|
||
|
? "(" + fieldExpr + " & " + mask + ")"
|
||
|
: "((" + fieldExpr + " & " + mask + ") >>> " + shiftCount + ")";
|
||
|
};
|
||
|
|
||
|
function BitFieldCheck(value, inverted, start, end, fieldExpr) {
|
||
|
this.value = value;
|
||
|
this.inverted = inverted;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.fieldExpr = fieldExpr;
|
||
|
}
|
||
|
|
||
|
BitFieldCheck.prototype.toString = function() {
|
||
|
var fieldExpr = this.fieldExpr ? nodeToString(this.fieldExpr) : "bitField";
|
||
|
var equality = this.inverted ? "===" : "!==";
|
||
|
return "((" + fieldExpr + " & " + this.value + ") " + equality + " 0)";
|
||
|
};
|
||
|
|
||
|
function InlineSlice(varExpr, collectionExpression, startExpression, endExpression, start, end, isBrowser) {
|
||
|
this.varExpr = varExpr;
|
||
|
this.collectionExpression = collectionExpression;
|
||
|
this.startExpression = startExpression;
|
||
|
this.endExpression = endExpression;
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.isBrowser = isBrowser;
|
||
|
}
|
||
|
|
||
|
InlineSlice.prototype.hasSimpleStartExpression =
|
||
|
function InlineSlice$hasSimpleStartExpression() {
|
||
|
return this.startExpression.type === "Identifier" ||
|
||
|
this.startExpression.type === "Literal";
|
||
|
};
|
||
|
|
||
|
InlineSlice.prototype.hasSimpleEndExpression =
|
||
|
function InlineSlice$hasSimpleEndExpression() {
|
||
|
return this.endExpression.type === "Identifier" ||
|
||
|
this.endExpression.type === "Literal";
|
||
|
};
|
||
|
|
||
|
InlineSlice.prototype.hasSimpleCollection = function InlineSlice$hasSimpleCollection() {
|
||
|
return this.collectionExpression.type === "Identifier";
|
||
|
};
|
||
|
|
||
|
InlineSlice.prototype.toString = function InlineSlice$toString() {
|
||
|
var init = this.hasSimpleCollection()
|
||
|
? ""
|
||
|
: "var $_collection = " + nodeToString(this.collectionExpression) + ";";
|
||
|
|
||
|
var collectionExpression = this.hasSimpleCollection()
|
||
|
? nodeToString(this.collectionExpression)
|
||
|
: "$_collection";
|
||
|
|
||
|
|
||
|
init += "var $_len = " + collectionExpression + ".length;";
|
||
|
|
||
|
var varExpr = nodeToString(this.varExpr);
|
||
|
|
||
|
//No offset arguments at all
|
||
|
if( this.startExpression === firstElement ) {
|
||
|
if (this.isBrowser) {
|
||
|
return "var " + varExpr + " = [].slice.call("+collectionExpression+");";
|
||
|
} else {
|
||
|
return init + "var " + varExpr + " = new Array($_len); " +
|
||
|
"for(var $_i = 0; $_i < $_len; ++$_i) {" +
|
||
|
varExpr + "[$_i] = " + collectionExpression + "[$_i];" +
|
||
|
"}";
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
if( !this.hasSimpleStartExpression() ) {
|
||
|
init += "var $_start = " + nodeToString(this.startExpression) + ";";
|
||
|
}
|
||
|
var startExpression = this.hasSimpleStartExpression()
|
||
|
? nodeToString(this.startExpression)
|
||
|
: "$_start";
|
||
|
|
||
|
//Start offset argument given
|
||
|
if( this.endExpression === lastElement ) {
|
||
|
if (this.isBrowser) {
|
||
|
return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+");";
|
||
|
} else {
|
||
|
return init + "var " + varExpr + " = new Array(Math.max($_len - " +
|
||
|
startExpression + ", 0)); " +
|
||
|
"for(var $_i = " + startExpression + "; $_i < $_len; ++$_i) {" +
|
||
|
varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" +
|
||
|
"}";
|
||
|
}
|
||
|
}
|
||
|
//Start and end offset argument given
|
||
|
else {
|
||
|
|
||
|
if( !this.hasSimpleEndExpression() ) {
|
||
|
init += "var $_end = " + nodeToString(this.endExpression) + ";";
|
||
|
}
|
||
|
var endExpression = this.hasSimpleEndExpression()
|
||
|
? nodeToString(this.endExpression)
|
||
|
: "$_end";
|
||
|
|
||
|
if (this.isBrowser) {
|
||
|
return "var " + varExpr + " = [].slice.call("+collectionExpression+", "+startExpression+", "+endExpression+");";
|
||
|
} else {
|
||
|
return init + "var " + varExpr + " = new Array(Math.max(" + endExpression + " - " +
|
||
|
startExpression + ", 0)); " +
|
||
|
"for(var $_i = " + startExpression + "; $_i < " + endExpression + "; ++$_i) {" +
|
||
|
varExpr + "[$_i - "+startExpression+"] = " + collectionExpression + "[$_i];" +
|
||
|
"}";
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var opts = {
|
||
|
ecmaVersion: 5,
|
||
|
strictSemicolons: false,
|
||
|
allowTrailingCommas: true,
|
||
|
forbidReserved: false,
|
||
|
locations: false,
|
||
|
onComment: null,
|
||
|
ranges: false,
|
||
|
program: null,
|
||
|
sourceFile: null
|
||
|
};
|
||
|
|
||
|
var rlineterm = /[\r\n\u2028\u2029]/;
|
||
|
var rhorizontalws = /[ \t]/;
|
||
|
|
||
|
var convertSrc = function( src, results ) {
|
||
|
if( results.length ) {
|
||
|
results.sort(function(a, b){
|
||
|
var ret = a.start - b.start;
|
||
|
if( ret === 0 ) {
|
||
|
ret = a.end - b.end;
|
||
|
}
|
||
|
return ret;
|
||
|
});
|
||
|
for( var i = 1; i < results.length; ++i ) {
|
||
|
var item = results[i];
|
||
|
if( item.start === results[i-1].start &&
|
||
|
item.end === results[i-1].end ) {
|
||
|
results.splice(i++, 1);
|
||
|
}
|
||
|
}
|
||
|
var ret = "";
|
||
|
var start = 0;
|
||
|
for( var i = 0, len = results.length; i < len; ++i ) {
|
||
|
var item = results[i];
|
||
|
ret += src.substring( start, item.start );
|
||
|
ret += item.toString();
|
||
|
start = item.end;
|
||
|
}
|
||
|
ret += src.substring( start );
|
||
|
return ret;
|
||
|
}
|
||
|
return src;
|
||
|
};
|
||
|
|
||
|
var rescape = /[\r\n\u2028\u2029"]/g;
|
||
|
|
||
|
var replacer = function( ch ) {
|
||
|
return "\\u" + (("0000") +
|
||
|
(ch.charCodeAt(0).toString(16))).slice(-4);
|
||
|
};
|
||
|
|
||
|
function safeToEmbedString( str ) {
|
||
|
return str.replace( rescape, replacer );
|
||
|
}
|
||
|
|
||
|
function parse( src, opts, fileName) {
|
||
|
if( !fileName ) {
|
||
|
fileName = opts;
|
||
|
opts = void 0;
|
||
|
}
|
||
|
try {
|
||
|
return jsp.parse(src, opts);
|
||
|
}
|
||
|
catch(e) {
|
||
|
e.message = e.message + " " + fileName;
|
||
|
e.scriptSrc = src;
|
||
|
throw e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var inlinedFunctions = Object.create(null);
|
||
|
|
||
|
var lastElement = jsp.parse("___input.length").body[0].expression;
|
||
|
var firstElement = jsp.parse("0").body[0].expression;
|
||
|
inlinedFunctions.INLINE_SLICE = function( node, isBrowser ) {
|
||
|
var statement = node;
|
||
|
node = node.expression;
|
||
|
var args = node.arguments;
|
||
|
|
||
|
if( !(2 <= args.length && args.length <= 4 ) ) {
|
||
|
throw new Error("INLINE_SLICE must have exactly 2, 3 or 4 arguments");
|
||
|
}
|
||
|
|
||
|
var varExpression = args[0];
|
||
|
var collectionExpression = args[1];
|
||
|
var startExpression = args.length < 3
|
||
|
? firstElement
|
||
|
: args[2];
|
||
|
var endExpression = args.length < 4
|
||
|
? lastElement
|
||
|
: args[3];
|
||
|
return new InlineSlice(varExpression, collectionExpression,
|
||
|
startExpression, endExpression, statement.start, statement.end, isBrowser);
|
||
|
};
|
||
|
inlinedFunctions.BIT_FIELD_READ = function(node) {
|
||
|
var statement = node;
|
||
|
var args = node.expression.arguments;
|
||
|
if (args.length !== 1 && args.length !== 2) {
|
||
|
throw new Error("BIT_FIELD must have 1 or 2 arguments");
|
||
|
}
|
||
|
var arg = args[0];
|
||
|
if (arg.type !== "Identifier") {
|
||
|
throw new Error("BIT_FIELD argument must be an identifier");
|
||
|
}
|
||
|
var name = arg.name;
|
||
|
var constant = constants[name];
|
||
|
if (constant === undefined) {
|
||
|
throw new Error(name + " is not a constant");
|
||
|
}
|
||
|
var value = constant.value;
|
||
|
return new BitFieldRead(value, statement.start, statement.end, args[1]);
|
||
|
};
|
||
|
inlinedFunctions.BIT_FIELD_CHECK = function(node) {
|
||
|
var statement = node;
|
||
|
var args = node.expression.arguments;
|
||
|
if (args.length !== 1 && args.length !== 2) {
|
||
|
throw new Error("BIT_FIELD must have 1 or 2 arguments");
|
||
|
}
|
||
|
var arg = args[0];
|
||
|
if (arg.type !== "Identifier") {
|
||
|
throw new Error("BIT_FIELD argument must be an identifier");
|
||
|
}
|
||
|
var name = arg.name;
|
||
|
var constant = constants[name];
|
||
|
if (constant === undefined) {
|
||
|
throw new Error(name + " is not a constant");
|
||
|
}
|
||
|
var value = constant.value;
|
||
|
var inverted = false;
|
||
|
if (name.slice(-4) === "_NEG") {
|
||
|
inverted = true;
|
||
|
}
|
||
|
return new BitFieldCheck(value, inverted, statement.start, statement.end, args[1]);
|
||
|
};
|
||
|
inlinedFunctions.USE = function(node) {
|
||
|
return new Empty(node.start, node.end);
|
||
|
};
|
||
|
|
||
|
var constants = {};
|
||
|
var ignore = [];
|
||
|
Error.stackTraceLimit = 10000;
|
||
|
var astPasses = module.exports = {
|
||
|
|
||
|
inlineExpansion: function( src, fileName, isBrowser ) {
|
||
|
var ast = parse(src, fileName);
|
||
|
var results = [];
|
||
|
var expr = [];
|
||
|
function doInline(node) {
|
||
|
if( node.expression.type !== 'CallExpression' ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var name = node.expression.callee.name;
|
||
|
|
||
|
if(typeof inlinedFunctions[ name ] === "function" &&
|
||
|
expr.indexOf(node.expression) === -1) {
|
||
|
expr.push(node.expression);
|
||
|
try {
|
||
|
results.push( inlinedFunctions[ name ]( node, isBrowser ) );
|
||
|
}
|
||
|
catch(e) {
|
||
|
e.fileName = fileName;
|
||
|
throw e;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
walk.simple(ast, {
|
||
|
ExpressionStatement: doInline,
|
||
|
CallExpression: function(node) {
|
||
|
node.expression = node;
|
||
|
doInline(node);
|
||
|
}
|
||
|
});
|
||
|
var ret = convertSrc( src, results );
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
//Parse constants in from constants.js
|
||
|
readConstants: function( src, fileName ) {
|
||
|
var ast = parse(src, fileName);
|
||
|
walk.simple(ast, {
|
||
|
ExpressionStatement: function( node ) {
|
||
|
if( node.expression.type !== 'CallExpression' ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var start = node.start;
|
||
|
var end = node.end;
|
||
|
node = node.expression;
|
||
|
var callee = node.callee;
|
||
|
if( callee.name === "CONSTANT" &&
|
||
|
callee.type === "Identifier" ) {
|
||
|
|
||
|
if( node.arguments.length !== 2 ) {
|
||
|
throw new Error( "Exactly 2 arguments must be passed to CONSTANT\n" +
|
||
|
src.substring(start, end)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if( node.arguments[0].type !== "Identifier" ) {
|
||
|
throw new Error( "Can only define identifier as a constant\n" +
|
||
|
src.substring(start, end)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
var args = node.arguments;
|
||
|
|
||
|
var name = args[0];
|
||
|
var nameStr = name.name;
|
||
|
var expr = args[1];
|
||
|
|
||
|
var e = eval;
|
||
|
constants[nameStr] = {
|
||
|
identifier: name,
|
||
|
value: e(nodeToString(expr))
|
||
|
};
|
||
|
walk.simple( expr, {
|
||
|
Identifier: function( node ) {
|
||
|
ignore.push(node);
|
||
|
}
|
||
|
});
|
||
|
global[nameStr] = constants[nameStr].value;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
//Expand constants in normal source files
|
||
|
expandConstants: function( src, fileName ) {
|
||
|
var results = [];
|
||
|
var identifiers = [];
|
||
|
var ast = parse(src, fileName);
|
||
|
walk.simple(ast, {
|
||
|
Identifier: function( node ) {
|
||
|
identifiers.push( node );
|
||
|
}
|
||
|
});
|
||
|
|
||
|
for( var i = 0, len = identifiers.length; i < len; ++i ) {
|
||
|
var id = identifiers[i];
|
||
|
if( ignore.indexOf(id) > -1 ) {
|
||
|
continue;
|
||
|
}
|
||
|
var constant = constants[id.name];
|
||
|
if( constant === void 0 ) {
|
||
|
continue;
|
||
|
}
|
||
|
if( constant.identifier === id ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
results.push( new ConstantReplacement( constant.value, id.start, id.end ) );
|
||
|
|
||
|
}
|
||
|
return convertSrc( src, results );
|
||
|
},
|
||
|
|
||
|
removeComments: function( src, fileName ) {
|
||
|
var results = [];
|
||
|
var rnoremove = /^[*\s\/]*(?:@preserve|jshint|global)/;
|
||
|
opts.onComment = function( block, text, start, end ) {
|
||
|
if( rnoremove.test(text) ) {
|
||
|
return;
|
||
|
}
|
||
|
var e = end + 1;
|
||
|
var s = start - 1;
|
||
|
while(rhorizontalws.test(src.charAt(s--)));
|
||
|
while(rlineterm.test(src.charAt(e++)));
|
||
|
results.push( new Empty( s + 2, e - 1 ) );
|
||
|
};
|
||
|
var ast = parse(src, opts, fileName);
|
||
|
return convertSrc( src, results );
|
||
|
},
|
||
|
|
||
|
expandAsserts: function( src, fileName ) {
|
||
|
var ast = parse( src, fileName );
|
||
|
var results = [];
|
||
|
walk.simple(ast, {
|
||
|
CallExpression: function( node ) {
|
||
|
|
||
|
var start = node.start;
|
||
|
var end = node.end;
|
||
|
var callee = node.callee;
|
||
|
|
||
|
if( callee.type === "Identifier" &&
|
||
|
callee.name === "ASSERT" ) {
|
||
|
if( node.arguments.length !== 1 ) {
|
||
|
results.push({
|
||
|
start: start,
|
||
|
end: end,
|
||
|
toString: function() {
|
||
|
return src.substring(start, end);
|
||
|
}
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var expr = node.arguments[0];
|
||
|
var str = src.substring(expr.start, expr.end);
|
||
|
str = '"' + safeToEmbedString(str) + '"'
|
||
|
var assertion = new Assertion( expr, str, start, end );
|
||
|
|
||
|
results.push( assertion );
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return convertSrc( src, results );
|
||
|
},
|
||
|
|
||
|
removeAsserts: function( src, fileName ) {
|
||
|
var ast = parse( src, fileName );
|
||
|
var results = [];
|
||
|
walk.simple(ast, {
|
||
|
ExpressionStatement: function( node ) {
|
||
|
if( node.expression.type !== 'CallExpression' ) {
|
||
|
return;
|
||
|
}
|
||
|
var start = node.start;
|
||
|
var end = node.end;
|
||
|
node = node.expression;
|
||
|
var callee = node.callee;
|
||
|
|
||
|
if( callee.type === "Identifier" &&
|
||
|
callee.name === "ASSERT" ) {
|
||
|
var e = end + 1;
|
||
|
var s = start - 1;
|
||
|
|
||
|
while(rhorizontalws.test(src.charAt(s--)));
|
||
|
while(rlineterm.test(src.charAt(e++)));
|
||
|
results.push( new Empty( s + 2, e - 1) );
|
||
|
}
|
||
|
},
|
||
|
VariableDeclaration: function(node) {
|
||
|
var start = node.start;
|
||
|
var end = node.end;
|
||
|
if (node.kind === 'var' && node.declarations.length === 1) {
|
||
|
var decl = node.declarations[0];
|
||
|
if (decl.id.type === "Identifier" &&
|
||
|
decl.id.name === "ASSERT") {
|
||
|
var e = end + 1;
|
||
|
var s = start - 1;
|
||
|
while(rhorizontalws.test(src.charAt(s--)));
|
||
|
while(rlineterm.test(src.charAt(e++)));
|
||
|
results.push( new Empty( s + 2, e - 1) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return convertSrc( src, results );
|
||
|
},
|
||
|
|
||
|
asyncConvert: function( src, objName, fnProp, fileName ) {
|
||
|
var ast = parse( src, fileName );
|
||
|
|
||
|
var results = [];
|
||
|
walk.simple(ast, {
|
||
|
CallExpression: function( node ) {
|
||
|
var start = node.start;
|
||
|
var end = node.end;
|
||
|
if( node.callee.type === "MemberExpression" &&
|
||
|
node.callee.object.name === objName &&
|
||
|
node.callee.property.name === fnProp &&
|
||
|
node.arguments.length === 3
|
||
|
) {
|
||
|
|
||
|
var args = node.arguments;
|
||
|
var fnDereference = args[0];
|
||
|
var dynamicReceiver = args[1];
|
||
|
var arg = args[2];
|
||
|
|
||
|
var receiver = getReceiver(fnDereference);
|
||
|
|
||
|
if( receiver == null || !equals(receiver, dynamicReceiver) ) {
|
||
|
//Have to use fnDereference.call(dynamicReceiver, arg);
|
||
|
results.push(
|
||
|
new DynamicCall( dynamicReceiver, fnDereference, arg, start, end )
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
var fnName = fnDereference.property;
|
||
|
results.push(
|
||
|
new DirectCall( receiver, fnName, arg, start, end )
|
||
|
);
|
||
|
//Can use receiver.fnName( arg );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
return convertSrc( src, results );
|
||
|
}
|
||
|
};
|