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.
This commit is contained in:
Simon Lydell
2015-01-13 20:21:45 +01:00
parent 9fa77af576
commit 22f19522ff
4 changed files with 44 additions and 45 deletions

View File

@@ -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;
}
};

View File

@@ -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++;

View File

@@ -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

View File

@@ -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