From 22f19522fffd20c95cb3edebcb0ce009f5fac25e Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Tue, 13 Jan 2015 20:21:45 +0100 Subject: [PATCH] Get rid of `Scope.root` hack Using the static property `Scope.root` for the top-level scope of a file is a hack, which makes it impossible to have several independent `Scope` instances at the same time (should we ever need that). This commit makes every instance have a reference to its root instead. --- lib/coffee-script/nodes.js | 35 ++++++++++++++++++----------------- lib/coffee-script/scope.js | 7 +++---- src/nodes.coffee | 33 +++++++++++++++++---------------- src/scope.litcoffee | 14 ++++++-------- 4 files changed, 44 insertions(+), 45 deletions(-) diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index f9eeb9a4..b74ffff8 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -1054,7 +1054,7 @@ Extends.prototype.children = ['child', 'parent']; Extends.prototype.compileToFragments = function(o) { - return new Call(new Value(new Literal(utility('extends'))), [this.child, this.parent]).compileToFragments(o); + return new Call(new Value(new Literal(utility('extends', o))), [this.child, this.parent]).compileToFragments(o); }; return Extends; @@ -1423,7 +1423,7 @@ for (_i = 0, _len = _ref2.length; _i < _len; _i++) { bvar = _ref2[_i]; lhs = (new Value(new Literal("this"), [new Access(bvar)])).compile(o); - this.ctor.body.unshift(new Literal(lhs + " = " + (utility('bind')) + "(" + lhs + ", this)")); + this.ctor.body.unshift(new Literal(lhs + " = " + (utility('bind', o)) + "(" + lhs + ", this)")); } }; @@ -1698,7 +1698,7 @@ if (!expandedIdx && obj instanceof Splat) { name = obj.name.unwrap().value; obj = obj.unwrap(); - val = olen + " <= " + vvarText + ".length ? " + (utility('slice')) + ".call(" + vvarText + ", " + i; + val = olen + " <= " + vvarText + ".length ? " + (utility('slice', o)) + ".call(" + vvarText + ", " + i; if (rest = olen - i - 1) { ivar = o.scope.freeVariable('i'); val += ", " + ivar + " = " + vvarText + ".length - " + rest + ") : (" + ivar + " = " + i + ", [])"; @@ -2111,13 +2111,13 @@ if (apply) { return fragments; } - return [].concat(node.makeCode((utility('slice')) + ".call("), fragments, node.makeCode(")")); + return [].concat(node.makeCode((utility('slice', o)) + ".call("), fragments, node.makeCode(")")); } args = list.slice(index); for (i = _i = 0, _len = args.length; _i < _len; i = ++_i) { node = args[i]; compiledNode = node.compileToFragments(o, LEVEL_LIST); - args[i] = node instanceof Splat ? [].concat(node.makeCode((utility('slice')) + ".call("), compiledNode, node.makeCode(")")) : [].concat(node.makeCode("["), compiledNode, node.makeCode("]")); + args[i] = node instanceof Splat ? [].concat(node.makeCode((utility('slice', o)) + ".call("), compiledNode, node.makeCode(")")) : [].concat(node.makeCode("["), compiledNode, node.makeCode("]")); } if (index === 0) { node = list[0]; @@ -2489,7 +2489,7 @@ Op.prototype.compileModulo = function(o) { var mod; - mod = new Value(new Literal(utility('modulo'))); + mod = new Value(new Literal(utility('modulo', o))); return new Call(mod, [this.first, this.second]).compileToFragments(o); }; @@ -2555,7 +2555,7 @@ In.prototype.compileLoopTest = function(o) { var fragments, ref, sub, _ref2; _ref2 = this.object.cache(o, LEVEL_LIST), sub = _ref2[0], ref = _ref2[1]; - fragments = [].concat(this.makeCode(utility('indexOf') + ".call("), this.array.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), ref, this.makeCode(") " + (this.negated ? '< 0' : '>= 0'))); + fragments = [].concat(this.makeCode(utility('indexOf', o) + ".call("), this.array.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), ref, this.makeCode(") " + (this.negated ? '< 0' : '>= 0'))); if (fragmentsToText(sub) === fragmentsToText(ref)) { return fragments; } @@ -2839,7 +2839,7 @@ if (this.object) { forPartFragments = [this.makeCode(kvar + " in " + svar)]; if (this.own) { - guardPart = "\n" + idt1 + "if (!" + (utility('hasProp')) + ".call(" + svar + ", " + kvar + ")) continue;"; + guardPart = "\n" + idt1 + "if (!" + (utility('hasProp', o)) + ".call(" + svar + ", " + kvar + ")) continue;"; } } bodyFragments = body.compileToFragments(merge(o, { @@ -3093,8 +3093,8 @@ })(Base); UTILITIES = { - "extends": function() { - return "function(child, parent) { for (var key in parent) { if (" + (utility('hasProp')) + ".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }"; + "extends": function(o) { + return "function(child, parent) { for (var key in parent) { if (" + (utility('hasProp', o)) + ".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }"; }, bind: function() { return 'function(fn, me){ return function(){ return fn.apply(me, arguments); }; }'; @@ -3143,14 +3143,15 @@ IS_REGEX = /^\//; - utility = function(name) { - var ref; - if (name in Scope.root.utilities) { - return Scope.root.utilities[name]; + utility = function(name, o) { + var ref, root; + root = o.scope.root; + if (name in root.utilities) { + return root.utilities[name]; } else { - ref = Scope.root.freeVariable("_" + name); - Scope.root.assign(ref, UTILITIES[name]()); - return Scope.root.utilities[name] = ref; + ref = root.freeVariable("_" + name); + root.assign(ref, UTILITIES[name](o)); + return root.utilities[name] = ref; } }; diff --git a/lib/coffee-script/scope.js b/lib/coffee-script/scope.js index 2ffb641d..0a8a50fc 100644 --- a/lib/coffee-script/scope.js +++ b/lib/coffee-script/scope.js @@ -6,9 +6,8 @@ _ref = require('./helpers'), extend = _ref.extend, last = _ref.last; exports.Scope = Scope = (function() { - Scope.root = null; - function Scope(_at_parent, _at_expressions, _at_method, _at_referencedVars) { + var _ref1, _ref2; this.parent = _at_parent; this.expressions = _at_expressions; this.method = _at_method; @@ -22,8 +21,8 @@ this.positions = {}; if (!this.parent) { this.utilities = {}; - Scope.root = this; } + this.root = (_ref1 = (_ref2 = this.parent) != null ? _ref2.root : void 0) != null ? _ref1 : this; } Scope.prototype.add = function(name, type, immediate) { @@ -96,7 +95,7 @@ index = 0; while (true) { temp = this.temporary(name, index); - if (!(this.check(temp) || __indexOf.call(Scope.root.referencedVars, temp) >= 0)) { + if (!(this.check(temp) || __indexOf.call(this.root.referencedVars, temp) >= 0)) { break; } index++; diff --git a/src/nodes.coffee b/src/nodes.coffee index d624b700..274a20f3 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -736,7 +736,7 @@ exports.Extends = class Extends extends Base # Hooks one constructor into another's prototype chain. compileToFragments: (o) -> - new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compileToFragments o + new Call(new Value(new Literal utility 'extends', o), [@child, @parent]).compileToFragments o #### Access @@ -1017,7 +1017,7 @@ exports.Class = class Class extends Base addBoundFunctions: (o) -> for bvar in @boundFuncs lhs = (new Value (new Literal "this"), [new Access bvar]).compile o - @ctor.body.unshift new Literal "#{lhs} = #{utility 'bind'}(#{lhs}, this)" + @ctor.body.unshift new Literal "#{lhs} = #{utility 'bind', o}(#{lhs}, this)" return # Merge the properties from a top-level object as prototypal properties @@ -1231,7 +1231,7 @@ exports.Assign = class Assign extends Base if not expandedIdx and obj instanceof Splat name = obj.name.unwrap().value obj = obj.unwrap() - val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}" + val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice', o }.call(#{vvarText}, #{i}" if rest = olen - i - 1 ivar = o.scope.freeVariable 'i' val += ", #{ivar} = #{vvarText}.length - #{rest}) : (#{ivar} = #{i}, [])" @@ -1506,12 +1506,12 @@ exports.Splat = class Splat extends Base node = list[0] fragments = node.compileToFragments o, LEVEL_LIST return fragments if apply - return [].concat node.makeCode("#{ utility 'slice' }.call("), fragments, node.makeCode(")") + return [].concat node.makeCode("#{ utility 'slice', o }.call("), fragments, node.makeCode(")") args = list[index..] for node, i in args compiledNode = node.compileToFragments o, LEVEL_LIST args[i] = if node instanceof Splat - then [].concat node.makeCode("#{ utility 'slice' }.call("), compiledNode, node.makeCode(")") + then [].concat node.makeCode("#{ utility 'slice', o }.call("), compiledNode, node.makeCode(")") else [].concat node.makeCode("["), compiledNode, node.makeCode("]") if index is 0 node = list[0] @@ -1777,7 +1777,7 @@ exports.Op = class Op extends Base new Call(floor, [div]).compileToFragments o compileModulo: (o) -> - mod = new Value new Literal utility 'modulo' + mod = new Value new Literal utility 'modulo', o new Call(mod, [@first, @second]).compileToFragments o toString: (idt) -> @@ -1811,7 +1811,7 @@ exports.In = class In extends Base compileLoopTest: (o) -> [sub, ref] = @object.cache o, LEVEL_LIST - fragments = [].concat @makeCode(utility('indexOf') + ".call("), @array.compileToFragments(o, LEVEL_LIST), + fragments = [].concat @makeCode(utility('indexOf', o) + ".call("), @array.compileToFragments(o, LEVEL_LIST), @makeCode(", "), ref, @makeCode(") " + if @negated then '< 0' else '>= 0') return fragments if fragmentsToText(sub) is fragmentsToText(ref) fragments = sub.concat @makeCode(', '), fragments @@ -2020,7 +2020,7 @@ exports.For = class For extends While varPart = "\n#{idt1}#{namePart};" if namePart if @object forPartFragments = [@makeCode("#{kvar} in #{svar}")] - guardPart = "\n#{idt1}if (!#{utility 'hasProp'}.call(#{svar}, #{kvar})) continue;" if @own + guardPart = "\n#{idt1}if (!#{utility 'hasProp', o}.call(#{svar}, #{kvar})) continue;" if @own bodyFragments = body.compileToFragments merge(o, indent: idt1), LEVEL_TOP if bodyFragments and (bodyFragments.length > 0) bodyFragments = [].concat @makeCode("\n"), bodyFragments, @makeCode("\n") @@ -2179,10 +2179,10 @@ UTILITIES = # Correctly set up a prototype chain for inheritance, including a reference # to the superclass for `super()` calls, and copies of any static properties. - extends: -> " + extends: (o) -> " function(child, parent) { for (var key in parent) { - if (#{utility 'hasProp'}.call(parent, key)) child[key] = parent[key]; + if (#{utility 'hasProp', o}.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; @@ -2259,13 +2259,14 @@ IS_REGEX = /^\// # ---------------- # Helper for ensuring that utility functions are assigned at the top level. -utility = (name) -> - if name of Scope.root.utilities - Scope.root.utilities[name] +utility = (name, o) -> + {root} = o.scope + if name of root.utilities + root.utilities[name] else - ref = Scope.root.freeVariable "_#{name}" - Scope.root.assign ref, UTILITIES[name]() - Scope.root.utilities[name] = ref + ref = root.freeVariable "_#{name}" + root.assign ref, UTILITIES[name] o + root.utilities[name] = ref multident = (code, tab) -> code = code.replace /\n/g, '$&' + tab diff --git a/src/scope.litcoffee b/src/scope.litcoffee index ab813ece..b381f3c1 100644 --- a/src/scope.litcoffee +++ b/src/scope.litcoffee @@ -11,10 +11,6 @@ Import the helpers we plan to use. exports.Scope = class Scope -The `root` is the top-level **Scope** object for a given file. - - @root: null - Initialize a scope with its parent, for lookups up the chain, as well as a reference to the **Block** node it belongs to, which is where it should declare its variables, a reference to the function that @@ -24,9 +20,11 @@ and therefore should be avoided when generating variables. constructor: (@parent, @expressions, @method, @referencedVars) -> @variables = [{name: 'arguments', type: 'arguments'}] @positions = {} - unless @parent - @utilities = {} - Scope.root = this + @utilities = {} unless @parent + +The `@root` is the top-level **Scope** object for a given file. + + @root = @parent?.root ? this Adds a new variable or overrides an existing one. @@ -89,7 +87,7 @@ compiler-generated variable. `_var`, `_var2`, and so on... index = 0 loop temp = @temporary name, index - break unless @check(temp) or temp in Scope.root.referencedVars + break unless @check(temp) or temp in @root.referencedVars index++ @add temp, 'var', yes if reserve temp