From 415d161dbeed07717977c2d582d6369b1a0883d7 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 8 Nov 2013 09:00:39 +0100 Subject: [PATCH] evaluate the callee in CallExpression, fixes #117 This allow fancy wrappers around identifiers --- lib/BasicEvaluatedExpression.js | 10 ++++++ lib/Parser.js | 52 +++++++++++++++++++++++-------- lib/dependencies/AMDPlugin.js | 12 +++++++ test/browsertest/lib/index.web.js | 17 ++++++++++ 4 files changed, 78 insertions(+), 13 deletions(-) diff --git a/lib/BasicEvaluatedExpression.js b/lib/BasicEvaluatedExpression.js index bb0548dac..21ee24555 100644 --- a/lib/BasicEvaluatedExpression.js +++ b/lib/BasicEvaluatedExpression.js @@ -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; diff --git a/lib/Parser.js b/lib/Parser.js index 4de3323a6..d5d6a5cc5 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -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; } diff --git a/lib/dependencies/AMDPlugin.js b/lib/dependencies/AMDPlugin.js index 9463d03b6..1b89a7fdf 100644 --- a/lib/dependencies/AMDPlugin.js +++ b/lib/dependencies/AMDPlugin.js @@ -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); }); diff --git a/test/browsertest/lib/index.web.js b/test/browsertest/lib/index.web.js index 2ed7cc3d5..1e6fa9b83 100644 --- a/test/browsertest/lib/index.web.js +++ b/test/browsertest/lib/index.web.js @@ -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() {