Re-enabling garbage collection of tempvars, only at function boundaries.

This commit is contained in:
Jeremy Ashkenas
2010-10-06 21:19:05 -04:00
parent d4dac214ab
commit 69d2048ccc
7 changed files with 69 additions and 29 deletions

View File

@@ -2,6 +2,6 @@
lunch = eat food for food in ['toast', 'cheese', 'wine'] lunch = eat food for food in ['toast', 'cheese', 'wine']
# Naive collision detection. # Naive collision detection.
for roid, index in asteroids for roid, pos in asteroids
for roid2 in asteroids when roid isnt roid2 for roid2 in asteroids when roid isnt roid2
roid.explode() if roid.overlaps roid2 roid.explode() if roid.overlaps roid2

View File

@@ -1,4 +1,4 @@
var _i, _j, _len, _len2, _len3, _ref, _result, food, index, lunch, roid, roid2; var _i, _len, _len2, _ref, _result, food, lunch, pos, roid, roid2;
lunch = (function() { lunch = (function() {
_result = []; _ref = ['toast', 'cheese', 'wine']; _result = []; _ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -7,10 +7,10 @@ lunch = (function() {
} }
return _result; return _result;
})(); })();
for (index = 0, _len2 = asteroids.length; index < _len2; index++) { for (pos = 0, _len = asteroids.length; pos < _len; pos++) {
roid = asteroids[index]; roid = asteroids[pos];
for (_j = 0, _len3 = asteroids.length; _j < _len3; _j++) { for (_i = 0, _len2 = asteroids.length; _i < _len2; _i++) {
roid2 = asteroids[_j]; roid2 = asteroids[_i];
if (roid !== roid2) { if (roid !== roid2) {
if (roid.overlaps(roid2)) { if (roid.overlaps(roid2)) {
roid.explode(); roid.explode();

View File

@@ -829,10 +829,10 @@ lyrics = (function() {
lunch <span class="Keyword">=</span> eat food <span class="Keyword">for</span> food <span class="Keyword">in</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>] lunch <span class="Keyword">=</span> eat food <span class="Keyword">for</span> food <span class="Keyword">in</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>]
<span class="Comment"><span class="Comment">#</span> Naive collision detection.</span> <span class="Comment"><span class="Comment">#</span> Naive collision detection.</span>
<span class="Keyword">for</span> roid, index <span class="Keyword">in</span> asteroids <span class="Keyword">for</span> roid, pos <span class="Keyword">in</span> asteroids
<span class="Keyword">for</span> roid2 <span class="Keyword">in</span> asteroids <span class="Keyword">when</span> roid <span class="Keyword">isnt</span> roid2 <span class="Keyword">for</span> roid2 <span class="Keyword">in</span> asteroids <span class="Keyword">when</span> roid <span class="Keyword">isnt</span> roid2
roid.explode() <span class="Keyword">if</span> roid.overlaps roid2 roid.explode() <span class="Keyword">if</span> roid.overlaps roid2
</pre><pre class="idle"><span class="Storage">var</span> _i, _j, _len, _len2, _len3, _ref, _result, food, index, lunch, roid, roid2; </pre><pre class="idle"><span class="Storage">var</span> _i, _len, _len2, _ref, _result, food, lunch, pos, roid, roid2;
lunch <span class="Keyword">=</span> (<span class="Storage">function</span>() { lunch <span class="Keyword">=</span> (<span class="Storage">function</span>() {
_result <span class="Keyword">=</span> []; _ref <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>]; _result <span class="Keyword">=</span> []; _ref <span class="Keyword">=</span> [<span class="String"><span class="String">'</span>toast<span class="String">'</span></span>, <span class="String"><span class="String">'</span>cheese<span class="String">'</span></span>, <span class="String"><span class="String">'</span>wine<span class="String">'</span></span>];
<span class="Keyword">for</span> (_i <span class="Keyword">=</span> <span class="Number">0</span>, _len <span class="Keyword">=</span> _ref.<span class="LibraryConstant">length</span>; _i <span class="Keyword">&lt;</span> _len; _i<span class="Keyword">++</span>) { <span class="Keyword">for</span> (_i <span class="Keyword">=</span> <span class="Number">0</span>, _len <span class="Keyword">=</span> _ref.<span class="LibraryConstant">length</span>; _i <span class="Keyword">&lt;</span> _len; _i<span class="Keyword">++</span>) {
@@ -841,10 +841,10 @@ lunch <span class="Keyword">=</span> (<span class="Storage">function</span>() {
} }
<span class="Keyword">return</span> _result; <span class="Keyword">return</span> _result;
})(); })();
<span class="Keyword">for</span> (index <span class="Keyword">=</span> <span class="Number">0</span>, _len2 <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; index <span class="Keyword">&lt;</span> _len2; index<span class="Keyword">++</span>) { <span class="Keyword">for</span> (pos <span class="Keyword">=</span> <span class="Number">0</span>, _len <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; pos <span class="Keyword">&lt;</span> _len; pos<span class="Keyword">++</span>) {
roid <span class="Keyword">=</span> asteroids[index]; roid <span class="Keyword">=</span> asteroids[pos];
<span class="Keyword">for</span> (_j <span class="Keyword">=</span> <span class="Number">0</span>, _len3 <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; _j <span class="Keyword">&lt;</span> _len3; _j<span class="Keyword">++</span>) { <span class="Keyword">for</span> (_i <span class="Keyword">=</span> <span class="Number">0</span>, _len2 <span class="Keyword">=</span> asteroids.<span class="LibraryConstant">length</span>; _i <span class="Keyword">&lt;</span> _len2; _i<span class="Keyword">++</span>) {
roid2 <span class="Keyword">=</span> asteroids[_j]; roid2 <span class="Keyword">=</span> asteroids[_i];
<span class="Keyword">if</span> (roid <span class="Keyword">!</span><span class="Keyword">==</span> roid2) { <span class="Keyword">if</span> (roid <span class="Keyword">!</span><span class="Keyword">==</span> roid2) {
<span class="Keyword">if</span> (roid.overlaps(roid2)) { <span class="Keyword">if</span> (roid.overlaps(roid2)) {
roid.explode(); roid.explode();

View File

@@ -785,7 +785,7 @@
ObjectNode.prototype.children = ['properties']; ObjectNode.prototype.children = ['properties'];
ObjectNode.prototype.topSensitive = YES; ObjectNode.prototype.topSensitive = YES;
ObjectNode.prototype.compileNode = function(o) { ObjectNode.prototype.compileNode = function(o) {
var _i, _len, _len2, _ref2, _ref3, _result, _result2, i, indent, join, lastNoncom, nonComments, obj, prop, props, top; var _i, _len, _ref2, _result, i, indent, join, lastNoncom, nonComments, obj, prop, props, top;
top = del(o, 'top'); top = del(o, 'top');
o.indent = this.idt(1); o.indent = this.idt(1);
nonComments = (function() { nonComments = (function() {
@@ -800,10 +800,10 @@
}).call(this); }).call(this);
lastNoncom = last(nonComments); lastNoncom = last(nonComments);
props = (function() { props = (function() {
_result2 = []; _ref3 = this.properties; _result = []; _ref2 = this.properties;
for (i = 0, _len2 = _ref3.length; i < _len2; i++) { for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref3[i]; prop = _ref2[i];
_result2.push((function() { _result.push((function() {
join = ",\n"; join = ",\n";
if ((prop === lastNoncom) || (prop instanceof CommentNode)) { if ((prop === lastNoncom) || (prop instanceof CommentNode)) {
join = "\n"; join = "\n";
@@ -820,7 +820,7 @@
return indent + prop.compile(o) + join; return indent + prop.compile(o) + join;
}).call(this)); }).call(this));
} }
return _result2; return _result;
}).call(this); }).call(this);
props = props.join(''); props = props.join('');
obj = '{' + (props ? '\n' + props + '\n' + this.idt() : '') + '}'; obj = '{' + (props ? '\n' + props + '\n' + this.idt() : '') + '}';
@@ -1102,7 +1102,7 @@
__extends(CodeNode, BaseNode); __extends(CodeNode, BaseNode);
CodeNode.prototype.children = ['params', 'body']; CodeNode.prototype.children = ['params', 'body'];
CodeNode.prototype.compileNode = function(o) { CodeNode.prototype.compileNode = function(o) {
var _i, _j, _len, _len2, _len3, _ref2, _ref3, _result, close, code, empty, func, i, open, param, params, sharedScope, splat, top, value; var _i, _len, _len2, _ref2, _ref3, _result, close, code, empty, func, i, open, param, params, sharedScope, splat, top, value;
sharedScope = del(o, 'sharedScope'); sharedScope = del(o, 'sharedScope');
top = del(o, 'top'); top = del(o, 'top');
o.scope = sharedScope || new Scope(o.scope, this.body, this); o.scope = sharedScope || new Scope(o.scope, this.body, this);
@@ -1139,6 +1139,7 @@
} }
} }
} }
o.scope.startLevel();
params = (function() { params = (function() {
_result = []; _result = [];
for (_i = 0, _len2 = params.length; _i < _len2; _i++) { for (_i = 0, _len2 = params.length; _i < _len2; _i++) {
@@ -1150,8 +1151,8 @@
if (!(empty)) { if (!(empty)) {
this.body.makeReturn(); this.body.makeReturn();
} }
for (_j = 0, _len3 = params.length; _j < _len3; _j++) { for (_i = 0, _len2 = params.length; _i < _len2; _i++) {
param = params[_j]; param = params[_i];
(o.scope.parameter(param)); (o.scope.parameter(param));
} }
if (this.className) { if (this.className) {
@@ -1161,6 +1162,7 @@
open = this.className ? ("(function() {\n" + (this.idt(1)) + "return function " + (this.className) + "(") : "function("; open = this.className ? ("(function() {\n" + (this.idt(1)) + "return function " + (this.className) + "(") : "function(";
close = this.className ? ("" + (code && this.idt(1)) + "};\n" + (this.tab) + "})()") : ("" + (code && this.tab) + "}"); close = this.className ? ("" + (code && this.idt(1)) + "};\n" + (this.tab) + "})()") : ("" + (code && this.tab) + "}");
func = ("" + open + (params.join(', ')) + ") {" + code + close); func = ("" + open + (params.join(', ')) + ") {" + code + close);
o.scope.endLevel();
if (this.bound) { if (this.bound) {
return ("" + (utility('bind')) + "(" + func + ", " + (this.context) + ")"); return ("" + (utility('bind')) + "(" + func + ", " + (this.context) + ")");
} }

View File

@@ -11,13 +11,31 @@
this.variables = { this.variables = {
'arguments': 'arguments' 'arguments': 'arguments'
}; };
if (!(this.parent)) { if (this.parent) {
this.garbage = this.parent.garbage;
} else {
this.garbage = [];
Scope.root = this; Scope.root = this;
} }
return this; return this;
}; };
})(); })();
Scope.root = null; Scope.root = null;
Scope.prototype.startLevel = function() {
return this.garbage.push([]);
};
Scope.prototype.endLevel = function() {
var _i, _len, _ref2, _result, name, vars;
vars = this.variables;
_result = []; _ref2 = this.garbage.pop();
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
name = _ref2[_i];
if (vars[name] === 'var') {
_result.push(vars[name] = 'reuse');
}
}
return _result;
};
Scope.prototype.find = function(name, options) { Scope.prototype.find = function(name, options) {
if (this.check(name, options)) { if (this.check(name, options)) {
return true; return true;
@@ -54,10 +72,13 @@
Scope.prototype.freeVariable = function(type) { Scope.prototype.freeVariable = function(type) {
var index, temp; var index, temp;
index = 0; index = 0;
while (this.check(temp = this.temporary(type, index))) { while (this.check(temp = this.temporary(type, index)) && this.variables[temp] !== 'reuse') {
index++; index++;
} }
this.variables[temp] = 'var'; this.variables[temp] = 'var';
if (this.garbage.length) {
last(this.garbage).push(temp);
}
return temp; return temp;
}; };
Scope.prototype.assign = function(name, value) { Scope.prototype.assign = function(name, value) {
@@ -68,7 +89,7 @@
}; };
Scope.prototype.hasDeclarations = function(body) { Scope.prototype.hasDeclarations = function(body) {
return body === this.expressions && this.any(function(k, val) { return body === this.expressions && this.any(function(k, val) {
return val === 'var'; return ('var' === val || 'reuse' === val);
}); });
}; };
Scope.prototype.hasAssignments = function(body) { Scope.prototype.hasAssignments = function(body) {
@@ -83,7 +104,7 @@
for (key in _ref2) { for (key in _ref2) {
if (!__hasProp.call(_ref2, key)) continue; if (!__hasProp.call(_ref2, key)) continue;
val = _ref2[key]; val = _ref2[key];
if (val === 'var') { if (('var' === val || 'reuse' === val)) {
_result.push(key); _result.push(key);
} }
} }

View File

@@ -984,6 +984,7 @@ exports.CodeNode = class CodeNode extends BaseNode
@body.unshift(splat) @body.unshift(splat)
else else
params.push param params.push param
o.scope.startLevel()
params = (param.compile(o) for param in params) params = (param.compile(o) for param in params)
@body.makeReturn() unless empty @body.makeReturn() unless empty
(o.scope.parameter(param)) for param in params (o.scope.parameter(param)) for param in params
@@ -992,6 +993,7 @@ exports.CodeNode = class CodeNode extends BaseNode
open = if @className then "(function() {\n#{@idt(1)}return function #{@className}(" else "function(" open = if @className then "(function() {\n#{@idt(1)}return function #{@className}(" else "function("
close = if @className then "#{code and @idt(1)}};\n#{@tab}})()" else "#{code and @tab}}" close = if @className then "#{code and @idt(1)}};\n#{@tab}})()" else "#{code and @tab}}"
func = "#{open}#{ params.join(', ') }) {#{code}#{close}" func = "#{open}#{ params.join(', ') }) {#{code}#{close}"
o.scope.endLevel()
return "#{utility 'bind'}(#{func}, #{@context})" if @bound return "#{utility 'bind'}(#{func}, #{@context})" if @bound
if top then "(#{func})" else func if top then "(#{func})" else func

View File

@@ -19,7 +19,21 @@ exports.Scope = class Scope
# it wraps. # it wraps.
constructor: (@parent, @expressions, @method) -> constructor: (@parent, @expressions, @method) ->
@variables = {'arguments'} @variables = {'arguments'}
Scope.root = this unless @parent if @parent
@garbage = @parent.garbage
else
@garbage = []
Scope.root = this
# Create a new garbage level
startLevel: ->
@garbage.push []
# Return to the previous garbage level and erase referenced temporary
# variables in current level from scope.
endLevel: ->
vars = @variables
(vars[name] = 'reuse') for name in @garbage.pop() when vars[name] is 'var'
# Look up a variable name in lexical scope, and declare it if it does not # Look up a variable name in lexical scope, and declare it if it does not
# already exist. # already exist.
@@ -57,8 +71,9 @@ exports.Scope = class Scope
# compiler-generated variable. `_var`, `_var2`, and so on... # compiler-generated variable. `_var`, `_var2`, and so on...
freeVariable: (type) -> freeVariable: (type) ->
index = 0 index = 0
index++ while @check(temp = @temporary type, index) index++ while @check(temp = @temporary type, index) and @variables[temp] isnt 'reuse'
@variables[temp] = 'var' @variables[temp] = 'var'
last(@garbage).push temp if @garbage.length
temp temp
# Ensure that an assignment is made at the top of this scope # Ensure that an assignment is made at the top of this scope
@@ -69,7 +84,7 @@ exports.Scope = class Scope
# Does this scope reference any variables that need to be declared in the # Does this scope reference any variables that need to be declared in the
# given function body? # given function body?
hasDeclarations: (body) -> hasDeclarations: (body) ->
body is @expressions and @any (k, val) -> val is 'var' body is @expressions and @any (k, val) -> val in ['var', 'reuse']
# Does this scope reference any assignments that need to be declared at the # Does this scope reference any assignments that need to be declared at the
# top of the given function body? # top of the given function body?
@@ -78,7 +93,7 @@ exports.Scope = class Scope
# Return the list of variables first declared in this scope. # Return the list of variables first declared in this scope.
declaredVariables: -> declaredVariables: ->
(key for key, val of @variables when val is 'var').sort() (key for key, val of @variables when val in ['var', 'reuse']).sort()
# Return the list of assignments that are supposed to be made at the top # Return the list of assignments that are supposed to be made at the top
# of this scope. # of this scope.