fix infinite recursion in function inlining

This commit is contained in:
kzc 2017-11-06 10:05:23 -05:00
parent 2cfb5aa7da
commit 6784aea026
3 changed files with 196 additions and 8 deletions

View File

@ -91,19 +91,23 @@ var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos
}, null); }, null);
var AST_Node = DEFNODE("Node", "start end", { var AST_Node = DEFNODE("Node", "start end", {
_clone: function(deep) { _clone: function(deep, depth) {
if (typeof depth == "undefined") depth = 0;
if (depth++ > 1000) {
throw new Error("clone depth exceeded (" + this.start.line + ", " + this.start.col + ")");
}
if (deep) { if (deep) {
var self = this.clone(); var self = this.clone(false, depth);
return self.transform(new TreeTransformer(function(node) { return self.transform(new TreeTransformer(function(node) {
if (node !== self) { if (node !== self) {
return node.clone(true); return node.clone(true, depth);
} }
})); }));
} }
return new this.CTOR(this); return new this.CTOR(this);
}, },
clone: function(deep) { clone: function(deep, depth) {
return this._clone(deep); return this._clone(deep, depth);
}, },
$documentation: "Base class of all AST nodes", $documentation: "Base class of all AST nodes",
$propdoc: { $propdoc: {
@ -202,8 +206,8 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
this.body._walk(visitor); this.body._walk(visitor);
}); });
}, },
clone: function(deep) { clone: function(deep, depth) {
var node = this._clone(deep); var node = this._clone(deep, depth);
if (deep) { if (deep) {
var label = node.label; var label = node.label;
var def = this.label; var def = this.label;

View File

@ -4243,7 +4243,11 @@ merge(Compressor.prototype, {
} }
if (fixed && d.single_use) { if (fixed && d.single_use) {
var value = fixed.optimize(compressor); var value = fixed.optimize(compressor);
try {
return value === fixed ? fixed.clone(true) : value; return value === fixed ? fixed.clone(true) : value;
} catch (x) {
// infinite clone - do nothing
}
} }
if (fixed && d.should_replace === undefined) { if (fixed && d.should_replace === undefined) {
var init; var init;

View File

@ -3654,3 +3654,183 @@ issue_2440_with_2: {
} }
} }
} }
recursive_inlining_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
!function(){
function foo() { bar(); }
function bar() { foo(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
}
recursive_inlining_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
!function(){
function foo() { qux(); }
function bar() { foo(); }
function qux() { bar(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
}
recursive_inlining_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
}();
}
expect: {
!function() {
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) (function(x) {
console.log("bar", x);
if (x) qux(x - 1);
})(x - 1);
})(x - 1);
}
qux(4);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
}();
}
expect: {
!function() {
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) bar(x - 1);
})(x - 1);
}
qux(4);
bar(5);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_5: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
foo(3);
}();
}
expect: {
!function() {
function foo(x) {
console.log("foo", x);
if (x) bar(x - 1);
}
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) foo(x - 1);
}
qux(4);
bar(5);
foo(3);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}