mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Added safe soaking on non-existent variables.
This commit is contained in:
parent
d0d0fa4d10
commit
0b3bb66708
3 changed files with 40 additions and 17 deletions
34
lib/nodes.js
34
lib/nodes.js
|
@ -425,34 +425,46 @@
|
|||
ValueNode.prototype.is_statement = function is_statement() {
|
||||
return this.base.is_statement && this.base.is_statement() && !this.has_properties();
|
||||
};
|
||||
// Works out if the value is the start of a chain.
|
||||
ValueNode.prototype.is_start = function is_start(o) {
|
||||
var node;
|
||||
if (this === o.chain_root && this.properties[0] instanceof AccessorNode) {
|
||||
return true;
|
||||
}
|
||||
node = o.chain_root.base || o.chain_root.variable;
|
||||
while (node instanceof CallNode) {
|
||||
node = node.variable;
|
||||
}
|
||||
return node === this;
|
||||
};
|
||||
// We compile a value to JavaScript by compiling and joining each property.
|
||||
// Things get much more insteresting if the chain of properties has *soak*
|
||||
// operators `?.` interspersed. Then we have to take care not to accidentally
|
||||
// evaluate a anything twice when building the soak chain.
|
||||
ValueNode.prototype.compile_node = function compile_node(o) {
|
||||
var _b, _c, _d, baseline, complete, only, op, part, prop, props, temp;
|
||||
var _b, _c, baseline, complete, i, only, op, part, prop, props, temp;
|
||||
only = del(o, 'only_first');
|
||||
op = del(o, 'operation');
|
||||
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
|
||||
if (!(o.chain_root)) {
|
||||
o.chain_root = this;
|
||||
}
|
||||
o.chain_root = o.chain_root || this;
|
||||
baseline = this.base.compile(o);
|
||||
if (this.base instanceof ObjectNode && this.has_properties()) {
|
||||
baseline = ("(" + baseline + ")");
|
||||
}
|
||||
complete = (this.last = baseline);
|
||||
_c = props;
|
||||
for (_b = 0, _d = _c.length; _b < _d; _b++) {
|
||||
prop = _c[_b];
|
||||
_b = props;
|
||||
for (i = 0, _c = _b.length; i < _c; i++) {
|
||||
prop = _b[i];
|
||||
this.source = baseline;
|
||||
if (prop.soak_node) {
|
||||
if (this.base instanceof CallNode && prop === props[0]) {
|
||||
if (this.base instanceof CallNode && i === 0) {
|
||||
temp = o.scope.free_variable();
|
||||
complete = ("(" + temp + " = " + complete + ")" + this.SOAK) + (baseline = temp + prop.compile(o));
|
||||
} else {
|
||||
complete = complete + this.SOAK + (baseline += prop.compile(o));
|
||||
complete = ("(" + (baseline = temp) + " = (" + complete + "))");
|
||||
}
|
||||
if (i === 0 && this.is_start(o)) {
|
||||
complete = ("typeof " + complete + " === \"undefined\" || " + baseline);
|
||||
}
|
||||
complete += this.SOAK + (baseline += prop.compile(o));
|
||||
} else {
|
||||
part = prop.compile(o);
|
||||
baseline += part;
|
||||
|
|
|
@ -298,6 +298,13 @@ exports.ValueNode: class ValueNode extends BaseNode
|
|||
# Values are considered to be statements if their base is a statement.
|
||||
is_statement: ->
|
||||
@base.is_statement and @base.is_statement() and not @has_properties()
|
||||
|
||||
# Works out if the value is the start of a chain.
|
||||
is_start: (o) ->
|
||||
return true if this is o.chain_root and @properties[0] instanceof AccessorNode
|
||||
node: o.chain_root.base or o.chain_root.variable
|
||||
while node instanceof CallNode then node: node.variable
|
||||
node is this
|
||||
|
||||
# We compile a value to JavaScript by compiling and joining each property.
|
||||
# Things get much more insteresting if the chain of properties has *soak*
|
||||
|
@ -307,19 +314,19 @@ exports.ValueNode: class ValueNode extends BaseNode
|
|||
only: del(o, 'only_first')
|
||||
op: del(o, 'operation')
|
||||
props: if only then @properties[0...@properties.length - 1] else @properties
|
||||
o.chain_root: this unless o.chain_root
|
||||
o.chain_root: or this
|
||||
baseline: @base.compile o
|
||||
baseline: "($baseline)" if @base instanceof ObjectNode and @has_properties()
|
||||
complete: @last: baseline
|
||||
|
||||
for prop in props
|
||||
for prop, i in props
|
||||
@source: baseline
|
||||
if prop.soak_node
|
||||
if @base instanceof CallNode and prop is props[0]
|
||||
if @base instanceof CallNode and i is 0
|
||||
temp: o.scope.free_variable()
|
||||
complete: "($temp = $complete)$@SOAK" + (baseline: temp + prop.compile(o))
|
||||
else
|
||||
complete: complete + @SOAK + (baseline: + prop.compile(o))
|
||||
complete: "(${ baseline: temp } = ($complete))"
|
||||
complete: "typeof $complete === \"undefined\" || $baseline" if i is 0 and @is_start(o)
|
||||
complete: + @SOAK + (baseline: + prop.compile(o))
|
||||
else
|
||||
part: prop.compile(o)
|
||||
baseline: + part
|
||||
|
|
|
@ -74,3 +74,7 @@ ok result is '10'
|
|||
result: not value?.property?
|
||||
ok result
|
||||
|
||||
|
||||
# Safely calls values off of non-existent variables.
|
||||
result: nothing?.value
|
||||
ok result is undefined
|
||||
|
|
Loading…
Reference in a new issue