evaluate the callee in CallExpression, fixes #117

This allow fancy wrappers around identifiers
This commit is contained in:
Tobias Koppers 2013-11-08 09:00:39 +01:00
parent a6a71dc90c
commit 415d161dbe
4 changed files with 78 additions and 13 deletions

View File

@ -25,6 +25,9 @@ BasicEvaluatedExpression.prototype.isConditional = function() {
BasicEvaluatedExpression.prototype.isArray = function() {
return Object.prototype.hasOwnProperty.call(this, "items");
};
BasicEvaluatedExpression.prototype.isIdentifier = function() {
return Object.prototype.hasOwnProperty.call(this, "identifier");
};
BasicEvaluatedExpression.prototype.isWrapped = function() {
return Object.prototype.hasOwnProperty.call(this, "prefix");
};
@ -75,6 +78,13 @@ BasicEvaluatedExpression.prototype.setRegExp = function(regExp) {
this.regExp = regExp;
return this;
};
BasicEvaluatedExpression.prototype.setIdentifier = function(identifier) {
if(identifier === null)
delete this.identifier;
else
this.identifier = identifier;
return this;
};
BasicEvaluatedExpression.prototype.setWrapped = function(prefix, postfix) {
this.prefix = prefix;
this.postfix = postfix;

View File

@ -36,6 +36,27 @@ Parser.prototype.initializeEvaluating = function() {
if(expr.value instanceof RegExp)
return new BasicEvaluatedExpression().setRegExp(expr.value).setRange(expr.range);
});
this.plugin("evaluate LogicalExpression", function(expr) {
if(expr.operator == "&&") {
var left = this.evaluateExpression(expr.left);
var leftAsBool = left && left.asBool();
if(leftAsBool === false) return new BasicEvaluatedExpression().setBoolean(false).setRange(expr.range);
if(leftAsBool !== true) return;
var right = this.evaluateExpression(expr.right);
var rightAsBool = right && right.asBool();
if(typeof rightAsBool === "boolean")
return new BasicEvaluatedExpression().setBoolean(rightAsBool).setRange(expr.range);
} else if(expr.operator == "||") {
var left = this.evaluateExpression(expr.left);
var leftAsBool = left && left.asBool();
if(leftAsBool === true) return new BasicEvaluatedExpression().setBoolean(true).setRange(expr.range);
if(leftAsBool !== false) return;
var right = this.evaluateExpression(expr.right);
var rightAsBool = right && right.asBool();
if(typeof rightAsBool === "boolean")
return new BasicEvaluatedExpression().setBoolean(rightAsBool).setRange(expr.range);
}
});
this.plugin("evaluate BinaryExpression", function(expr) {
if(expr.operator == "+") {
var left = this.evaluateExpression(expr.left);
@ -179,7 +200,13 @@ Parser.prototype.initializeEvaluating = function() {
}
});
this.plugin("evaluate Identifier", function(expr) {
return this.applyPluginsBailResult("evaluate Identifier " + expr.name, expr);
if(this.scope.definitions.indexOf(expr.name) == -1) {
var result = this.applyPluginsBailResult("evaluate Identifier " + expr.name, expr);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(expr.name).setRange(expr.range);
} else {
return this.applyPluginsBailResult("evaluate defined Identifier " + expr.name, expr);
}
});
this.plugin("evaluate MemberExpression", function(expression) {
var expr = expression;
@ -188,10 +215,16 @@ Parser.prototype.initializeEvaluating = function() {
exprName.unshift(expr.property.name);
expr = expr.object;
}
if(expr.type == "Identifier" && this.scope.definitions.indexOf(expr.name) == -1) {
if(expr.type == "Identifier") {
exprName.unshift(expr.name);
exprName = exprName.join(".");
return this.applyPluginsBailResult("evaluate Identifier " + exprName, expression);
if(this.scope.definitions.indexOf(expr.name) == -1) {
var result = this.applyPluginsBailResult("evaluate Identifier " + exprName, expression);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(exprName).setRange(expression.range);
} else {
return this.applyPluginsBailResult("evaluate defined Identifier " + exprName, expression);
}
}
});
this.plugin("evaluate CallExpression", function(expr) {
@ -491,16 +524,9 @@ Parser.prototype.walkExpression = function walkExpression(expression) {
this.walkExpressions(expression.arguments);
break;
case "CallExpression":
var callee = expression.callee;
var calleeName = [];
while(callee.type == "MemberExpression" && callee.property.type == "Identifier") {
calleeName.unshift(callee.property.name);
callee = callee.object;
}
if(callee.type == "Identifier" && this.scope.definitions.indexOf(callee.name) == -1) {
calleeName.unshift(callee.name);
calleeName = calleeName.join(".");
var result = this.applyPluginsBailResult("call " + calleeName, expression);
var callee = this.evaluateExpression(expression.callee);
if(callee.isIdentifier()) {
var result = this.applyPluginsBailResult("call " + callee.identifier, expression);
if(result === true)
break;
}

View File

@ -64,6 +64,18 @@ AMDPlugin.prototype.apply = function(compiler) {
compiler.parser.plugin("expression __webpack_amd_options__", function(expr) {
return this.state.current.addVariable("__webpack_amd_options__", JSON.stringify(options));
});
compiler.parser.plugin("evaluate typeof define.amd", function(expr) {
return new BasicEvaluatedExpression().setString(typeof options).setRange(expr.range);
});
compiler.parser.plugin("evaluate typeof require.amd", function(expr) {
return new BasicEvaluatedExpression().setString(typeof options).setRange(expr.range);
});
compiler.parser.plugin("evaluate Identifier define.amd", function(expr) {
return new BasicEvaluatedExpression().setBoolean(true).setRange(expr.range);
});
compiler.parser.plugin("evaluate Identifier require.amd", function(expr) {
return new BasicEvaluatedExpression().setBoolean(true).setRange(expr.range);
});
compiler.parser.plugin("evaluate typeof define", function(expr) {
return new BasicEvaluatedExpression().setString("function").setRange(expr.range);
});

View File

@ -206,6 +206,23 @@ describe("main", function() {
var x = require(DEBUG ? "fail" : "./a");
var y = DEBUG ? require("fail") : require("./a");
});
it("should parse fancy function calls", function() {
("function"==typeof define && define.amd ?
define :
function(e,t){return t()}
)(["./constructor"], function(c) {
return new c(1324);
});
module.exports.should.have.property("value").be.eql(1324);
(("function"==typeof define && define.amd ?
define :
function(e,t){return t()}
)(["./constructor"], function(c) {
return new c(4231);
}));
module.exports.should.have.property("value").be.eql(4231);
});
});
describe("polyfilling", function() {