mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-04-11 03:00:13 -04:00
Added safe soaking on non-existent variables.
This commit is contained in:
34
lib/nodes.js
34
lib/nodes.js
@@ -425,34 +425,46 @@
|
|||||||
ValueNode.prototype.is_statement = function is_statement() {
|
ValueNode.prototype.is_statement = function is_statement() {
|
||||||
return this.base.is_statement && this.base.is_statement() && !this.has_properties();
|
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.
|
// We compile a value to JavaScript by compiling and joining each property.
|
||||||
// Things get much more insteresting if the chain of properties has *soak*
|
// Things get much more insteresting if the chain of properties has *soak*
|
||||||
// operators `?.` interspersed. Then we have to take care not to accidentally
|
// operators `?.` interspersed. Then we have to take care not to accidentally
|
||||||
// evaluate a anything twice when building the soak chain.
|
// evaluate a anything twice when building the soak chain.
|
||||||
ValueNode.prototype.compile_node = function compile_node(o) {
|
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');
|
only = del(o, 'only_first');
|
||||||
op = del(o, 'operation');
|
op = del(o, 'operation');
|
||||||
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
|
props = only ? this.properties.slice(0, this.properties.length - 1) : this.properties;
|
||||||
if (!(o.chain_root)) {
|
o.chain_root = o.chain_root || this;
|
||||||
o.chain_root = this;
|
|
||||||
}
|
|
||||||
baseline = this.base.compile(o);
|
baseline = this.base.compile(o);
|
||||||
if (this.base instanceof ObjectNode && this.has_properties()) {
|
if (this.base instanceof ObjectNode && this.has_properties()) {
|
||||||
baseline = ("(" + baseline + ")");
|
baseline = ("(" + baseline + ")");
|
||||||
}
|
}
|
||||||
complete = (this.last = baseline);
|
complete = (this.last = baseline);
|
||||||
_c = props;
|
_b = props;
|
||||||
for (_b = 0, _d = _c.length; _b < _d; _b++) {
|
for (i = 0, _c = _b.length; i < _c; i++) {
|
||||||
prop = _c[_b];
|
prop = _b[i];
|
||||||
this.source = baseline;
|
this.source = baseline;
|
||||||
if (prop.soak_node) {
|
if (prop.soak_node) {
|
||||||
if (this.base instanceof CallNode && prop === props[0]) {
|
if (this.base instanceof CallNode && i === 0) {
|
||||||
temp = o.scope.free_variable();
|
temp = o.scope.free_variable();
|
||||||
complete = ("(" + temp + " = " + complete + ")" + this.SOAK) + (baseline = temp + prop.compile(o));
|
complete = ("(" + (baseline = temp) + " = (" + complete + "))");
|
||||||
} else {
|
|
||||||
complete = complete + this.SOAK + (baseline += prop.compile(o));
|
|
||||||
}
|
}
|
||||||
|
if (i === 0 && this.is_start(o)) {
|
||||||
|
complete = ("typeof " + complete + " === \"undefined\" || " + baseline);
|
||||||
|
}
|
||||||
|
complete += this.SOAK + (baseline += prop.compile(o));
|
||||||
} else {
|
} else {
|
||||||
part = prop.compile(o);
|
part = prop.compile(o);
|
||||||
baseline += part;
|
baseline += part;
|
||||||
|
|||||||
@@ -298,6 +298,13 @@ exports.ValueNode: class ValueNode extends BaseNode
|
|||||||
# Values are considered to be statements if their base is a statement.
|
# Values are considered to be statements if their base is a statement.
|
||||||
is_statement: ->
|
is_statement: ->
|
||||||
@base.is_statement and @base.is_statement() and not @has_properties()
|
@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.
|
# We compile a value to JavaScript by compiling and joining each property.
|
||||||
# Things get much more insteresting if the chain of properties has *soak*
|
# 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')
|
only: del(o, 'only_first')
|
||||||
op: del(o, 'operation')
|
op: del(o, 'operation')
|
||||||
props: if only then @properties[0...@properties.length - 1] else @properties
|
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: @base.compile o
|
||||||
baseline: "($baseline)" if @base instanceof ObjectNode and @has_properties()
|
baseline: "($baseline)" if @base instanceof ObjectNode and @has_properties()
|
||||||
complete: @last: baseline
|
complete: @last: baseline
|
||||||
|
|
||||||
for prop in props
|
for prop, i in props
|
||||||
@source: baseline
|
@source: baseline
|
||||||
if prop.soak_node
|
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()
|
temp: o.scope.free_variable()
|
||||||
complete: "($temp = $complete)$@SOAK" + (baseline: temp + prop.compile(o))
|
complete: "(${ baseline: temp } = ($complete))"
|
||||||
else
|
complete: "typeof $complete === \"undefined\" || $baseline" if i is 0 and @is_start(o)
|
||||||
complete: complete + @SOAK + (baseline: + prop.compile(o))
|
complete: + @SOAK + (baseline: + prop.compile(o))
|
||||||
else
|
else
|
||||||
part: prop.compile(o)
|
part: prop.compile(o)
|
||||||
baseline: + part
|
baseline: + part
|
||||||
|
|||||||
@@ -74,3 +74,7 @@ ok result is '10'
|
|||||||
result: not value?.property?
|
result: not value?.property?
|
||||||
ok result
|
ok result
|
||||||
|
|
||||||
|
|
||||||
|
# Safely calls values off of non-existent variables.
|
||||||
|
result: nothing?.value
|
||||||
|
ok result is undefined
|
||||||
|
|||||||
Reference in New Issue
Block a user