From 1cb6464948550f8aaaa262ca9b1f3f7cc0b21d3e Mon Sep 17 00:00:00 2001 From: satyr Date: Tue, 26 Oct 2010 20:51:02 +0900 Subject: [PATCH] optimized splatting assignment --- lib/nodes.js | 29 ++++++++++++++++------------- src/nodes.coffee | 27 +++++++++++++++------------ test/test_arguments.coffee | 15 +++++++++++---- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index 4af9bb87..55b47723 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -933,15 +933,15 @@ return o.level <= LEVEL_LIST ? val : "(" + val + ")"; }; Assign.prototype.compilePatternMatch = function(o) { - var _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, acc, assigns, code, i, idx, isObject, ivar, obj, objects, olength, ref, rest, splat, top, val, valVar, value; + var _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, acc, assigns, code, i, idx, isObject, ivar, obj, objects, olen, ref, rest, splat, top, val, value, vvar; top = o.level === LEVEL_TOP; value = this.value; objects = this.variable.base.objects; - if (!(olength = objects.length)) { + if (!(olen = objects.length)) { return value.compile(o); } isObject = this.variable.isObject(); - if (top && olength === 1 && !((obj = objects[0]) instanceof Splat)) { + if (top && olen === 1 && !((obj = objects[0]) instanceof Splat)) { if (obj instanceof Assign) { _ref2 = obj, (_ref3 = _ref2.variable, idx = _ref3.base, _ref3), obj = _ref2.value; } else { @@ -956,12 +956,12 @@ value.properties.push(new (acc ? Accessor : Index)(idx)); return new Assign(obj, value).compile(o); } - valVar = value.compile(o, LEVEL_LIST); + vvar = value.compile(o, LEVEL_LIST); assigns = []; splat = false; - if (!IDENTIFIER.test(valVar) || this.variable.assigns(valVar)) { - assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + valVar); - valVar = ref; + if (!IDENTIFIER.test(vvar) || this.variable.assigns(vvar)) { + assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + vvar); + vvar = ref; } for (i = 0, _len = objects.length; i < _len; i++) { obj = objects[i]; @@ -978,12 +978,15 @@ } } if (!splat && obj instanceof Splat) { - if (rest = olength - i - 1 || '') { + val = "" + olen + " <= " + vvar + ".length ? " + (utility('slice')) + ".call(" + vvar + ", " + i; + if (rest = olen - i - 1) { ivar = o.scope.freeVariable('i'); - rest = ", " + ivar + " = " + valVar + ".length - " + rest; + val += ", " + ivar + " = " + vvar + ".length - " + rest + ") : (" + ivar + " = " + i + ", [])"; + } else { + val += ") : []"; } - val = new Literal(utility('slice') + (".call(" + valVar + ", " + i + rest + ")")); - splat = "" + ivar + " < " + i + " ? " + ivar + " = " + i + " : " + ivar + "++"; + val = new Literal(val); + splat = "" + ivar + "++"; } else { if (obj instanceof Splat) { obj = obj.name.compile(o); @@ -995,12 +998,12 @@ } else { acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0); } - val = new Value(new Literal(valVar), [new (acc ? Accessor : Index)(idx)]); + val = new Value(new Literal(vvar), [new (acc ? Accessor : Index)(idx)]); } assigns.push(new Assign(obj, val).compile(o, LEVEL_LIST)); } if (!top) { - assigns.push(valVar); + assigns.push(vvar); } code = assigns.join(', '); return o.level < LEVEL_LIST ? code : "(" + code + ")"; diff --git a/src/nodes.coffee b/src/nodes.coffee index afa9775a..d4bb78b5 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -764,9 +764,9 @@ exports.Assign = class Assign extends Base top = o.level is LEVEL_TOP {value} = this {objects} = @variable.base - return value.compile o unless olength = objects.length + return value.compile o unless olen = objects.length isObject = @variable.isObject() - if top and olength is 1 and (obj = objects[0]) not instanceof Splat + if top and olen is 1 and (obj = objects[0]) not instanceof Splat # Unroll simplest cases: `{v} = x` -> `v = x.v` if obj instanceof Assign {variable: {base: idx}, value: obj} = obj @@ -781,12 +781,12 @@ exports.Assign = class Assign extends Base value = new Value value value.properties.push new (if acc then Accessor else Index) idx return new Assign(obj, value).compile o - valVar = value.compile o, LEVEL_LIST + vvar = value.compile o, LEVEL_LIST assigns = [] splat = false - if not IDENTIFIER.test(valVar) or @variable.assigns(valVar) - assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{valVar}" - valVar = ref + if not IDENTIFIER.test(vvar) or @variable.assigns(vvar) + assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{vvar}" + vvar = ref for obj, i in objects # A regular array pattern-match. idx = i @@ -800,11 +800,14 @@ exports.Assign = class Assign extends Base then [obj, idx] = new Value(obj.unwrapAll()).cacheReference o else idx = if obj.tags.this then obj.properties[0].name else obj if not splat and obj instanceof Splat - if rest = olength - i - 1 or '' + val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}" + if rest = olen - i - 1 ivar = o.scope.freeVariable 'i' - rest = ", #{ivar} = #{valVar}.length - #{rest}" - val = new Literal utility('slice') + ".call(#{valVar}, #{i}#{rest})" - splat = "#{ivar} < #{i} ? #{ivar} = #{i} : #{ivar}++" + val += ", #{ivar} = #{vvar}.length - #{rest}) : (#{ivar} = #{i}, [])" + else + val += ") : []" + val = new Literal val + splat = "#{ivar}++" else if obj instanceof Splat obj = obj.name.compile o @@ -815,9 +818,9 @@ exports.Assign = class Assign extends Base acc = no else acc = isObject and IDENTIFIER.test idx.unwrap().value or 0 - val = new Value new Literal(valVar), [new (if acc then Accessor else Index) idx] + val = new Value new Literal(vvar), [new (if acc then Accessor else Index) idx] assigns.push new Assign(obj, val).compile o, LEVEL_LIST - assigns.push valVar unless top + assigns.push vvar unless top code = assigns.join ', ' if o.level < LEVEL_LIST then code else "(#{code})" diff --git a/test/test_arguments.coffee b/test/test_arguments.coffee index c607395c..159ec74f 100644 --- a/test/test_arguments.coffee +++ b/test/test_arguments.coffee @@ -29,21 +29,28 @@ ok sumOfArgs(1, 2, 3, 4, 5) is 15 ok context.arg is 1 ((splat..., @arg) ->).call context, 1, 2, 3 -ok context.arg is 3 +eq context.arg, 3 ((@arg...) ->).call context, 1, 2, 3 -ok context.arg.join ' ' is '1 2 3' +eq context.arg.join(' '), '1 2 3' class Klass constructor: (@one, @two) -> obj = new Klass 1, 2 -ok obj.one is 1 -ok obj.two is 2 +eq obj.one, 1 +eq obj.two, 2 # Default arguments. obj = f: (q = 123, @p = 456) -> q eq obj.f(), 123 eq obj.p , 456 + +withSplats = (a = 2, b..., c = 3, d = 5) -> a * (b.length + 1) * c * d +eq 30, withSplats() +eq 15, withSplats 1 +eq 5, withSplats 1, 1 +eq 1, withSplats 1, 1, 1 +eq 2, withSplats 1, 1, 1, 1