handle overlapped variable definitions (#1691)
Process variable definitions with or without assigned values against: - `arguments` - named function arguments - multiple definitions within same scope Essentially demote variable declarations with no value assignments. Also fixed invalid use of `AST_VarDef` over `arguments` - should use a member of `AST_SymbolDeclaration` instead.
This commit is contained in:
@@ -260,7 +260,7 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var d = node.definition();
|
||||
d.references.push(node);
|
||||
if (!d.fixed || !is_safe(d)
|
||||
if (d.fixed === undefined || !is_safe(d)
|
||||
|| is_modified(node, 0, d.fixed instanceof AST_Lambda)) {
|
||||
d.fixed = false;
|
||||
}
|
||||
@@ -270,10 +270,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_VarDef) {
|
||||
var d = node.name.definition();
|
||||
if (d.fixed === undefined) {
|
||||
d.fixed = node.value || make_node(AST_Undefined, node);
|
||||
if (d.fixed == null) {
|
||||
d.fixed = node.value;
|
||||
mark_as_safe(d);
|
||||
} else {
|
||||
} else if (node.value) {
|
||||
d.fixed = false;
|
||||
}
|
||||
}
|
||||
@@ -357,7 +357,14 @@ merge(Compressor.prototype, {
|
||||
|
||||
function is_safe(def) {
|
||||
for (var i = safe_ids.length, id = def.id; --i >= 0;) {
|
||||
if (safe_ids[i][id]) return true;
|
||||
if (safe_ids[i][id]) {
|
||||
if (def.fixed == null) {
|
||||
var orig = def.orig[0];
|
||||
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
||||
def.fixed = make_node(AST_Undefined, orig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
lib/scope.js
32
lib/scope.js
@@ -100,15 +100,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
if (node instanceof AST_Catch) {
|
||||
var save_scope = scope;
|
||||
scope = new AST_Scope(node);
|
||||
scope.init_scope_vars();
|
||||
scope.parent_scope = save_scope;
|
||||
scope.init_scope_vars(save_scope);
|
||||
descend();
|
||||
scope = save_scope;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
node.init_scope_vars();
|
||||
var save_scope = node.parent_scope = scope;
|
||||
node.init_scope_vars(scope);
|
||||
var save_scope = scope;
|
||||
var save_defun = defun;
|
||||
var save_labels = labels;
|
||||
defun = scope = node;
|
||||
@@ -243,23 +242,24 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){
|
||||
}
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("init_scope_vars", function(){
|
||||
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||
this.parent_scope = null; // the parent scope
|
||||
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||
this.cname = -1; // the current index for mangling functions/variables
|
||||
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
|
||||
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||
this.parent_scope = parent_scope; // the parent scope
|
||||
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||
this.cname = -1; // the current index for mangling functions/variables
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||
this.uses_arguments = false;
|
||||
|
||||
var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end });
|
||||
var def = new SymbolDef(this, this.variables.size(), symbol);
|
||||
this.variables.set(symbol.name, def);
|
||||
this.def_variable(new AST_SymbolVar({
|
||||
name: "arguments",
|
||||
start: this.start,
|
||||
end: this.end
|
||||
}));
|
||||
});
|
||||
|
||||
AST_SymbolRef.DEFMETHOD("reference", function(options) {
|
||||
|
||||
@@ -425,7 +425,7 @@ iife_new: {
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
multi_def: {
|
||||
multi_def_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
@@ -435,7 +435,7 @@ multi_def: {
|
||||
if (a)
|
||||
var b = 1;
|
||||
else
|
||||
var b = 2
|
||||
var b = 2;
|
||||
console.log(b + 1);
|
||||
}
|
||||
}
|
||||
@@ -444,7 +444,7 @@ multi_def: {
|
||||
if (a)
|
||||
var b = 1;
|
||||
else
|
||||
var b = 2
|
||||
var b = 2;
|
||||
console.log(b + 1);
|
||||
}
|
||||
}
|
||||
@@ -479,6 +479,33 @@ multi_def_2: {
|
||||
}
|
||||
}
|
||||
|
||||
multi_def_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = 2;
|
||||
if (a)
|
||||
var b;
|
||||
else
|
||||
var b;
|
||||
console.log(b + 1);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var b = 2;
|
||||
if (a)
|
||||
var b;
|
||||
else
|
||||
var b;
|
||||
console.log(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use_before_var: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
@@ -1571,3 +1598,259 @@ unary_delete: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
redefine_arguments_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}
|
||||
function g() {
|
||||
var arguments = 42;
|
||||
return typeof arguments;
|
||||
}
|
||||
function h(x) {
|
||||
var arguments = x;
|
||||
return typeof arguments;
|
||||
}
|
||||
console.log(f(), g(), h());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}
|
||||
function g() {
|
||||
return"number";
|
||||
}
|
||||
function h(x) {
|
||||
var arguments = x;
|
||||
return typeof arguments;
|
||||
}
|
||||
console.log(f(), g(), h());
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
redefine_arguments_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}
|
||||
function g() {
|
||||
var arguments = 42;
|
||||
return typeof arguments;
|
||||
}
|
||||
function h(x) {
|
||||
var arguments = x;
|
||||
return typeof arguments;
|
||||
}
|
||||
console.log(f(), g(), h());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}(), function() {
|
||||
return"number";
|
||||
}(), function(x) {
|
||||
var arguments = x;
|
||||
return typeof arguments;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
redefine_arguments_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}
|
||||
function g() {
|
||||
var arguments = 42;
|
||||
return typeof arguments;
|
||||
}
|
||||
function h(x) {
|
||||
var arguments = x;
|
||||
return typeof arguments;
|
||||
}
|
||||
console.log(f(), g(), h());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var arguments;
|
||||
return typeof arguments;
|
||||
}(), "number", "undefined");
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
redefine_farg_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}
|
||||
function g(a) {
|
||||
var a = 42;
|
||||
return typeof a;
|
||||
}
|
||||
function h(a, b) {
|
||||
var a = b;
|
||||
return typeof a;
|
||||
}
|
||||
console.log(f([]), g([]), h([]));
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}
|
||||
function g() {
|
||||
return"number";
|
||||
}
|
||||
function h(a, b) {
|
||||
var a = b;
|
||||
return typeof a;
|
||||
}
|
||||
console.log(f([]), g([]), h([]));
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
redefine_farg_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}
|
||||
function g(a) {
|
||||
var a = 42;
|
||||
return typeof a;
|
||||
}
|
||||
function h(a, b) {
|
||||
var a = b;
|
||||
return typeof a;
|
||||
}
|
||||
console.log(f([]), g([]), h([]));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}([]), function() {
|
||||
return "number";
|
||||
}(),function(a, b) {
|
||||
var a = b;
|
||||
return typeof a;
|
||||
}([]));
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
redefine_farg_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
keep_fargs: false,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}
|
||||
function g(a) {
|
||||
var a = 42;
|
||||
return typeof a;
|
||||
}
|
||||
function h(a, b) {
|
||||
var a = b;
|
||||
return typeof a;
|
||||
}
|
||||
console.log(f([]), g([]), h([]));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
var a;
|
||||
return typeof a;
|
||||
}([]), "number", function(a) {
|
||||
var a = void 0;
|
||||
return typeof a;
|
||||
}([]));
|
||||
}
|
||||
expect_stdout: "object number undefined"
|
||||
}
|
||||
|
||||
delay_def: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
return a;
|
||||
var a;
|
||||
}
|
||||
function g() {
|
||||
return a;
|
||||
var a = 1;
|
||||
}
|
||||
console.log(f(), g());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
return a;
|
||||
var a;
|
||||
}
|
||||
function g() {
|
||||
return a;
|
||||
var a = 1;
|
||||
}
|
||||
console.log(f(), g());
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user