enhance conditionals (#5542)
This commit is contained in:
209
lib/ast.js
209
lib/ast.js
@@ -109,6 +109,9 @@ var AST_Node = DEFNODE("Node", "start end", {
|
||||
start: "[AST_Token] The first token of this node",
|
||||
end: "[AST_Token] The last token of this node"
|
||||
},
|
||||
equals: function(node) {
|
||||
return this.TYPE == node.TYPE && this._equals(node);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
visitor.visit(this);
|
||||
},
|
||||
@@ -231,6 +234,24 @@ AST_Node.disable_validation = function() {
|
||||
while (restore = restore_transforms.pop()) restore();
|
||||
};
|
||||
|
||||
function all_equals(k, l) {
|
||||
return k.length == l.length && all(k, function(m, i) {
|
||||
return m.equals(l[i]);
|
||||
});
|
||||
}
|
||||
|
||||
function list_equals(s, t) {
|
||||
return s.length == t.length && all(s, function(u, i) {
|
||||
return u == t[i];
|
||||
});
|
||||
}
|
||||
|
||||
function prop_equals(u, v) {
|
||||
if (u === v) return true;
|
||||
if (u == null) return v == null;
|
||||
return u instanceof AST_Node && v instanceof AST_Node && u.equals(v);
|
||||
}
|
||||
|
||||
/* -----[ statements ]----- */
|
||||
|
||||
var AST_Statement = DEFNODE("Statement", null, {
|
||||
@@ -242,6 +263,7 @@ var AST_Statement = DEFNODE("Statement", null, {
|
||||
|
||||
var AST_Debugger = DEFNODE("Debugger", null, {
|
||||
$documentation: "Represents a debugger statement",
|
||||
_equals: return_true,
|
||||
}, AST_Statement);
|
||||
|
||||
var AST_Directive = DEFNODE("Directive", "quote value", {
|
||||
@@ -250,6 +272,9 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
|
||||
quote: "[string?] the original quote character",
|
||||
value: "[string] The value of this directive as a plain string (it's not an AST_String!)",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.value == node.value;
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.quote != null) {
|
||||
if (typeof this.quote != "string") throw new Error("quote must be string");
|
||||
@@ -260,7 +285,8 @@ var AST_Directive = DEFNODE("Directive", "quote value", {
|
||||
}, AST_Statement);
|
||||
|
||||
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||
$documentation: "The empty statement (empty block or simply a semicolon)"
|
||||
$documentation: "The empty statement (empty block or simply a semicolon)",
|
||||
_equals: return_true,
|
||||
}, AST_Statement);
|
||||
|
||||
function is_statement(node) {
|
||||
@@ -291,6 +317,9 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
||||
$propdoc: {
|
||||
body: "[AST_Node] an expression node (should not be instanceof AST_Statement)",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -342,6 +371,9 @@ var AST_Block = DEFNODE("Block", "body", {
|
||||
$propdoc: {
|
||||
body: "[AST_Statement*] an array of statements"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.body, node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -376,6 +408,10 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
||||
$propdoc: {
|
||||
label: "[AST_Label] a label definition"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.label.equals(node.label)
|
||||
&& this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -417,6 +453,10 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", {
|
||||
$propdoc: {
|
||||
condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.body.equals(node.body)
|
||||
&& this.condition.equals(node.condition);
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop");
|
||||
must_be_expression(this, "condition");
|
||||
@@ -431,7 +471,7 @@ var AST_Do = DEFNODE("Do", null, {
|
||||
node.body.walk(visitor);
|
||||
node.condition.walk(visitor);
|
||||
});
|
||||
}
|
||||
},
|
||||
}, AST_DWLoop);
|
||||
|
||||
var AST_While = DEFNODE("While", null, {
|
||||
@@ -442,7 +482,7 @@ var AST_While = DEFNODE("While", null, {
|
||||
node.condition.walk(visitor);
|
||||
node.body.walk(visitor);
|
||||
});
|
||||
}
|
||||
},
|
||||
}, AST_DWLoop);
|
||||
|
||||
var AST_For = DEFNODE("For", "init condition step", {
|
||||
@@ -452,6 +492,12 @@ var AST_For = DEFNODE("For", "init condition step", {
|
||||
condition: "[AST_Node?] the `for` termination clause, or null if empty",
|
||||
step: "[AST_Node?] the `for` update clause, or null if empty"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.init, node.init)
|
||||
&& prop_equals(this.condition, node.condition)
|
||||
&& prop_equals(this.step, node.step)
|
||||
&& this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -479,6 +525,11 @@ var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", {
|
||||
init: "[AST_Node] the assignment target during iteration",
|
||||
object: "[AST_Node] the object to iterate over"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.init.equals(node.init)
|
||||
&& this.object.equals(node.object)
|
||||
&& this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -519,6 +570,10 @@ var AST_With = DEFNODE("With", "expression", {
|
||||
$propdoc: {
|
||||
expression: "[AST_Node] the `with` expression"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.expression.equals(node.expression)
|
||||
&& this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -621,6 +676,13 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_argu
|
||||
});
|
||||
if (this.rest) this.rest.walk(tw);
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.rest, node.rest)
|
||||
&& prop_equals(this.name, node.name)
|
||||
&& prop_equals(this.value, node.value)
|
||||
&& all_equals(this.argnames, node.argnames)
|
||||
&& all_equals(this.body, node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -831,6 +893,11 @@ var AST_Class = DEFNODE("Class", "extends name properties", {
|
||||
extends: "[AST_Node?] the super class, or null if not specified",
|
||||
properties: "[AST_ClassProperty*] array of class properties",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.name, node.name)
|
||||
&& prop_equals(this.extends, node.extends)
|
||||
&& all_equals(this.properties, node.properties);
|
||||
},
|
||||
resolve: function(def_class) {
|
||||
return def_class ? this : this.parent_scope.resolve();
|
||||
},
|
||||
@@ -883,6 +950,12 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
|
||||
static: "[boolean] whether this is a static property",
|
||||
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return !this.private == !node.private
|
||||
&& !this.static == !node.static
|
||||
&& prop_equals(this.key, node.key)
|
||||
&& prop_equals(this.value, node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -959,6 +1032,9 @@ var AST_Exit = DEFNODE("Exit", "value", {
|
||||
$propdoc: {
|
||||
value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.value, node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -989,6 +1065,9 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", {
|
||||
$propdoc: {
|
||||
label: "[AST_LabelRef?] the label, or null if none",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.label, node.label);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1019,6 +1098,11 @@ var AST_If = DEFNODE("If", "condition alternative", {
|
||||
condition: "[AST_Node] the `if` condition",
|
||||
alternative: "[AST_Statement?] the `else` part, or null if not present"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.body.equals(node.body)
|
||||
&& this.condition.equals(node.condition)
|
||||
&& prop_equals(this.alternative, node.alternative);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1116,6 +1200,10 @@ var AST_Catch = DEFNODE("Catch", "argname", {
|
||||
$propdoc: {
|
||||
argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.argname, node.argname)
|
||||
&& all_equals(this.body, node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1141,6 +1229,9 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
|
||||
$propdoc: {
|
||||
definitions: "[AST_VarDef*] array of variable definitions"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.definitions, node.definitions);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1197,6 +1288,10 @@ var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||
name: "[AST_Destructured|AST_SymbolVar] name of the variable",
|
||||
value: "[AST_Node?] initializer, or null of there's no initializer",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.name.equals(node.name)
|
||||
&& prop_equals(this.value, node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1216,6 +1311,9 @@ var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", {
|
||||
$propdoc: {
|
||||
body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1236,6 +1334,9 @@ var AST_ExportDefault = DEFNODE("ExportDefault", "body", {
|
||||
$propdoc: {
|
||||
body: "[AST_Node] the default export",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.body.equals(node.body);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1257,6 +1358,11 @@ var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", {
|
||||
path: "[string] the path to import module",
|
||||
quote: "[string?] the original quote character",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.path == node.path
|
||||
&& list_equals(this.aliases, node.aliases)
|
||||
&& list_equals(this.keys, node.keys);
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.aliases.length != this.keys.length) {
|
||||
throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length);
|
||||
@@ -1280,6 +1386,9 @@ var AST_ExportReferences = DEFNODE("ExportReferences", "properties", {
|
||||
$propdoc: {
|
||||
properties: "[AST_SymbolExport*] array of aliases to export",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.properties, node.properties);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1304,6 +1413,11 @@ var AST_Import = DEFNODE("Import", "all default path properties quote", {
|
||||
properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified",
|
||||
quote: "[string?] the original quote character",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.path == node.path
|
||||
&& prop_equals(this.default, node.default)
|
||||
&& (this.all ? prop_equals(this.all, node.all) : all_equals(this.properties, node.properties));
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1340,6 +1454,10 @@ var AST_DefaultValue = DEFNODE("DefaultValue", "name value", {
|
||||
name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable",
|
||||
value: "[AST_Node] value to assign if variable is `undefined`",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.name.equals(node.name)
|
||||
&& this.value.equals(node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1367,6 +1485,11 @@ var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
|
||||
pure: "[boolean/S] marker for side-effect-free call expression",
|
||||
terminal: "[boolean] whether the chain has ended",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return !this.optional == !node.optional
|
||||
&& this.expression.equals(node.expression)
|
||||
&& all_equals(this.args, node.args);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1395,6 +1518,9 @@ var AST_Sequence = DEFNODE("Sequence", "expressions", {
|
||||
$propdoc: {
|
||||
expressions: "[AST_Node*] array of expressions (at least two)"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.expressions, node.expressions);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1422,6 +1548,11 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression optional property termina
|
||||
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node",
|
||||
terminal: "[boolean] whether the chain has ended",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return !this.optional == !node.optional
|
||||
&& prop_equals(this.property, node.property)
|
||||
&& this.expression.equals(node.expression);
|
||||
},
|
||||
get_property: function() {
|
||||
var p = this.property;
|
||||
if (p instanceof AST_Constant) return p.value;
|
||||
@@ -1466,6 +1597,9 @@ var AST_Spread = DEFNODE("Spread", "expression", {
|
||||
$propdoc: {
|
||||
expression: "[AST_Node] expression to be expanded",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.expression.equals(node.expression);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1483,6 +1617,10 @@ var AST_Unary = DEFNODE("Unary", "operator expression", {
|
||||
operator: "[string] the operator",
|
||||
expression: "[AST_Node] expression that this unary operator applies to"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.operator == node.operator
|
||||
&& this.expression.equals(node.expression);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1511,6 +1649,11 @@ var AST_Binary = DEFNODE("Binary", "operator left right", {
|
||||
operator: "[string] the operator",
|
||||
right: "[AST_Node] right-hand side expression"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.operator == node.operator
|
||||
&& this.left.equals(node.left)
|
||||
&& this.right.equals(node.right);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1532,6 +1675,11 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
|
||||
consequent: "[AST_Node]",
|
||||
alternative: "[AST_Node]"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.condition.equals(node.condition)
|
||||
&& this.consequent.equals(node.consequent)
|
||||
&& this.alternative.equals(node.alternative);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1573,6 +1721,9 @@ var AST_Await = DEFNODE("Await", "expression", {
|
||||
$propdoc: {
|
||||
expression: "[AST_Node] expression with Promise to resolve on",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.expression.equals(node.expression);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1590,6 +1741,10 @@ var AST_Yield = DEFNODE("Yield", "expression nested", {
|
||||
expression: "[AST_Node?] return value for iterator, or null if undefined",
|
||||
nested: "[boolean] whether to iterate over expression as generator",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return !this.nested == !node.nested
|
||||
&& prop_equals(this.expression, node.expression);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1612,6 +1767,9 @@ var AST_Array = DEFNODE("Array", "elements", {
|
||||
$propdoc: {
|
||||
elements: "[AST_Node*] array of elements"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.elements, node.elements);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1654,6 +1812,10 @@ var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
|
||||
$propdoc: {
|
||||
elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.rest, node.rest)
|
||||
&& all_equals(this.elements, node.elements);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1671,6 +1833,10 @@ var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
|
||||
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||
value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.key, node.key)
|
||||
&& this.value.equals(node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1692,6 +1858,10 @@ var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
|
||||
$propdoc: {
|
||||
properties: "[AST_DestructuredKeyVal*] array of properties",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.rest, node.rest)
|
||||
&& all_equals(this.properties, node.properties);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1713,6 +1883,9 @@ var AST_Object = DEFNODE("Object", "properties", {
|
||||
$propdoc: {
|
||||
properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return all_equals(this.properties, node.properties);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1736,6 +1909,10 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.key, node.key)
|
||||
&& this.value.equals(node.value);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
@@ -1790,6 +1967,9 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
|
||||
thedef: "[SymbolDef/S] the definition of this symbol"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.thedef ? this.thedef === node.thedef : this.name == node.name;
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol");
|
||||
if (typeof this.name != "string") throw new Error("name must be string");
|
||||
@@ -1809,6 +1989,10 @@ var AST_SymbolImport = DEFNODE("SymbolImport", "key", {
|
||||
$propdoc: {
|
||||
key: "[string] the original `export` name",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.name == node.name
|
||||
&& this.key == node.key;
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.key != "string") throw new Error("key must be string");
|
||||
},
|
||||
@@ -1866,6 +2050,10 @@ var AST_SymbolExport = DEFNODE("SymbolExport", "alias", {
|
||||
$propdoc: {
|
||||
alias: "[string] the `export` alias",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return this.name == node.name
|
||||
&& this.alias == node.alias;
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.alias != "string") throw new Error("alias must be string");
|
||||
},
|
||||
@@ -1877,6 +2065,7 @@ var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||
|
||||
var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, {
|
||||
$documentation: "Base class for `super` & `this`",
|
||||
_equals: return_true,
|
||||
_validate: function() {
|
||||
if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity");
|
||||
},
|
||||
@@ -1911,7 +2100,12 @@ var AST_Template = DEFNODE("Template", "expressions strings tag", {
|
||||
$propdoc: {
|
||||
expressions: "[AST_Node*] the placeholder expressions",
|
||||
strings: "[string*] the raw text segments",
|
||||
tag: "[AST_Node] tag function, or null if absent",
|
||||
tag: "[AST_Node?] tag function, or null if absent",
|
||||
},
|
||||
_equals: function(node) {
|
||||
return prop_equals(this.tag, node.tag)
|
||||
&& list_equals(this.strings, node.strings)
|
||||
&& all_equals(this.expressions, node.expressions);
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
@@ -1936,6 +2130,9 @@ var AST_Template = DEFNODE("Template", "expressions strings tag", {
|
||||
|
||||
var AST_Constant = DEFNODE("Constant", null, {
|
||||
$documentation: "Base class for all constants",
|
||||
_equals: function(node) {
|
||||
return this.value === node.value;
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant");
|
||||
},
|
||||
@@ -1984,6 +2181,9 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
|
||||
$propdoc: {
|
||||
value: "[RegExp] the actual regexp"
|
||||
},
|
||||
_equals: function(node) {
|
||||
return "" + this.value == "" + node.value;
|
||||
},
|
||||
_validate: function() {
|
||||
if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp");
|
||||
},
|
||||
@@ -1991,6 +2191,7 @@ var AST_RegExp = DEFNODE("RegExp", "value", {
|
||||
|
||||
var AST_Atom = DEFNODE("Atom", null, {
|
||||
$documentation: "Base class for atoms",
|
||||
_equals: return_true,
|
||||
_validate: function() {
|
||||
if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom");
|
||||
},
|
||||
|
||||
130
lib/compress.js
130
lib/compress.js
@@ -270,10 +270,6 @@ Compressor.prototype.compress = function(node) {
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("equivalent_to", function(node) {
|
||||
return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
|
||||
if (!compressor.option("hoist_exports")) return;
|
||||
var body = this.body, props = [];
|
||||
@@ -930,7 +926,7 @@ Compressor.prototype.compress = function(node) {
|
||||
var scan = ld || left instanceof AST_Destructured;
|
||||
switch (node.operator) {
|
||||
case "=":
|
||||
if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
|
||||
if (left.equals(right) && !left.has_side_effects(compressor)) {
|
||||
right.walk(tw);
|
||||
walk_prop(left);
|
||||
node.redundant = true;
|
||||
@@ -2031,7 +2027,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// Cascade compound assignments
|
||||
if (compound && scan_lhs && can_replace && !stop_if_hit
|
||||
&& node instanceof AST_Assign && node.operator != "=" && node.left.equivalent_to(lhs)) {
|
||||
&& node instanceof AST_Assign && node.operator != "=" && node.left.equals(lhs)) {
|
||||
replaced++;
|
||||
changed = true;
|
||||
AST_Node.info("Cascading {node} [{file}:{line},{col}]", {
|
||||
@@ -2075,7 +2071,7 @@ Compressor.prototype.compress = function(node) {
|
||||
// Replace variable with assignment when found
|
||||
var hit_rhs;
|
||||
if (!(node instanceof AST_SymbolDeclaration)
|
||||
&& (scan_lhs && lhs.equivalent_to(node)
|
||||
&& (scan_lhs && lhs.equals(node)
|
||||
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
|
||||
if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
|
||||
if (!hit_rhs && !value_def) abort = true;
|
||||
@@ -2420,11 +2416,11 @@ Compressor.prototype.compress = function(node) {
|
||||
if (node !== parent.init) return true;
|
||||
}
|
||||
if (node instanceof AST_Assign) {
|
||||
return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
return node.operator != "=" && lhs.equals(node.left);
|
||||
}
|
||||
if (node instanceof AST_Call) {
|
||||
if (!(lhs instanceof AST_PropAccess)) return false;
|
||||
if (!lhs.equivalent_to(node.expression)) return false;
|
||||
if (!lhs.equals(node.expression)) return false;
|
||||
return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
|
||||
}
|
||||
if (node instanceof AST_Class) return !compressor.has_directive("use strict");
|
||||
@@ -3102,7 +3098,7 @@ Compressor.prototype.compress = function(node) {
|
||||
return !circular && rhs_exact_match;
|
||||
|
||||
function rhs_exact_match(node) {
|
||||
return expr.equivalent_to(node);
|
||||
return expr.equals(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3400,7 +3396,7 @@ Compressor.prototype.compress = function(node) {
|
||||
var changed = false;
|
||||
var parent = compressor.parent();
|
||||
var self = compressor.self();
|
||||
var exit, exit_defs, merge_exit;
|
||||
var exit, merge_exit;
|
||||
var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
|
||||
var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
|
||||
var multiple_if_returns = has_multiple_if_returns(statements);
|
||||
@@ -3548,7 +3544,6 @@ Compressor.prototype.compress = function(node) {
|
||||
|
||||
if (stat instanceof AST_Exit) {
|
||||
exit = stat;
|
||||
exit_defs = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3578,30 +3573,15 @@ Compressor.prototype.compress = function(node) {
|
||||
if (exit.TYPE != ab.TYPE) return false;
|
||||
var value = ab.value;
|
||||
if (!value) return false;
|
||||
var equals = exit.equivalent_to(ab);
|
||||
var equals = exit.equals(ab);
|
||||
if (!equals && value instanceof AST_Sequence) {
|
||||
value = value.tail_node();
|
||||
if (exit.value && exit.value.equivalent_to(value)) equals = 2;
|
||||
if (exit.value && exit.value.equals(value)) equals = 2;
|
||||
}
|
||||
if (!equals && !exact && exit.value instanceof AST_Sequence) {
|
||||
if (exit.value.tail_node().equivalent_to(value)) equals = 3;
|
||||
if (exit.value.tail_node().equals(value)) equals = 3;
|
||||
}
|
||||
if (!equals) return false;
|
||||
if (exit_defs == null) {
|
||||
exit_defs = new Dictionary();
|
||||
exit.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_SymbolRef) exit_defs.set(node.name, node.definition());
|
||||
}));
|
||||
if (!exit_defs.size()) exit_defs = false;
|
||||
}
|
||||
var abort = false;
|
||||
if (exit_defs) value.walk(new TreeWalker(function(node) {
|
||||
if (abort) return true;
|
||||
if (node instanceof AST_SymbolRef && exit_defs.get(node.name) !== node.definition()) {
|
||||
return abort = true;
|
||||
}
|
||||
}));
|
||||
return !abort && equals;
|
||||
return equals;
|
||||
}
|
||||
|
||||
function can_drop_abort(ab) {
|
||||
@@ -4635,7 +4615,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (keep_unary
|
||||
&& fixed instanceof AST_UnaryPrefix
|
||||
&& fixed.operator == "+"
|
||||
&& fixed.expression.equivalent_to(this)) {
|
||||
&& fixed.expression.equals(this)) {
|
||||
return false;
|
||||
}
|
||||
this.is_number = return_false;
|
||||
@@ -9456,10 +9436,10 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
|
||||
function match(cond) {
|
||||
if (node.equivalent_to(cond)) return true;
|
||||
if (node.equals(cond)) return true;
|
||||
if (!(cond instanceof AST_UnaryPrefix)) return false;
|
||||
if (cond.operator != "!") return false;
|
||||
if (!node.equivalent_to(cond.expression)) return false;
|
||||
if (!node.equals(cond.expression)) return false;
|
||||
negated = true;
|
||||
return true;
|
||||
}
|
||||
@@ -9633,9 +9613,43 @@ Compressor.prototype.compress = function(node) {
|
||||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor);
|
||||
}
|
||||
if (self.alternative) {
|
||||
var body_stats = as_array(self.body);
|
||||
var body_index = last_index(body_stats);
|
||||
var alt_stats = as_array(self.alternative);
|
||||
var alt_index = last_index(alt_stats);
|
||||
for (var stats = []; body_index >= 0 && alt_index >= 0;) {
|
||||
var stat = body_stats[body_index];
|
||||
if (!stat.equals(alt_stats[alt_index])) break;
|
||||
body_stats.splice(body_index--, 1);
|
||||
alt_stats.splice(alt_index--, 1);
|
||||
stats.unshift(stat);
|
||||
}
|
||||
if (stats.length > 0) {
|
||||
self.body = body_stats.length > 0 ? make_node(AST_BlockStatement, self, {
|
||||
body: body_stats,
|
||||
}) : make_node(AST_EmptyStatement, self);
|
||||
self.alternative = alt_stats.length > 0 ? make_node(AST_BlockStatement, self, {
|
||||
body: alt_stats,
|
||||
}) : null;
|
||||
stats.unshift(self);
|
||||
return make_node(AST_BlockStatement, self, { body: stats }).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
|
||||
return self;
|
||||
|
||||
function as_array(node) {
|
||||
return node instanceof AST_BlockStatement ? node.body : [ node ];
|
||||
}
|
||||
|
||||
function last_index(stats) {
|
||||
for (var index = stats.length; --index >= 0;) {
|
||||
if (!is_declaration(stats[index], true)) break;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
function sequencesize(stat, defuns, var_defs, refs) {
|
||||
if (stat == null) return [];
|
||||
if (stat instanceof AST_BlockStatement) {
|
||||
@@ -9753,7 +9767,7 @@ Compressor.prototype.compress = function(node) {
|
||||
case 0:
|
||||
var prev_block = make_node(AST_BlockStatement, prev, prev);
|
||||
var next_block = make_node(AST_BlockStatement, branch, { body: statements });
|
||||
if (prev_block.equivalent_to(next_block)) prev.body = [];
|
||||
if (prev_block.equals(next_block)) prev.body = [];
|
||||
}
|
||||
}
|
||||
if (side_effects.length) {
|
||||
@@ -11457,7 +11471,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (self.left instanceof AST_SymbolRef
|
||||
&& assign instanceof AST_Assign
|
||||
&& assign.operator == "="
|
||||
&& self.left.equivalent_to(assign.left)) {
|
||||
&& self.left.equals(assign.left)) {
|
||||
return make_node(AST_Assign, self, {
|
||||
operator: "=",
|
||||
left: assign.left,
|
||||
@@ -11483,7 +11497,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
|
||||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
|
||||
(self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
|
||||
repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
|
||||
repeatable(compressor, self.left) && self.left.equals(self.right)) {
|
||||
self.operator = self.operator.slice(0, 2);
|
||||
}
|
||||
// XXX: intentionally falling down to the next case
|
||||
@@ -11530,7 +11544,7 @@ Compressor.prototype.compress = function(node) {
|
||||
&& (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
|
||||
|| lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
|
||||
&& !expr.has_side_effects(compressor)
|
||||
&& expr.equivalent_to(self.right.right)) {
|
||||
&& expr.equals(self.right.right)) {
|
||||
lhs.operator = lhs.operator.slice(0, -1);
|
||||
lhs.left = make_node(AST_Null, self);
|
||||
return self.left;
|
||||
@@ -11542,7 +11556,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (compressor.option("booleans")) {
|
||||
var lhs = self.left;
|
||||
if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
|
||||
if (lhs.equivalent_to(self.right)) {
|
||||
if (lhs.equals(self.right)) {
|
||||
return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
|
||||
}
|
||||
mark_duplicate_condition(compressor, lhs);
|
||||
@@ -12494,7 +12508,7 @@ Compressor.prototype.compress = function(node) {
|
||||
exprs.push(self.right);
|
||||
return make_sequence(self, exprs).optimize(compressor);
|
||||
}
|
||||
if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
|
||||
if (self.left.equals(self.right) && !self.left.has_side_effects(compressor)) {
|
||||
return self.right;
|
||||
}
|
||||
var exp = self.left.expression;
|
||||
@@ -12510,7 +12524,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
} else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
|
||||
var parent;
|
||||
if (self.operator == "=" && self.left.equivalent_to(self.right)
|
||||
if (self.operator == "=" && self.left.equals(self.right)
|
||||
&& !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
|
||||
return self.right;
|
||||
}
|
||||
@@ -12697,13 +12711,13 @@ Compressor.prototype.compress = function(node) {
|
||||
var alternative = self.alternative;
|
||||
if (repeatable(compressor, condition)) {
|
||||
// x ? x : y ---> x || y
|
||||
if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
|
||||
if (condition.equals(consequent)) return make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: condition,
|
||||
right: alternative,
|
||||
}).optimize(compressor);
|
||||
// x ? y : x ---> x && y
|
||||
if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
|
||||
if (condition.equals(alternative)) return make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: condition,
|
||||
right: consequent,
|
||||
@@ -12720,7 +12734,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if ((is_eq || consequent === seq_tail)
|
||||
&& alt_tail instanceof AST_Assign
|
||||
&& seq_tail.operator == alt_tail.operator
|
||||
&& seq_tail.left.equivalent_to(alt_tail.left)
|
||||
&& seq_tail.left.equals(alt_tail.left)
|
||||
&& (is_eq && seq_tail.left instanceof AST_SymbolRef
|
||||
|| !condition.has_side_effects(compressor)
|
||||
&& can_shift_lhs_of_tail(consequent)
|
||||
@@ -12737,7 +12751,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
}
|
||||
// x ? y : y ---> x, y
|
||||
if (consequent.equivalent_to(alternative)) return make_sequence(self, [
|
||||
if (consequent.equals(alternative)) return make_sequence(self, [
|
||||
condition,
|
||||
consequent
|
||||
]).optimize(compressor);
|
||||
@@ -12751,7 +12765,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (consequent instanceof AST_Call
|
||||
&& alternative.TYPE == consequent.TYPE
|
||||
&& (arg_index = arg_diff(consequent, alternative)) >= 0
|
||||
&& consequent.expression.equivalent_to(alternative.expression)
|
||||
&& consequent.expression.equals(alternative.expression)
|
||||
&& !condition.has_side_effects(compressor)
|
||||
&& !consequent.expression.has_side_effects(compressor)) {
|
||||
var node = consequent.clone();
|
||||
@@ -12771,7 +12785,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// x ? (y ? a : b) : b ---> x && y ? a : b
|
||||
if (consequent instanceof AST_Conditional
|
||||
&& consequent.alternative.equivalent_to(alternative)) {
|
||||
&& consequent.alternative.equals(alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: condition,
|
||||
@@ -12784,7 +12798,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// x ? (y ? a : b) : a ---> !x || y ? a : b
|
||||
if (consequent instanceof AST_Conditional
|
||||
&& consequent.consequent.equivalent_to(alternative)) {
|
||||
&& consequent.consequent.equals(alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: negated,
|
||||
@@ -12797,7 +12811,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// x ? a : (y ? a : b) ---> x || y ? a : b
|
||||
if (alternative instanceof AST_Conditional
|
||||
&& consequent.equivalent_to(alternative.consequent)) {
|
||||
&& consequent.equals(alternative.consequent)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: condition,
|
||||
@@ -12810,7 +12824,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// x ? b : (y ? a : b) ---> !x && y ? a : b
|
||||
if (alternative instanceof AST_Conditional
|
||||
&& consequent.equivalent_to(alternative.alternative)) {
|
||||
&& consequent.equals(alternative.alternative)) {
|
||||
return make_node(AST_Conditional, self, {
|
||||
condition: make_node(AST_Binary, self, {
|
||||
left: negated,
|
||||
@@ -12823,7 +12837,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
// x ? (a, c) : (b, c) ---> x ? a : b, c
|
||||
if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
|
||||
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
|
||||
&& consequent.tail_node().equals(alternative.tail_node())) {
|
||||
return make_sequence(self, [
|
||||
make_node(AST_Conditional, self, {
|
||||
condition: condition,
|
||||
@@ -12836,7 +12850,7 @@ Compressor.prototype.compress = function(node) {
|
||||
// x ? y && a : a ---> (!x || y) && a
|
||||
if (consequent instanceof AST_Binary
|
||||
&& consequent.operator == "&&"
|
||||
&& consequent.right.equivalent_to(alternative)) {
|
||||
&& consequent.right.equals(alternative)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: make_node(AST_Binary, self, {
|
||||
@@ -12850,7 +12864,7 @@ Compressor.prototype.compress = function(node) {
|
||||
// x ? y || a : a ---> x && y || a
|
||||
if (consequent instanceof AST_Binary
|
||||
&& consequent.operator == "||"
|
||||
&& consequent.right.equivalent_to(alternative)) {
|
||||
&& consequent.right.equals(alternative)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: make_node(AST_Binary, self, {
|
||||
@@ -12864,7 +12878,7 @@ Compressor.prototype.compress = function(node) {
|
||||
// x ? a : y && a ---> (x || y) && a
|
||||
if (alternative instanceof AST_Binary
|
||||
&& alternative.operator == "&&"
|
||||
&& alternative.right.equivalent_to(consequent)) {
|
||||
&& alternative.right.equals(consequent)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "&&",
|
||||
left: make_node(AST_Binary, self, {
|
||||
@@ -12878,7 +12892,7 @@ Compressor.prototype.compress = function(node) {
|
||||
// x ? a : y || a ---> !x && y || a
|
||||
if (alternative instanceof AST_Binary
|
||||
&& alternative.operator == "||"
|
||||
&& alternative.right.equivalent_to(consequent)) {
|
||||
&& alternative.right.equals(consequent)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "||",
|
||||
left: make_node(AST_Binary, self, {
|
||||
@@ -12974,10 +12988,10 @@ Compressor.prototype.compress = function(node) {
|
||||
var len = a.length;
|
||||
if (len != b.length) return -2;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (!a[i].equivalent_to(b[i])) {
|
||||
if (!a[i].equals(b[i])) {
|
||||
if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
|
||||
for (var j = i + 1; j < len; j++) {
|
||||
if (!a[j].equivalent_to(b[j])) return -2;
|
||||
if (!a[j].equals(b[j])) return -2;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
@@ -12998,7 +13012,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (!(consequent instanceof AST_PropAccess)) return;
|
||||
var p = consequent.property;
|
||||
var q = alternative.property;
|
||||
return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
|
||||
return (p instanceof AST_Node ? p.equals(q) : p == q)
|
||||
&& !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,88 @@ ifs_7: {
|
||||
}
|
||||
}
|
||||
|
||||
merge_tail_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a)
|
||||
while (console.log("bar"));
|
||||
else
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"baz",
|
||||
"foo",
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
}
|
||||
|
||||
merge_tail_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
c = "baz";
|
||||
while (console.log(c));
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
var c;
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (!a) {
|
||||
c = "baz";
|
||||
while (console.log(c));
|
||||
var c;
|
||||
}
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"baz",
|
||||
"bar",
|
||||
"foo",
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
}
|
||||
|
||||
cond_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
|
||||
@@ -142,6 +142,80 @@ if_dead_branch: {
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
retain_tail_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
const b = "bar";
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
} else {
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
const b = "bar";
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
} else {
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
retain_tail_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
const b = "baz";
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
const b = "baz";
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
merge_vars_1: {
|
||||
options = {
|
||||
merge_vars: true,
|
||||
@@ -579,6 +653,37 @@ dead_block_after_return: {
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
if_return_3: {
|
||||
options = {
|
||||
if_return: true,
|
||||
}
|
||||
input: {
|
||||
var a = "PASS";
|
||||
function f(b) {
|
||||
if (console) {
|
||||
const b = a;
|
||||
return b;
|
||||
} else
|
||||
while (console.log("FAIL 1"));
|
||||
return b;
|
||||
}
|
||||
console.log(f("FAIL 2"));
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS";
|
||||
function f(b) {
|
||||
if (console) {
|
||||
const b = a;
|
||||
return b;
|
||||
} else
|
||||
while (console.log("FAIL 1"));
|
||||
return b;
|
||||
}
|
||||
console.log(f("FAIL 2"));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
do_if_continue_1: {
|
||||
options = {
|
||||
if_return: true,
|
||||
|
||||
@@ -190,6 +190,96 @@ if_dead_branch: {
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
retain_tail_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
let b = "bar";
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
} else {
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
let b = "bar";
|
||||
while (console.log("baz"));
|
||||
console.log(b);
|
||||
} else {
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"moo",
|
||||
"foo",
|
||||
"baz",
|
||||
"bar",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
retain_tail_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
let b = "baz";
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
function f(a) {
|
||||
var b = "foo";
|
||||
if (a) {
|
||||
while (console.log("bar"));
|
||||
console.log(b);
|
||||
} else {
|
||||
let b = "baz";
|
||||
while (console.log("moo"));
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
f();
|
||||
f(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"moo",
|
||||
"baz",
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
merge_vars_1: {
|
||||
options = {
|
||||
merge_vars: true,
|
||||
|
||||
Reference in New Issue
Block a user