From 2a932597e4d9f1adc8b8788f2ea2885728c78db1 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 15 Jul 2010 09:08:51 -0400 Subject: [PATCH] fixing existential chains directly against a 'new Func()' call. Issue #503 --- lib/nodes.js | 49 ++++++++++++++++++++++---------------- src/nodes.coffee | 6 ++--- test/test_existence.coffee | 10 ++++++++ 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index 2ef08272..e0799dbf 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -48,7 +48,9 @@ BaseNode.prototype.compileReference = function(o, options) { var compiled, pair, reference; pair = (function() { - if (!(this instanceof CallNode || this instanceof ValueNode && (!(this.base instanceof LiteralNode) || this.hasProperties()))) { + if (!((this instanceof CallNode || this.contains(function(n) { + return n instanceof CallNode; + })) || (this instanceof ValueNode && (!(this.base instanceof LiteralNode) || this.hasProperties())))) { return [this, this]; } else { reference = literal(o.scope.freeVariable()); @@ -359,7 +361,7 @@ return !o.top || this.properties.length ? ValueNode.__superClass__.compile.call(this, o) : this.base.compile(o); }; ValueNode.prototype.compileNode = function(o) { - var _b, _c, baseline, complete, i, only, op, part, prop, props, temp; + var _b, _c, _d, baseline, complete, i, only, op, props; only = del(o, 'onlyFirst'); op = del(o, 'operation'); props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties; @@ -369,25 +371,32 @@ baseline = ("(" + baseline + ")"); } complete = (this.last = baseline); - _b = props; - for (i = 0, _c = _b.length; i < _c; i++) { - prop = _b[i]; - this.source = baseline; - if (prop.soakNode) { - if (this.base instanceof CallNode && i === 0) { - temp = o.scope.freeVariable(); - complete = ("(" + (baseline = temp) + " = (" + complete + "))"); + _c = props; + for (_b = 0, _d = _c.length; _b < _d; _b++) { + (function() { + var part, temp; + var i = _b; + var prop = _c[_b]; + this.source = baseline; + if (prop.soakNode) { + if (this.base instanceof CallNode || this.base.contains(function(n) { + return n instanceof CallNode; + }) && i === 0) { + temp = o.scope.freeVariable(); + complete = ("(" + (baseline = temp) + " = (" + complete + "))"); + } + if (i === 0 && this.isStart(o)) { + complete = ("typeof " + complete + " === \"undefined\" || " + baseline); + } + return complete += this.SOAK + (baseline += prop.compile(o)); + } else { + part = prop.compile(o); + baseline += part; + complete += part; + this.last = part; + return this.last; } - if (i === 0 && this.isStart(o)) { - complete = ("typeof " + complete + " === \"undefined\" || " + baseline); - } - complete += this.SOAK + (baseline += prop.compile(o)); - } else { - part = prop.compile(o); - baseline += part; - complete += part; - this.last = part; - } + }).call(this); } return op && this.wrapped ? ("(" + complete + ")") : complete; }; diff --git a/src/nodes.coffee b/src/nodes.coffee index b9c0cc01..56dfbfeb 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -62,8 +62,8 @@ exports.BaseNode: class BaseNode # in multiple places, ensure that the expression is only ever evaluated once, # by assigning it to a temporary variable. compileReference: (o, options) -> - pair: if not (this instanceof CallNode or this instanceof ValueNode and - (not (@base instanceof LiteralNode) or @hasProperties())) + pair: if not ((this instanceof CallNode or @contains((n) -> n instanceof CallNode)) or + (this instanceof ValueNode and (not (@base instanceof LiteralNode) or @hasProperties()))) [this, this] else reference: literal o.scope.freeVariable() @@ -356,7 +356,7 @@ exports.ValueNode: class ValueNode extends BaseNode for prop, i in props @source: baseline if prop.soakNode - if @base instanceof CallNode and i is 0 + if @base instanceof CallNode or @base.contains((n) -> n instanceof CallNode) and i is 0 temp: o.scope.freeVariable() complete: "(${ baseline: temp } = ($complete))" complete: "typeof $complete === \"undefined\" || $baseline" if i is 0 and @isStart(o) diff --git a/test/test_existence.coffee b/test/test_existence.coffee index 250e330e..af9013ea 100644 --- a/test/test_existence.coffee +++ b/test/test_existence.coffee @@ -70,6 +70,16 @@ result: value?.toString().toLowerCase() ok result is '10' +# Soaks constructor invocations. +a: 0 +class Foo + constructor: -> a += 1 + bar: "bat" + +ok (new Foo())?.bar is 'bat' +ok a is 1 + + # Safely existence test on soaks. result: not value?.property? ok result