diff --git a/lib/nodes.js b/lib/nodes.js index 2e95793d..d9dedf01 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1305,17 +1305,16 @@ o.scope.find(first); } if (this.operator === '?=') { - return ("" + (first) + " = " + (ExistenceNode.compileTest(o, literal(firstVar))) + " ? " + (firstVar) + " : " + (second)); + return ("" + (first) + " = " + (ExistenceNode.compileTest(o, literal(firstVar))[0]) + " ? " + (firstVar) + " : " + (second)); } return "" + (first) + " = " + (firstVar) + " " + (this.operator.substr(0, 2)) + " " + (second); }; OpNode.prototype.compileExistence = function(o) { - var _b, first, second, test; - _b = [this.first.compile(o), this.second.compile(o)]; - first = _b[0]; - second = _b[1]; - test = ExistenceNode.compileTest(o, this.first); - return "" + (test) + " ? " + (first) + " : " + (second); + var _b, ref, test; + _b = ExistenceNode.compileTest(o, this.first); + test = _b[0]; + ref = _b[1]; + return "" + (test) + " ? " + (ref) + " : " + (this.second.compile(o)); }; OpNode.prototype.compileUnary = function(o) { var parts, space; @@ -1439,14 +1438,16 @@ ExistenceNode.prototype["class"] = 'ExistenceNode'; ExistenceNode.prototype.children = ['expression']; ExistenceNode.prototype.compileNode = function(o) { - return ExistenceNode.compileTest(o, this.expression); + return ExistenceNode.compileTest(o, this.expression)[0]; }; ExistenceNode.compileTest = function(o, variable) { var _b, first, second; - _b = variable.compileReference(o); + _b = variable.compileReference(o, { + precompile: true + }); first = _b[0]; second = _b[1]; - return "(typeof " + (first.compile(o)) + " !== \"undefined\" && " + (second.compile(o)) + " !== null)"; + return [("(typeof " + (first) + " !== \"undefined\" && " + (second) + " !== null)"), second]; }; return ExistenceNode; }).call(this); diff --git a/src/nodes.coffee b/src/nodes.coffee index 5e78d739..c252ce91 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1114,15 +1114,14 @@ exports.OpNode = class OpNode extends BaseNode second = @second.compile o second = "(#{second})" if @second instanceof OpNode o.scope.find(first) if first.match(IDENTIFIER) - return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar)) } ? #{firstVar} : #{second}" if @operator is '?=' + return "#{first} = #{ ExistenceNode.compileTest(o, literal(firstVar))[0] } ? #{firstVar} : #{second}" if @operator is '?=' "#{first} = #{firstVar} #{ @operator.substr(0, 2) } #{second}" # If this is an existence operator, we delegate to `ExistenceNode.compileTest` # to give us the safe references for the variables. compileExistence: (o) -> - [first, second] = [@first.compile(o), @second.compile(o)] - test = ExistenceNode.compileTest(o, @first) - "#{test} ? #{first} : #{second}" + [test, ref] = ExistenceNode.compileTest(o, @first) + "#{test} ? #{ref} : #{ @second.compile(o) }" # Compile a unary **OpNode**. compileUnary: (o) -> @@ -1215,14 +1214,14 @@ exports.ExistenceNode = class ExistenceNode extends BaseNode constructor: (@expression) -> compileNode: (o) -> - ExistenceNode.compileTest(o, @expression) + ExistenceNode.compileTest(o, @expression)[0] # The meat of the **ExistenceNode** is in this static `compileTest` method # because other nodes like to check the existence of their variables as well. # Be careful not to double-evaluate anything. @compileTest: (o, variable) -> - [first, second] = variable.compileReference o - "(typeof #{first.compile(o)} !== \"undefined\" && #{second.compile(o)} !== null)" + [first, second] = variable.compileReference o, precompile: yes + ["(typeof #{first} !== \"undefined\" && #{second} !== null)", second] #### ParentheticalNode diff --git a/test/test_existence.coffee b/test/test_existence.coffee index c54d7f5e..59df5259 100644 --- a/test/test_existence.coffee +++ b/test/test_existence.coffee @@ -17,9 +17,13 @@ ok a is 10 and b is 10 # The existential operator. z = null x = z ? "EX" - ok z is null and x is "EX" +i = 9 +func = -> i += 1 +result = func() ? 101 +ok result is 10 + # Only evaluate once. counter = 0