Merge 774dc23acb into 7eb52d2837
This commit is contained in:
commit
e2dddd3103
356
lib/ast.js
356
lib/ast.js
|
|
@ -143,7 +143,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
function walk_body(node, visitor) {
|
function walk_body(node, visitor) {
|
||||||
if (node.body instanceof AST_Statement) {
|
if (node.body instanceof AST_Node) {
|
||||||
node.body._walk(visitor);
|
node.body._walk(visitor);
|
||||||
}
|
}
|
||||||
else node.body.forEach(function(stat){
|
else node.body.forEach(function(stat){
|
||||||
|
|
@ -263,6 +263,10 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
||||||
}
|
}
|
||||||
}, AST_IterationStatement);
|
}, AST_IterationStatement);
|
||||||
|
|
||||||
|
var AST_ForOf = DEFNODE("ForOf", null, {
|
||||||
|
$documentation: "A `for ... of` statement",
|
||||||
|
}, AST_ForIn);
|
||||||
|
|
||||||
var AST_With = DEFNODE("With", "expression", {
|
var AST_With = DEFNODE("With", "expression", {
|
||||||
$documentation: "A `with` statement",
|
$documentation: "A `with` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
|
@ -278,9 +282,10 @@ var AST_With = DEFNODE("With", "expression", {
|
||||||
|
|
||||||
/* -----[ scope and functions ]----- */
|
/* -----[ scope and functions ]----- */
|
||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
var AST_Scope = DEFNODE("Scope", "is_block_scope directives variables functions uses_with uses_eval parent_scope enclosed cname", {
|
||||||
$documentation: "Base class for all statements introducing a lexical scope",
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
is_block_scope: "[boolean] identifies a block scope",
|
||||||
directives: "[string*/S] an array of directives declared in this scope",
|
directives: "[string*/S] an array of directives declared in this scope",
|
||||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
|
|
@ -290,6 +295,13 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
|
||||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||||
},
|
},
|
||||||
|
get_defun_scope: function () {
|
||||||
|
var self = this;
|
||||||
|
while (self.is_block_scope && self.parent_scope) {
|
||||||
|
self = self.parent_scope;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
|
|
@ -359,13 +371,99 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
var AST_Expansion = DEFNODE("Expansion", "expression", {
|
||||||
|
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "AST_Symbol the thing to be expanded"
|
||||||
|
},
|
||||||
|
_walk: function(visitor) {
|
||||||
|
var self = this;
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
self.expression.walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
|
||||||
|
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
|
||||||
|
$propdoc: {
|
||||||
|
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
|
||||||
|
},
|
||||||
|
as_params: function (croak) {
|
||||||
|
// We don't want anything which doesn't belong in a destructuring
|
||||||
|
var root = this;
|
||||||
|
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
|
||||||
|
if (ex instanceof AST_Object) {
|
||||||
|
if (ex.properties.length == 0)
|
||||||
|
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||||
|
return new AST_Destructuring({
|
||||||
|
start: ex.start,
|
||||||
|
end: ex.end,
|
||||||
|
is_array: false,
|
||||||
|
default: default_seen_above,
|
||||||
|
names: ex.properties.map(to_fun_args)
|
||||||
|
});
|
||||||
|
} else if (ex instanceof AST_ObjectKeyVal && ex.shorthand) {
|
||||||
|
return new AST_SymbolFunarg({
|
||||||
|
name: ex.key,
|
||||||
|
start: ex.start,
|
||||||
|
end: ex.end
|
||||||
|
});
|
||||||
|
} else if (ex instanceof AST_Destructuring) {
|
||||||
|
if (ex.names.length == 0)
|
||||||
|
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||||
|
ex.names = ex.names.map(to_fun_args);
|
||||||
|
return ex;
|
||||||
|
} else if (ex instanceof AST_SymbolRef) {
|
||||||
|
return new AST_SymbolFunarg({
|
||||||
|
name: ex.name,
|
||||||
|
default: default_seen_above,
|
||||||
|
start: ex.start,
|
||||||
|
end: ex.end
|
||||||
|
});
|
||||||
|
} else if (ex instanceof AST_Expansion) {
|
||||||
|
return ex;
|
||||||
|
} else if (ex instanceof AST_Array) {
|
||||||
|
if (ex.elements.length === 0)
|
||||||
|
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
|
||||||
|
return new AST_Destructuring({
|
||||||
|
start: ex.start,
|
||||||
|
end: ex.end,
|
||||||
|
is_array: true,
|
||||||
|
default: default_seen_above,
|
||||||
|
names: ex.elements.map(to_fun_args)
|
||||||
|
});
|
||||||
|
} else if (ex instanceof AST_Assign) {
|
||||||
|
return to_fun_args(ex.left, undefined, undefined, ex.right);
|
||||||
|
} else {
|
||||||
|
croak("Invalid function parameter", ex.start.line, ex.start.col);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
as_expr: function (croak) {
|
||||||
|
return AST_Seq.from_array(this.expressions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
|
||||||
$documentation: "Base class for functions",
|
$documentation: "Base class for functions",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
is_generator: "is generatorFn or not",
|
||||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||||
argnames: "[AST_SymbolFunarg*] array of function arguments",
|
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments",
|
||||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
||||||
},
|
},
|
||||||
|
args_as_names: function () {
|
||||||
|
var out = [];
|
||||||
|
for (var i = 0; i < this.argnames.length; i++) {
|
||||||
|
if (this.argnames[i] instanceof AST_Destructuring) {
|
||||||
|
out = out.concat(this.argnames[i].all_symbols());
|
||||||
|
} else {
|
||||||
|
out.push(this.argnames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
if (this.name) this.name._walk(visitor);
|
if (this.name) this.name._walk(visitor);
|
||||||
|
|
@ -385,10 +483,74 @@ var AST_Function = DEFNODE("Function", null, {
|
||||||
$documentation: "A function expression"
|
$documentation: "A function expression"
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
|
var AST_Arrow = DEFNODE("Arrow", null, {
|
||||||
|
$documentation: "An ES6 Arrow function ((a) => b)"
|
||||||
|
}, AST_Lambda);
|
||||||
|
|
||||||
|
var AST_ConciseMethod = DEFNODE("ConciseMethod", "is_generator static", {
|
||||||
|
$propdoc: {
|
||||||
|
is_generator: "is generatorFn or not",
|
||||||
|
static: "[boolean] whether this method is static (classes only)",
|
||||||
|
},
|
||||||
|
$documentation: "An ES6 concise method inside an object or class"
|
||||||
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Defun = DEFNODE("Defun", null, {
|
var AST_Defun = DEFNODE("Defun", null, {
|
||||||
$documentation: "A function definition"
|
$documentation: "A function definition"
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
|
/* -----[ DESTRUCTURING ]----- */
|
||||||
|
var AST_Destructuring = DEFNODE("Destructuring", "names is_array default", {
|
||||||
|
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
this.names.forEach(function(name){
|
||||||
|
name._walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
all_symbols: function() {
|
||||||
|
var out = [];
|
||||||
|
this.walk(new TreeWalker(function (node) {
|
||||||
|
if (node instanceof AST_Symbol) {
|
||||||
|
out.push(node);
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Expansion) {
|
||||||
|
out.push(node.expression);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
|
||||||
|
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
|
||||||
|
$propdoc: {
|
||||||
|
template_string: "[AST_TemplateString] The template string",
|
||||||
|
prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
|
||||||
|
},
|
||||||
|
_walk: function(visitor) {
|
||||||
|
this.prefix._walk(visitor);
|
||||||
|
this.template_string._walk(visitor);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
|
||||||
|
$documentation: "A template string literal",
|
||||||
|
$propdoc: {
|
||||||
|
segments: "[string|AST_Expression]* One or more segments. They can be the parts that are evaluated, or the raw string parts."
|
||||||
|
},
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
this.segments.forEach(function(seg, i){
|
||||||
|
if (i % 2 !== 0) {
|
||||||
|
seg._walk(visitor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ JUMPS ]----- */
|
/* -----[ JUMPS ]----- */
|
||||||
|
|
||||||
var AST_Jump = DEFNODE("Jump", null, {
|
var AST_Jump = DEFNODE("Jump", null, {
|
||||||
|
|
@ -542,16 +704,78 @@ var AST_Var = DEFNODE("Var", null, {
|
||||||
$documentation: "A `var` statement"
|
$documentation: "A `var` statement"
|
||||||
}, AST_Definitions);
|
}, AST_Definitions);
|
||||||
|
|
||||||
|
var AST_Let = DEFNODE("Let", null, {
|
||||||
|
$documentation: "A `let` statement"
|
||||||
|
}, AST_Definitions);
|
||||||
|
|
||||||
var AST_Const = DEFNODE("Const", null, {
|
var AST_Const = DEFNODE("Const", null, {
|
||||||
$documentation: "A `const` statement"
|
$documentation: "A `const` statement"
|
||||||
}, AST_Definitions);
|
}, AST_Definitions);
|
||||||
|
|
||||||
|
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
|
||||||
|
$documentation: "The part of the import statement that imports names from a module.",
|
||||||
|
$propdoc: {
|
||||||
|
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
|
||||||
|
name: "[AST_SymbolImport] The name as it becomes available to this module."
|
||||||
|
},
|
||||||
|
_walk: function (visitor) {
|
||||||
|
return visitor._visit(this, function() {
|
||||||
|
this.foreign_name._walk(visitor);
|
||||||
|
this.name._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||||
|
$documentation: "An `import` statement",
|
||||||
|
$propdoc: {
|
||||||
|
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
|
||||||
|
imported_names: "[AST_NameImport*] The names of non-default imported variables",
|
||||||
|
module_name: "[AST_String] String literal describing where this module came from",
|
||||||
|
},
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function() {
|
||||||
|
if (this.imported_name) {
|
||||||
|
this.imported_name._walk(visitor);
|
||||||
|
}
|
||||||
|
if (this.imported_names) {
|
||||||
|
this.imported_names.forEach(function (name_import) {
|
||||||
|
name_import._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.module_name._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default", {
|
||||||
|
$documentation: "An `export` statement",
|
||||||
|
$propdoc: {
|
||||||
|
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
||||||
|
exported_value: "[AST_Node?] An exported value",
|
||||||
|
is_default: "[Boolean] Whether this is the default exported value of this module"
|
||||||
|
},
|
||||||
|
_walk: function (visitor) {
|
||||||
|
visitor._visit(this, function () {
|
||||||
|
if (this.exported_definition) {
|
||||||
|
this.exported_definition._walk(visitor);
|
||||||
|
}
|
||||||
|
if (this.exported_value) {
|
||||||
|
this.exported_value._walk(visitor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, AST_Statement);
|
||||||
|
|
||||||
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
var AST_VarDef = DEFNODE("VarDef", "name value", {
|
||||||
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
$documentation: "A variable declaration; only appears in a AST_Definitions node",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable",
|
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
|
||||||
value: "[AST_Node?] initializer, or null of there's no initializer"
|
value: "[AST_Node?] initializer, or null of there's no initializer"
|
||||||
},
|
},
|
||||||
|
is_destructuring: function() {
|
||||||
|
return this.name instanceof AST_Destructuring;
|
||||||
|
},
|
||||||
_walk: function(visitor) {
|
_walk: function(visitor) {
|
||||||
return visitor._visit(this, function(){
|
return visitor._visit(this, function(){
|
||||||
this.name._walk(visitor);
|
this.name._walk(visitor);
|
||||||
|
|
@ -774,21 +998,68 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote shorthand", {
|
||||||
$documentation: "A key: value object property",
|
$documentation: "A key: value object property",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
quote: "[string] the original quote character"
|
quote: "[string] the original quote character",
|
||||||
|
shorthand: "[boolean] whether this is a shorthand key:value pair, expressed as just the key."
|
||||||
}
|
}
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
var AST_ObjectComputedKeyVal = DEFNODE("ObjectComputedKeyVal", null, {
|
||||||
|
$documentation: "An object property whose key is computed. Like `[Symbol.iterator]: function...` or `[routes.homepage]: renderHomepage`",
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
this.key._walk(visitor);
|
||||||
|
this.value._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
|
var AST_ObjectSetter = DEFNODE("ObjectSetter", "static", {
|
||||||
|
$propdoc: {
|
||||||
|
static: "[boolean] whether this is a static setter (classes only)"
|
||||||
|
},
|
||||||
$documentation: "An object setter property",
|
$documentation: "An object setter property",
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
var AST_ObjectGetter = DEFNODE("ObjectGetter", "static", {
|
||||||
|
$propdoc: {
|
||||||
|
static: "[boolean] whether this is a static getter (classes only)"
|
||||||
|
},
|
||||||
$documentation: "An object getter property",
|
$documentation: "An object getter property",
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
|
|
||||||
|
var AST_Class = DEFNODE("Class", "name extends properties", {
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
|
||||||
|
extends: "[AST_Node]? optional parent class",
|
||||||
|
properties: "[AST_ObjectProperty*] array of properties"
|
||||||
|
},
|
||||||
|
$documentation: "An ES6 class",
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, function(){
|
||||||
|
if (this.name) {
|
||||||
|
this.name._walk(visitor);
|
||||||
|
}
|
||||||
|
if (this.extends) {
|
||||||
|
this.extends._walk(visitor);
|
||||||
|
}
|
||||||
|
this.properties.forEach(function(prop){
|
||||||
|
prop._walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, AST_Scope);
|
||||||
|
|
||||||
|
var AST_DefClass = DEFNODE("DefClass", null, {
|
||||||
|
$documentation: "A class definition",
|
||||||
|
}, AST_Class);
|
||||||
|
|
||||||
|
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
|
||||||
|
$documentation: "A class expression."
|
||||||
|
}, AST_Class);
|
||||||
|
|
||||||
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
name: "[string] name of this symbol",
|
name: "[string] name of this symbol",
|
||||||
|
|
@ -798,14 +1069,24 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||||
$documentation: "Base class for all symbols",
|
$documentation: "Base class for all symbols",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var AST_NewTarget = DEFNODE("NewTarget", null, {
|
||||||
|
$documentation: "A reference to new.target"
|
||||||
|
});
|
||||||
|
|
||||||
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
||||||
$documentation: "The name of a property accessor (setter/getter function)"
|
$documentation: "The name of a property accessor (setter/getter function)"
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init default", {
|
||||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
init: "[AST_Node*/S] array of initializers for this declaration."
|
init: "[AST_Node*/S] array of initializers for this declaration.",
|
||||||
|
default: "[AST_Expression] The default for this parameter. For example, `= 6`"
|
||||||
|
},
|
||||||
|
_walk: function (visitor) {
|
||||||
|
return visitor._visit(this, function() {
|
||||||
|
if (this.default) this.default._walk(visitor);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
|
@ -813,9 +1094,17 @@ var AST_SymbolVar = DEFNODE("SymbolVar", null, {
|
||||||
$documentation: "Symbol defining a variable",
|
$documentation: "Symbol defining a variable",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
|
||||||
|
$documentation: "Base class for block-scoped declaration symbols"
|
||||||
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
|
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
|
||||||
$documentation: "A constant declaration"
|
$documentation: "A constant declaration"
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolBlockDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
|
||||||
|
$documentation: "A block-scoped `let` declaration"
|
||||||
|
}, AST_SymbolBlockDeclaration);
|
||||||
|
|
||||||
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
|
||||||
$documentation: "Symbol naming a function argument",
|
$documentation: "Symbol naming a function argument",
|
||||||
|
|
@ -825,13 +1114,33 @@ var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
|
||||||
$documentation: "Symbol defining a function",
|
$documentation: "Symbol defining a function",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
|
||||||
|
$documentation: "Symbol in an object defining a method",
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
|
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
|
||||||
$documentation: "Symbol naming a function expression",
|
$documentation: "Symbol naming a function expression",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
|
||||||
|
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
|
||||||
|
}, AST_SymbolBlockDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
|
||||||
|
$documentation: "Symbol naming a class's name. Lexically scoped to the class."
|
||||||
|
}, AST_SymbolDeclaration);
|
||||||
|
|
||||||
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
||||||
$documentation: "Symbol naming the exception in catch",
|
$documentation: "Symbol naming the exception in catch",
|
||||||
}, AST_SymbolDeclaration);
|
}, AST_SymbolBlockDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
|
||||||
|
$documentation: "Symbol refering to an imported name",
|
||||||
|
}, AST_SymbolBlockDeclaration);
|
||||||
|
|
||||||
|
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
|
||||||
|
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_Label = DEFNODE("Label", "references", {
|
var AST_Label = DEFNODE("Label", "references", {
|
||||||
$documentation: "Symbol naming a label (declaration)",
|
$documentation: "Symbol naming a label (declaration)",
|
||||||
|
|
@ -856,6 +1165,10 @@ var AST_This = DEFNODE("This", null, {
|
||||||
$documentation: "The `this` symbol",
|
$documentation: "The `this` symbol",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
var AST_Super = DEFNODE("Super", null, {
|
||||||
|
$documentation: "The `super` symbol",
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_Constant = DEFNODE("Constant", null, {
|
var AST_Constant = DEFNODE("Constant", null, {
|
||||||
$documentation: "Base class for all constants",
|
$documentation: "Base class for all constants",
|
||||||
getValue: function() {
|
getValue: function() {
|
||||||
|
|
@ -929,6 +1242,21 @@ var AST_True = DEFNODE("True", null, {
|
||||||
value: true
|
value: true
|
||||||
}, AST_Boolean);
|
}, AST_Boolean);
|
||||||
|
|
||||||
|
/* -----[ Yield ]----- */
|
||||||
|
|
||||||
|
var AST_Yield = DEFNODE("Yield", "expression is_star", {
|
||||||
|
$documentation: "A `yield` statement",
|
||||||
|
$propdoc: {
|
||||||
|
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
|
||||||
|
is_star: "[Boolean] Whether this is a yield or yield* statement"
|
||||||
|
},
|
||||||
|
_walk: function(visitor) {
|
||||||
|
return visitor._visit(this, this.expression && function(){
|
||||||
|
this.expression._walk(visitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ TreeWalker ]----- */
|
/* -----[ TreeWalker ]----- */
|
||||||
|
|
||||||
function TreeWalker(callback) {
|
function TreeWalker(callback) {
|
||||||
|
|
@ -979,7 +1307,7 @@ TreeWalker.prototype = {
|
||||||
var dir = this.directives[type];
|
var dir = this.directives[type];
|
||||||
if (dir) return dir;
|
if (dir) return dir;
|
||||||
var node = this.stack[this.stack.length - 1];
|
var node = this.stack[this.stack.length - 1];
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope && node.body) {
|
||||||
for (var i = 0; i < node.body.length; ++i) {
|
for (var i = 0; i < node.body.length; ++i) {
|
||||||
var st = node.body[i];
|
var st = node.body[i];
|
||||||
if (!(st instanceof AST_Directive)) break;
|
if (!(st instanceof AST_Directive)) break;
|
||||||
|
|
|
||||||
134
lib/compress.js
134
lib/compress.js
|
|
@ -246,6 +246,14 @@ merge(Compressor.prototype, {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function can_be_evicted_from_block(node) {
|
||||||
|
return !(
|
||||||
|
node instanceof AST_DefClass ||
|
||||||
|
node instanceof AST_Let ||
|
||||||
|
node instanceof AST_Const
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function loop_body(x) {
|
function loop_body(x) {
|
||||||
if (x instanceof AST_Switch) return x;
|
if (x instanceof AST_Switch) return x;
|
||||||
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
||||||
|
|
@ -353,7 +361,7 @@ merge(Compressor.prototype, {
|
||||||
if (side_effects_encountered |= lvalues_encountered) continue;
|
if (side_effects_encountered |= lvalues_encountered) continue;
|
||||||
|
|
||||||
// Non-constant single use vars can only be replaced in same scope.
|
// Non-constant single use vars can only be replaced in same scope.
|
||||||
if (ref.scope !== self) {
|
if (ref.scope.get_defun_scope() !== self) {
|
||||||
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
|
side_effects_encountered |= var_decl.value.has_side_effects(compressor);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -444,8 +452,16 @@ merge(Compressor.prototype, {
|
||||||
return /@ngInject/.test(comment.value);
|
return /@ngInject/.test(comment.value);
|
||||||
}
|
}
|
||||||
function make_arguments_names_list(func) {
|
function make_arguments_names_list(func) {
|
||||||
|
var foundDestructuring = false;
|
||||||
return func.argnames.map(function(sym){
|
return func.argnames.map(function(sym){
|
||||||
|
if (sym instanceof AST_Destructuring) {
|
||||||
|
compressor.warn("Function with destructuring arguments marked with @ngInject [{file}:{line},{col}]", token);
|
||||||
|
foundDestructuring = true;
|
||||||
|
}
|
||||||
|
if (foundDestructuring) { return null; }
|
||||||
return make_node(AST_String, sym, { value: sym.name });
|
return make_node(AST_String, sym, { value: sym.name });
|
||||||
|
}).filter(function (name) {
|
||||||
|
return name !== null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function make_array(orig, elements) {
|
function make_array(orig, elements) {
|
||||||
|
|
@ -516,7 +532,7 @@ merge(Compressor.prototype, {
|
||||||
function eliminate_spurious_blocks(statements) {
|
function eliminate_spurious_blocks(statements) {
|
||||||
var seen_dirs = [];
|
var seen_dirs = [];
|
||||||
return statements.reduce(function(a, stat){
|
return statements.reduce(function(a, stat){
|
||||||
if (stat instanceof AST_BlockStatement) {
|
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||||
} else if (stat instanceof AST_EmptyStatement) {
|
} else if (stat instanceof AST_EmptyStatement) {
|
||||||
|
|
@ -886,7 +902,7 @@ merge(Compressor.prototype, {
|
||||||
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
||||||
}
|
}
|
||||||
stat.walk(new TreeWalker(function(node){
|
stat.walk(new TreeWalker(function(node){
|
||||||
if (node instanceof AST_Definitions) {
|
if (node instanceof AST_Var) {
|
||||||
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
||||||
node.remove_initializers();
|
node.remove_initializers();
|
||||||
target.push(node);
|
target.push(node);
|
||||||
|
|
@ -1023,6 +1039,12 @@ merge(Compressor.prototype, {
|
||||||
// places too. :-( Wish JS had multiple inheritance.
|
// places too. :-( Wish JS had multiple inheritance.
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
|
def(AST_Arrow, function() {
|
||||||
|
throw def;
|
||||||
|
});
|
||||||
|
def(AST_Class, function() {
|
||||||
|
throw def;
|
||||||
|
});
|
||||||
function ev(node, compressor) {
|
function ev(node, compressor) {
|
||||||
if (!compressor) throw new Error("Compressor must be passed");
|
if (!compressor) throw new Error("Compressor must be passed");
|
||||||
|
|
||||||
|
|
@ -1041,7 +1063,8 @@ merge(Compressor.prototype, {
|
||||||
case "typeof":
|
case "typeof":
|
||||||
// Function would be evaluated to an array and so typeof would
|
// Function would be evaluated to an array and so typeof would
|
||||||
// incorrectly return 'object'. Hence making is a special case.
|
// incorrectly return 'object'. Hence making is a special case.
|
||||||
if (e instanceof AST_Function) return typeof function(){};
|
if (e instanceof AST_Function ||
|
||||||
|
e instanceof AST_Arrow) return typeof function(){};
|
||||||
|
|
||||||
e = ev(e, compressor);
|
e = ev(e, compressor);
|
||||||
|
|
||||||
|
|
@ -1067,6 +1090,7 @@ merge(Compressor.prototype, {
|
||||||
case "^" : result = ev(left, c) ^ ev(right, c); break;
|
case "^" : result = ev(left, c) ^ ev(right, c); break;
|
||||||
case "+" : result = ev(left, c) + ev(right, c); break;
|
case "+" : result = ev(left, c) + ev(right, c); break;
|
||||||
case "*" : result = ev(left, c) * ev(right, c); break;
|
case "*" : result = ev(left, c) * ev(right, c); break;
|
||||||
|
case "**" : result = Math.pow(ev(left, c), ev(right, c)); break;
|
||||||
case "/" : result = ev(left, c) / ev(right, c); break;
|
case "/" : result = ev(left, c) / ev(right, c); break;
|
||||||
case "%" : result = ev(left, c) % ev(right, c); break;
|
case "%" : result = ev(left, c) % ev(right, c); break;
|
||||||
case "-" : result = ev(left, c) - ev(right, c); break;
|
case "-" : result = ev(left, c) - ev(right, c); break;
|
||||||
|
|
@ -1215,6 +1239,8 @@ merge(Compressor.prototype, {
|
||||||
});
|
});
|
||||||
def(AST_Defun, function(compressor){ return true });
|
def(AST_Defun, function(compressor){ return true });
|
||||||
def(AST_Function, function(compressor){ return false });
|
def(AST_Function, function(compressor){ return false });
|
||||||
|
def(AST_Class, function(compressor){ return false });
|
||||||
|
def(AST_DefClass, function(compressor){ return true });
|
||||||
def(AST_Binary, function(compressor){
|
def(AST_Binary, function(compressor){
|
||||||
return this.left.has_side_effects(compressor)
|
return this.left.has_side_effects(compressor)
|
||||||
|| this.right.has_side_effects(compressor);
|
|| this.right.has_side_effects(compressor);
|
||||||
|
|
@ -1226,9 +1252,7 @@ merge(Compressor.prototype, {
|
||||||
|| this.alternative.has_side_effects(compressor);
|
|| this.alternative.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Unary, function(compressor){
|
def(AST_Unary, function(compressor){
|
||||||
return this.operator == "delete"
|
return member(this.operator, ["delete", "++", "--"])
|
||||||
|| this.operator == "++"
|
|
||||||
|| this.operator == "--"
|
|
||||||
|| this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor){
|
def(AST_SymbolRef, function(compressor){
|
||||||
|
|
@ -1241,6 +1265,9 @@ merge(Compressor.prototype, {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function(compressor){
|
def(AST_ObjectProperty, function(compressor){
|
||||||
|
if (this instanceof AST_ObjectComputedKeyVal &&
|
||||||
|
this.key.has_side_effects(compressor))
|
||||||
|
return true;
|
||||||
return this.value.has_side_effects(compressor);
|
return this.value.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Array, function(compressor){
|
def(AST_Array, function(compressor){
|
||||||
|
|
@ -1280,6 +1307,7 @@ merge(Compressor.prototype, {
|
||||||
var n = this.body.length;
|
var n = this.body.length;
|
||||||
return n > 0 && aborts(this.body[n - 1]);
|
return n > 0 && aborts(this.body[n - 1]);
|
||||||
};
|
};
|
||||||
|
def(AST_Import, function(){ return null; });
|
||||||
def(AST_BlockStatement, block_aborts);
|
def(AST_BlockStatement, block_aborts);
|
||||||
def(AST_SwitchBranch, block_aborts);
|
def(AST_SwitchBranch, block_aborts);
|
||||||
def(AST_If, function(){
|
def(AST_If, function(){
|
||||||
|
|
@ -1313,6 +1341,7 @@ merge(Compressor.prototype, {
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Block, function(self, compressor){
|
OPT(AST_Block, function(self, compressor){
|
||||||
|
if (self.body instanceof AST_Node) { return self; }
|
||||||
self.body = tighten_body(self.body, compressor);
|
self.body = tighten_body(self.body, compressor);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
@ -1320,7 +1349,11 @@ merge(Compressor.prototype, {
|
||||||
OPT(AST_BlockStatement, function(self, compressor){
|
OPT(AST_BlockStatement, function(self, compressor){
|
||||||
self.body = tighten_body(self.body, compressor);
|
self.body = tighten_body(self.body, compressor);
|
||||||
switch (self.body.length) {
|
switch (self.body.length) {
|
||||||
case 1: return self.body[0];
|
case 1:
|
||||||
|
if (can_be_evicted_from_block(self.body[0])) {
|
||||||
|
return self.body[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 0: return make_node(AST_EmptyStatement, self);
|
case 0: return make_node(AST_EmptyStatement, self);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -1330,7 +1363,6 @@ merge(Compressor.prototype, {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (compressor.has_directive("use asm")) return self;
|
if (compressor.has_directive("use asm")) return self;
|
||||||
if (compressor.option("unused")
|
if (compressor.option("unused")
|
||||||
&& !(self instanceof AST_Toplevel)
|
|
||||||
&& !self.uses_eval
|
&& !self.uses_eval
|
||||||
&& !self.uses_with
|
&& !self.uses_with
|
||||||
) {
|
) {
|
||||||
|
|
@ -1342,12 +1374,13 @@ merge(Compressor.prototype, {
|
||||||
var scope = this;
|
var scope = this;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node !== self) {
|
if (node !== self) {
|
||||||
if (node instanceof AST_Defun) {
|
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||||
initializations.add(node.name.name, node);
|
initializations.add(node.name.name, node);
|
||||||
return true; // don't go in nested scopes
|
return true; // don't go in nested scopes
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
|
if (def.is_destructuring()) return; /* Destructurings are type assertions! */
|
||||||
if (def.value) {
|
if (def.value) {
|
||||||
initializations.add(def.name.name, def.value);
|
initializations.add(def.name.name, def.value);
|
||||||
if (def.value.has_side_effects(compressor)) {
|
if (def.value.has_side_effects(compressor)) {
|
||||||
|
|
@ -1402,22 +1435,35 @@ merge(Compressor.prototype, {
|
||||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||||
if (!compressor.option("keep_fargs")) {
|
if (!compressor.option("keep_fargs")) {
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
if (a[i] instanceof AST_Destructuring) {
|
||||||
if (sym.unreferenced()) {
|
// Do not drop destructuring arguments.
|
||||||
a.pop();
|
// They constitute a type assertion, so dropping
|
||||||
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
// them would stop that TypeError which would happen
|
||||||
name : sym.name,
|
// if someone called it with an incorrectly formatted
|
||||||
file : sym.start.file,
|
// parameter.
|
||||||
line : sym.start.line,
|
break;
|
||||||
col : sym.start.col
|
} else {
|
||||||
});
|
var sym = a[i];
|
||||||
|
if (sym instanceof AST_Expansion) {
|
||||||
|
sym = sym.symbol;
|
||||||
|
}
|
||||||
|
if (sym.unreferenced()) {
|
||||||
|
a.pop();
|
||||||
|
compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", {
|
||||||
|
name : sym.name,
|
||||||
|
file : sym.start.file,
|
||||||
|
line : sym.start.line,
|
||||||
|
col : sym.start.col
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else break;
|
||||||
}
|
}
|
||||||
else break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Defun && node !== self) {
|
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
||||||
if (!(node.name.definition().id in in_use_ids)) {
|
var keep = (node.name.definition().id in in_use_ids) || node.name.definition().global;
|
||||||
|
if (!keep) {
|
||||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||||
name : node.name.name,
|
name : node.name.name,
|
||||||
file : node.name.start.file,
|
file : node.name.start.file,
|
||||||
|
|
@ -1430,7 +1476,10 @@ merge(Compressor.prototype, {
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
|
||||||
var def = node.definitions.filter(function(def){
|
var def = node.definitions.filter(function(def){
|
||||||
|
if (def.is_destructuring()) return true;
|
||||||
if (def.name.definition().id in in_use_ids) return true;
|
if (def.name.definition().id in in_use_ids) return true;
|
||||||
|
if (def.name.definition().global) return true;
|
||||||
|
|
||||||
var w = {
|
var w = {
|
||||||
name : def.name.name,
|
name : def.name.name,
|
||||||
file : def.name.start.file,
|
file : def.name.start.file,
|
||||||
|
|
@ -1509,6 +1558,12 @@ merge(Compressor.prototype, {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_BlockStatement) {
|
||||||
|
descend(node, this);
|
||||||
|
if (in_list && all(node.body, can_be_evicted_from_block)) {
|
||||||
|
return MAP.splice(node.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
@ -1520,8 +1575,12 @@ merge(Compressor.prototype, {
|
||||||
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
|
||||||
var self = this;
|
var self = this;
|
||||||
if (compressor.has_directive("use asm")) return self;
|
if (compressor.has_directive("use asm")) return self;
|
||||||
|
// Hoisting makes no sense in an arrow func
|
||||||
|
if (!Array.isArray(self.body)) return self;
|
||||||
|
|
||||||
var hoist_funs = compressor.option("hoist_funs");
|
var hoist_funs = compressor.option("hoist_funs");
|
||||||
var hoist_vars = compressor.option("hoist_vars");
|
var hoist_vars = compressor.option("hoist_vars");
|
||||||
|
|
||||||
if (hoist_funs || hoist_vars) {
|
if (hoist_funs || hoist_vars) {
|
||||||
var dirs = [];
|
var dirs = [];
|
||||||
var hoisted = [];
|
var hoisted = [];
|
||||||
|
|
@ -1550,6 +1609,7 @@ merge(Compressor.prototype, {
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Var && hoist_vars) {
|
if (node instanceof AST_Var && hoist_vars) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
|
if (def.is_destructuring()) { return; }
|
||||||
vars.set(def.name.name, def);
|
vars.set(def.name.name, def);
|
||||||
++vars_found;
|
++vars_found;
|
||||||
});
|
});
|
||||||
|
|
@ -1582,7 +1642,7 @@ merge(Compressor.prototype, {
|
||||||
vars.each(function(def, name){
|
vars.each(function(def, name){
|
||||||
if (self instanceof AST_Lambda
|
if (self instanceof AST_Lambda
|
||||||
&& find_if(function(x){ return x.name == def.name.name },
|
&& find_if(function(x){ return x.name == def.name.name },
|
||||||
self.argnames)) {
|
self.args_as_names())) {
|
||||||
vars.del(name);
|
vars.del(name);
|
||||||
} else {
|
} else {
|
||||||
def = def.clone();
|
def = def.clone();
|
||||||
|
|
@ -2004,13 +2064,23 @@ merge(Compressor.prototype, {
|
||||||
|
|
||||||
AST_Definitions.DEFMETHOD("to_assignments", function(){
|
AST_Definitions.DEFMETHOD("to_assignments", function(){
|
||||||
var assignments = this.definitions.reduce(function(a, def){
|
var assignments = this.definitions.reduce(function(a, def){
|
||||||
if (def.value) {
|
if (def.value && !def.is_destructuring()) {
|
||||||
var name = make_node(AST_SymbolRef, def.name, def.name);
|
var name = make_node(AST_SymbolRef, def.name, def.name);
|
||||||
a.push(make_node(AST_Assign, def, {
|
a.push(make_node(AST_Assign, def, {
|
||||||
operator : "=",
|
operator : "=",
|
||||||
left : name,
|
left : name,
|
||||||
right : def.value
|
right : def.value
|
||||||
}));
|
}));
|
||||||
|
} else if (def.value) {
|
||||||
|
// Because it's a destructuring, do not turn into an assignment.
|
||||||
|
var varDef = make_node(AST_VarDef, def, {
|
||||||
|
name: def.name,
|
||||||
|
value: def.value
|
||||||
|
});
|
||||||
|
var var_ = make_node(AST_Var, def, {
|
||||||
|
definitions: [ varDef ]
|
||||||
|
});
|
||||||
|
a.push(var_);
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -2024,6 +2094,10 @@ merge(Compressor.prototype, {
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPT(AST_Import, function(self, compressor) {
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_Function, function(self, compressor){
|
OPT(AST_Function, function(self, compressor){
|
||||||
self = AST_Lambda.prototype.optimize.call(self, compressor);
|
self = AST_Lambda.prototype.optimize.call(self, compressor);
|
||||||
if (compressor.option("unused") && !compressor.option("keep_fnames")) {
|
if (compressor.option("unused") && !compressor.option("keep_fnames")) {
|
||||||
|
|
@ -2137,6 +2211,11 @@ merge(Compressor.prototype, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "Symbol":
|
||||||
|
// Symbol's argument is only used for debugging.
|
||||||
|
self.args = [];
|
||||||
|
return self;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||||
|
|
@ -2902,4 +2981,11 @@ merge(Compressor.prototype, {
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPT(AST_Yield, function(self, compressor){
|
||||||
|
if (!self.is_star && self.expression instanceof AST_Undefined) {
|
||||||
|
self.expression = null;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
309
lib/output.js
309
lib/output.js
|
|
@ -67,7 +67,8 @@ function OutputStream(options) {
|
||||||
screw_ie8 : true,
|
screw_ie8 : true,
|
||||||
preamble : null,
|
preamble : null,
|
||||||
quote_style : 0,
|
quote_style : 0,
|
||||||
keep_quoted_props: false
|
keep_quoted_props: false,
|
||||||
|
ecma : 5,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
|
|
@ -77,9 +78,16 @@ function OutputStream(options) {
|
||||||
var OUTPUT = "";
|
var OUTPUT = "";
|
||||||
|
|
||||||
function to_ascii(str, identifier) {
|
function to_ascii(str, identifier) {
|
||||||
return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
|
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
|
||||||
var code = ch.charCodeAt(0).toString(16);
|
var code = get_full_char_code(ch, 0).toString(16);
|
||||||
if (code.length <= 2 && !identifier) {
|
|
||||||
|
if ((identifier && code.length === 1 && !options.es5) || code.length > 4) {
|
||||||
|
if (options.ecma < 6) {
|
||||||
|
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
|
||||||
|
+ ch.charCodeAt(1).toString(16);
|
||||||
|
}
|
||||||
|
return "\\u{" + code + "}";
|
||||||
|
} else if (code.length <= 2 && !identifier) {
|
||||||
while (code.length < 2) code = "0" + code;
|
while (code.length < 2) code = "0" + code;
|
||||||
return "\\x" + code;
|
return "\\x" + code;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -107,7 +115,7 @@ function OutputStream(options) {
|
||||||
case "\u2029": return "\\u2029";
|
case "\u2029": return "\\u2029";
|
||||||
case "\ufeff": return "\\ufeff";
|
case "\ufeff": return "\\ufeff";
|
||||||
case "\0":
|
case "\0":
|
||||||
return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
|
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0";
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
});
|
});
|
||||||
|
|
@ -158,7 +166,13 @@ function OutputStream(options) {
|
||||||
var last = null;
|
var last = null;
|
||||||
|
|
||||||
function last_char() {
|
function last_char() {
|
||||||
return last.charAt(last.length - 1);
|
var char = last.charAt(last.length - 1);
|
||||||
|
|
||||||
|
if (is_surrogate_pair_tail(char)) {
|
||||||
|
return last.charAt(last.length - 2) + char;
|
||||||
|
}
|
||||||
|
|
||||||
|
return char;
|
||||||
};
|
};
|
||||||
|
|
||||||
function maybe_newline() {
|
function maybe_newline() {
|
||||||
|
|
@ -170,7 +184,7 @@ function OutputStream(options) {
|
||||||
|
|
||||||
function print(str) {
|
function print(str) {
|
||||||
str = String(str);
|
str = String(str);
|
||||||
var ch = str.charAt(0);
|
var ch = get_full_char(str, 0);
|
||||||
if (might_need_semicolon) {
|
if (might_need_semicolon) {
|
||||||
might_need_semicolon = false;
|
might_need_semicolon = false;
|
||||||
|
|
||||||
|
|
@ -232,6 +246,10 @@ function OutputStream(options) {
|
||||||
OUTPUT += str;
|
OUTPUT += str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var star = function(){
|
||||||
|
print("*");
|
||||||
|
}
|
||||||
|
|
||||||
var space = options.beautify ? function() {
|
var space = options.beautify ? function() {
|
||||||
print(" ");
|
print(" ");
|
||||||
} : function() {
|
} : function() {
|
||||||
|
|
@ -349,6 +367,7 @@ function OutputStream(options) {
|
||||||
should_break : function() { return options.width && this.current_width() >= options.width },
|
should_break : function() { return options.width && this.current_width() >= options.width },
|
||||||
newline : newline,
|
newline : newline,
|
||||||
print : print,
|
print : print,
|
||||||
|
star : star,
|
||||||
space : space,
|
space : space,
|
||||||
comma : comma,
|
comma : comma,
|
||||||
colon : colon,
|
colon : colon,
|
||||||
|
|
@ -533,7 +552,13 @@ function OutputStream(options) {
|
||||||
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_PropAccess && p.expression === this
|
return p instanceof AST_PropAccess && p.expression === this
|
||||||
|| p instanceof AST_New;
|
|| p instanceof AST_New
|
||||||
|
|| p instanceof AST_Binary
|
||||||
|
&& p.operator === "**"
|
||||||
|
&& this instanceof AST_UnaryPrefix
|
||||||
|
&& p.left === this
|
||||||
|
&& this.operator !== "++"
|
||||||
|
&& this.operator !== "--";
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Seq, function(output){
|
PARENS(AST_Seq, function(output){
|
||||||
|
|
@ -573,6 +598,20 @@ function OutputStream(options) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Yield, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
// (yield 1) + (yield 2)
|
||||||
|
// a = yield 3
|
||||||
|
if (p instanceof AST_Binary && p.operator !== "=")
|
||||||
|
return true;
|
||||||
|
// (yield 1) ? yield 2 : yield 3
|
||||||
|
if (p instanceof AST_Conditional && p.condition === this)
|
||||||
|
return true;
|
||||||
|
// -(yield 4)
|
||||||
|
if (p instanceof AST_Unary)
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_PropAccess, function(output){
|
PARENS(AST_PropAccess, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_New && p.expression === this) {
|
if (p instanceof AST_New && p.expression === this) {
|
||||||
|
|
@ -650,6 +689,28 @@ function OutputStream(options) {
|
||||||
output.print_string(self.value, self.quote);
|
output.print_string(self.value, self.quote);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DEFPRINT(AST_Expansion, function (self, output) {
|
||||||
|
output.print('...');
|
||||||
|
self.expression.print(output);
|
||||||
|
});
|
||||||
|
|
||||||
|
DEFPRINT(AST_Destructuring, function (self, output) {
|
||||||
|
output.print(self.is_array ? "[" : "{");
|
||||||
|
var first = true;
|
||||||
|
self.names.forEach(function (name) {
|
||||||
|
if (first) first = false; else { output.comma(); output.space(); }
|
||||||
|
name.print(output);
|
||||||
|
})
|
||||||
|
output.print(self.is_array ? "]" : "}");
|
||||||
|
if (self.default) {
|
||||||
|
output.space();
|
||||||
|
output.print('=');
|
||||||
|
output.space();
|
||||||
|
self.default.print(output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
DEFPRINT(AST_Debugger, function(self, output){
|
DEFPRINT(AST_Debugger, function(self, output){
|
||||||
output.print("debugger");
|
output.print("debugger");
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
|
|
@ -774,7 +835,11 @@ function OutputStream(options) {
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
self.init.print(output);
|
self.init.print(output);
|
||||||
output.space();
|
output.space();
|
||||||
output.print("in");
|
if (self instanceof AST_ForOf) {
|
||||||
|
output.print("of");
|
||||||
|
} else {
|
||||||
|
output.print("in");
|
||||||
|
}
|
||||||
output.space();
|
output.space();
|
||||||
self.object.print(output);
|
self.object.print(output);
|
||||||
});
|
});
|
||||||
|
|
@ -796,10 +861,19 @@ function OutputStream(options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!nokeyword) {
|
if (!nokeyword) {
|
||||||
output.print("function");
|
output.print("function");
|
||||||
|
if (this.is_generator) {
|
||||||
|
output.star();
|
||||||
|
}
|
||||||
|
if (self.name) {
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (self.name) {
|
if (self.name instanceof AST_Symbol) {
|
||||||
output.space();
|
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
|
} else if (nokeyword && self.name instanceof AST_Node) {
|
||||||
|
output.with_square(function() {
|
||||||
|
self.name.print(output);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
self.argnames.forEach(function(arg, i){
|
self.argnames.forEach(function(arg, i){
|
||||||
|
|
@ -814,6 +888,62 @@ function OutputStream(options) {
|
||||||
self._do_print(output);
|
self._do_print(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
|
||||||
|
self.prefix.print(output);
|
||||||
|
self.template_string.print(output);
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_TemplateString, function(self, output) {
|
||||||
|
output.print("`");
|
||||||
|
for (var i = 0; i < self.segments.length; i++) {
|
||||||
|
if (typeof self.segments[i] !== "string") {
|
||||||
|
output.print("${");
|
||||||
|
self.segments[i].print(output);
|
||||||
|
output.print("}");
|
||||||
|
} else {
|
||||||
|
output.print(self.segments[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.print("`");
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Arrow.DEFMETHOD("_do_print", function(output){
|
||||||
|
var self = this;
|
||||||
|
var parent = output.parent();
|
||||||
|
var needs_parens = parent instanceof AST_Binary ||
|
||||||
|
parent instanceof AST_Unary ||
|
||||||
|
parent instanceof AST_Call;
|
||||||
|
if (needs_parens) { output.print("(") }
|
||||||
|
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol && !self.argnames[0].default) {
|
||||||
|
self.argnames[0].print(output);
|
||||||
|
} else {
|
||||||
|
output.with_parens(function(){
|
||||||
|
self.argnames.forEach(function(arg, i){
|
||||||
|
if (i) output.comma();
|
||||||
|
arg.print(output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
output.space();
|
||||||
|
output.print('=>');
|
||||||
|
output.space();
|
||||||
|
if (self.body instanceof AST_Node) {
|
||||||
|
this.body.print(output);
|
||||||
|
} else {
|
||||||
|
print_bracketed(this.body, output);
|
||||||
|
}
|
||||||
|
if (needs_parens) { output.print(")") }
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_ConciseMethod, function(self, output){
|
||||||
|
if (self.static) {
|
||||||
|
output.print("static");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
if (self.is_generator) {
|
||||||
|
output.print("*");
|
||||||
|
}
|
||||||
|
self._do_print(output, true /* do not print "function" */);
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ exits ]----- */
|
/* -----[ exits ]----- */
|
||||||
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
|
AST_Exit.DEFMETHOD("_do_print", function(output, kind){
|
||||||
output.print(kind);
|
output.print(kind);
|
||||||
|
|
@ -830,6 +960,17 @@ function OutputStream(options) {
|
||||||
self._do_print(output, "throw");
|
self._do_print(output, "throw");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* -----[ yield ]----- */
|
||||||
|
|
||||||
|
DEFPRINT(AST_Yield, function(self, output){
|
||||||
|
var star = self.is_star ? "*" : "";
|
||||||
|
output.print("yield" + star);
|
||||||
|
if (self.expression) {
|
||||||
|
output.space();
|
||||||
|
self.expression.print(output);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ loop control ]----- */
|
/* -----[ loop control ]----- */
|
||||||
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
|
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
|
||||||
output.print(kind);
|
output.print(kind);
|
||||||
|
|
@ -986,12 +1127,77 @@ function OutputStream(options) {
|
||||||
if (!avoid_semicolon)
|
if (!avoid_semicolon)
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Let, function(self, output){
|
||||||
|
self._do_print(output, "let");
|
||||||
|
});
|
||||||
DEFPRINT(AST_Var, function(self, output){
|
DEFPRINT(AST_Var, function(self, output){
|
||||||
self._do_print(output, "var");
|
self._do_print(output, "var");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Const, function(self, output){
|
DEFPRINT(AST_Const, function(self, output){
|
||||||
self._do_print(output, "const");
|
self._do_print(output, "const");
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Import, function(self, output) {
|
||||||
|
output.print("import");
|
||||||
|
output.space();
|
||||||
|
if (self.imported_name) {
|
||||||
|
self.imported_name.print(output);
|
||||||
|
}
|
||||||
|
if (self.imported_name && self.imported_names) {
|
||||||
|
output.print(",");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
if (self.imported_names) {
|
||||||
|
output.print("{");
|
||||||
|
self.imported_names.forEach(function(name_import, i) {
|
||||||
|
output.space();
|
||||||
|
name_import.print(output);
|
||||||
|
if (i < self.imported_names.length - 1) {
|
||||||
|
output.print(",");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
output.space();
|
||||||
|
output.print("}");
|
||||||
|
}
|
||||||
|
if (self.imported_name || self.imported_names) {
|
||||||
|
output.space();
|
||||||
|
output.print("from")
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
self.module_name.print(output);
|
||||||
|
output.semicolon();
|
||||||
|
});
|
||||||
|
|
||||||
|
DEFPRINT(AST_NameImport, function(self, output) {
|
||||||
|
var definition = self.name.definition();
|
||||||
|
var names_are_different =
|
||||||
|
(definition && definition.mangled_name || self.name.name) !==
|
||||||
|
self.foreign_name.name;
|
||||||
|
if (names_are_different) {
|
||||||
|
output.print(self.foreign_name.name);
|
||||||
|
output.space();
|
||||||
|
output.print("as");
|
||||||
|
output.space();
|
||||||
|
self.name.print(output);
|
||||||
|
} else {
|
||||||
|
self.name.print(output);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
DEFPRINT(AST_Export, function(self, output) {
|
||||||
|
output.print("export");
|
||||||
|
output.space();
|
||||||
|
if (self.is_default) {
|
||||||
|
output.print("default");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
if (self.exported_value) {
|
||||||
|
self.exported_value.print(output);
|
||||||
|
} else if (self.exported_definition) {
|
||||||
|
self.exported_definition.print(output);
|
||||||
|
}
|
||||||
|
output.semicolon();
|
||||||
|
});
|
||||||
|
|
||||||
function parenthesize_for_noin(node, output, noin) {
|
function parenthesize_for_noin(node, output, noin) {
|
||||||
if (!noin) node.print(output);
|
if (!noin) node.print(output);
|
||||||
|
|
@ -1163,9 +1369,45 @@ function OutputStream(options) {
|
||||||
});
|
});
|
||||||
else output.print("{}");
|
else output.print("{}");
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Class, function(self, output){
|
||||||
|
output.print("class");
|
||||||
|
output.space();
|
||||||
|
if (self.name) {
|
||||||
|
self.name.print(output);
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
if (self.extends) {
|
||||||
|
output.print("extends");
|
||||||
|
output.space();
|
||||||
|
self.extends.print(output);
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
|
if (self.properties.length > 0) output.with_block(function(){
|
||||||
|
self.properties.forEach(function(prop, i){
|
||||||
|
if (i) {
|
||||||
|
output.newline();
|
||||||
|
}
|
||||||
|
output.indent();
|
||||||
|
prop.print(output);
|
||||||
|
});
|
||||||
|
output.newline();
|
||||||
|
});
|
||||||
|
else output.print("{}");
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_NewTarget, function(self, output) {
|
||||||
|
output.print("new.target");
|
||||||
|
});
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
DEFPRINT(AST_ObjectKeyVal, function(self, output){
|
||||||
var key = self.key;
|
var key = self.key;
|
||||||
var quote = self.quote;
|
var quote = self.quote;
|
||||||
|
var print_as_shorthand = self.shorthand &&
|
||||||
|
self.value instanceof AST_Symbol &&
|
||||||
|
self.key == self.value.print_to_string();
|
||||||
|
|
||||||
|
if (print_as_shorthand) {
|
||||||
|
output.print_name(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (output.option("quote_keys")) {
|
if (output.option("quote_keys")) {
|
||||||
output.print_string(key + "");
|
output.print_string(key + "");
|
||||||
} else if ((typeof key == "number"
|
} else if ((typeof key == "number"
|
||||||
|
|
@ -1186,20 +1428,56 @@ function OutputStream(options) {
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectSetter, function(self, output){
|
DEFPRINT(AST_ObjectSetter, function(self, output){
|
||||||
|
if (self.static) {
|
||||||
|
output.print("static");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
output.print("set");
|
output.print("set");
|
||||||
output.space();
|
output.space();
|
||||||
self.key.print(output);
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ObjectGetter, function(self, output){
|
DEFPRINT(AST_ObjectGetter, function(self, output){
|
||||||
|
if (self.static) {
|
||||||
|
output.print("static");
|
||||||
|
output.space();
|
||||||
|
}
|
||||||
output.print("get");
|
output.print("get");
|
||||||
output.space();
|
output.space();
|
||||||
self.key.print(output);
|
self.key.print(output);
|
||||||
self.value._do_print(output, true);
|
self.value._do_print(output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Symbol, function(self, output){
|
DEFPRINT(AST_ObjectComputedKeyVal, function(self, output) {
|
||||||
var def = self.definition();
|
output.print("[");
|
||||||
output.print_name(def ? def.mangled_name || def.name : self.name);
|
self.key.print(output);
|
||||||
|
output.print("]:");
|
||||||
|
output.space();
|
||||||
|
self.value.print(output);
|
||||||
|
});
|
||||||
|
AST_Symbol.DEFMETHOD("_do_print", function(output){
|
||||||
|
var def = this.definition();
|
||||||
|
output.print_name(def ? def.mangled_name || def.name : this.name);
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_Symbol, function (self, output) {
|
||||||
|
self._do_print(output);
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_SymbolDeclaration, function(self, output){
|
||||||
|
self._do_print(output);
|
||||||
|
if (self.default) {
|
||||||
|
output.space();
|
||||||
|
output.print('=');
|
||||||
|
output.space();
|
||||||
|
self.default.print(output)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_SymbolMethod, function(self, output) {
|
||||||
|
if (self.name instanceof AST_Node) {
|
||||||
|
output.with_square(function() {
|
||||||
|
self.name.print(output);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self._do_print(output);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Undefined, function(self, output){
|
DEFPRINT(AST_Undefined, function(self, output){
|
||||||
output.print("void 0");
|
output.print("void 0");
|
||||||
|
|
@ -1214,6 +1492,9 @@ function OutputStream(options) {
|
||||||
DEFPRINT(AST_This, function(self, output){
|
DEFPRINT(AST_This, function(self, output){
|
||||||
output.print("this");
|
output.print("this");
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Super, function(self, output){
|
||||||
|
output.print("super");
|
||||||
|
});
|
||||||
DEFPRINT(AST_Constant, function(self, output){
|
DEFPRINT(AST_Constant, function(self, output){
|
||||||
output.print(self.getValue());
|
output.print(self.getValue());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
958
lib/parse.js
958
lib/parse.js
File diff suppressed because one or more lines are too long
|
|
@ -44,10 +44,16 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function find_builtins() {
|
function find_builtins() {
|
||||||
|
|
||||||
|
// Compatibility fix for es5.1 and earlier where Symbol isn't defined
|
||||||
|
if (!global.Symbol) {
|
||||||
|
global.Symbol = new Function();
|
||||||
|
}
|
||||||
|
|
||||||
var a = [];
|
var a = [];
|
||||||
[ Object, Array, Function, Number,
|
[ Object, Array, Function, Number,
|
||||||
String, Boolean, Error, Math,
|
String, Boolean, Error, Math,
|
||||||
Date, RegExp
|
Date, RegExp, Symbol
|
||||||
].forEach(function(ctor){
|
].forEach(function(ctor){
|
||||||
Object.getOwnPropertyNames(ctor).map(add);
|
Object.getOwnPropertyNames(ctor).map(add);
|
||||||
if (ctor.prototype) {
|
if (ctor.prototype) {
|
||||||
|
|
@ -108,6 +114,9 @@ function mangle_properties(ast, options) {
|
||||||
addStrings(node.property);
|
addStrings(node.property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (node instanceof AST_ConciseMethod) {
|
||||||
|
add(node.name.name);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// step 2: transform the tree, renaming properties
|
// step 2: transform the tree, renaming properties
|
||||||
|
|
@ -127,6 +136,11 @@ function mangle_properties(ast, options) {
|
||||||
if (!ignore_quoted)
|
if (!ignore_quoted)
|
||||||
node.property = mangleStrings(node.property);
|
node.property = mangleStrings(node.property);
|
||||||
}
|
}
|
||||||
|
else if (node instanceof AST_ConciseMethod) {
|
||||||
|
if (should_mangle(node.name.name)) {
|
||||||
|
node.name.name = mangle(node.name.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
// else if (node instanceof AST_String) {
|
// else if (node instanceof AST_String) {
|
||||||
// if (should_mangle(node.value)) {
|
// if (should_mangle(node.value)) {
|
||||||
// AST_Node.warn(
|
// AST_Node.warn(
|
||||||
|
|
|
||||||
111
lib/scope.js
111
lib/scope.js
|
|
@ -49,7 +49,9 @@ function SymbolDef(scope, index, orig) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.references = [];
|
this.references = [];
|
||||||
this.global = false;
|
this.global = false;
|
||||||
|
this.export = false;
|
||||||
this.mangled_name = null;
|
this.mangled_name = null;
|
||||||
|
this.object_destructuring_arg = false;
|
||||||
this.undeclared = false;
|
this.undeclared = false;
|
||||||
this.constant = false;
|
this.constant = false;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
|
@ -63,11 +65,17 @@ SymbolDef.prototype = {
|
||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
|
|
||||||
return (this.global && !options.toplevel)
|
return (this.global && !options.toplevel)
|
||||||
|
|| this.export
|
||||||
|
|| this.object_destructuring_arg
|
||||||
|| this.undeclared
|
|| this.undeclared
|
||||||
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
||||||
|| (options.keep_fnames
|
|| (options.keep_fnames
|
||||||
&& (this.orig[0] instanceof AST_SymbolLambda
|
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||||
|| this.orig[0] instanceof AST_SymbolDefun));
|
|| this.orig[0] instanceof AST_SymbolDefun))
|
||||||
|
|| this.orig[0] instanceof AST_SymbolMethod
|
||||||
|
|| (options.keep_classnames
|
||||||
|
&& (this.orig[0] instanceof AST_SymbolClass
|
||||||
|
|| this.orig[0] instanceof AST_SymbolDefClass));
|
||||||
},
|
},
|
||||||
mangle: function(options) {
|
mangle: function(options) {
|
||||||
var cache = options.cache && options.cache.props;
|
var cache = options.cache && options.cache.props;
|
||||||
|
|
@ -99,16 +107,33 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
var defun = null;
|
var defun = null;
|
||||||
var last_var_had_const_pragma = false;
|
var last_var_had_const_pragma = false;
|
||||||
var nesting = 0;
|
var nesting = 0;
|
||||||
|
var in_destructuring = null;
|
||||||
|
var in_export;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (options.screw_ie8 && node instanceof AST_Catch) {
|
var create_a_block_scope =
|
||||||
|
(options.screw_ie8 && node instanceof AST_Catch) ||
|
||||||
|
((node instanceof AST_Block) && node.creates_block_scope());
|
||||||
|
if (create_a_block_scope) {
|
||||||
var save_scope = scope;
|
var save_scope = scope;
|
||||||
scope = new AST_Scope(node);
|
scope = new AST_Scope(node);
|
||||||
scope.init_scope_vars(nesting);
|
scope.init_scope_vars(nesting);
|
||||||
scope.parent_scope = save_scope;
|
scope.parent_scope = save_scope;
|
||||||
|
scope.is_block_scope = true;
|
||||||
|
if (!(node instanceof AST_Scope)) {
|
||||||
|
scope.uses_with = save_scope.uses_with;
|
||||||
|
scope.uses_eval = save_scope.uses_eval;
|
||||||
|
scope.directives = save_scope.directives;
|
||||||
|
}
|
||||||
descend();
|
descend();
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Destructuring && node.is_array === false) {
|
||||||
|
in_destructuring = node; // These don't nest
|
||||||
|
descend();
|
||||||
|
in_destructuring = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
node.init_scope_vars(nesting);
|
node.init_scope_vars(nesting);
|
||||||
var save_scope = node.parent_scope = scope;
|
var save_scope = node.parent_scope = scope;
|
||||||
|
|
@ -122,6 +147,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
labels = save_labels;
|
labels = save_labels;
|
||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Export) {
|
||||||
|
in_export = true;
|
||||||
|
descend();
|
||||||
|
in_export = false;
|
||||||
|
}
|
||||||
if (node instanceof AST_LabeledStatement) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
var l = node.label;
|
var l = node.label;
|
||||||
if (labels.has(l.name)) {
|
if (labels.has(l.name)) {
|
||||||
|
|
@ -140,12 +170,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_SymbolFunarg) {
|
||||||
|
node.object_destructuring_arg = !!in_destructuring;
|
||||||
|
defun.def_variable(node, in_export);
|
||||||
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
node.thedef = node;
|
node.thedef = node;
|
||||||
node.references = [];
|
node.references = [];
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolLambda) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
defun.def_function(node);
|
defun.def_function(node, in_export);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
|
|
@ -153,15 +187,32 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
// scope when we encounter the AST_Defun node (which is
|
// scope when we encounter the AST_Defun node (which is
|
||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = defun.parent_scope).def_function(node);
|
var parent_lambda = defun.parent_scope;
|
||||||
|
while (parent_lambda.is_block_scope) {
|
||||||
|
parent_lambda = parent_lambda.parent_scope;
|
||||||
|
}
|
||||||
|
(node.scope = parent_lambda).def_function(node, in_export);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_SymbolClass) {
|
||||||
|
defun.def_variable(node, in_export);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_SymbolImport) {
|
||||||
|
scope.def_variable(node, in_export);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_SymbolDefClass) {
|
||||||
|
// This deals with the name of the class being available
|
||||||
|
// inside the class.
|
||||||
|
(node.scope = defun.parent_scope).def_function(node, in_export);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_Var) {
|
else if (node instanceof AST_Var) {
|
||||||
last_var_had_const_pragma = node.has_const_pragma();
|
last_var_had_const_pragma = node.has_const_pragma();
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar
|
||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst
|
||||||
var def = defun.def_variable(node);
|
|| node instanceof AST_SymbolLet) {
|
||||||
|
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export);
|
||||||
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
|
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
|
||||||
|
def.destructuring = in_destructuring;
|
||||||
def.init = tw.parent().value;
|
def.init = tw.parent().value;
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
|
|
@ -182,6 +233,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
var func = null;
|
var func = null;
|
||||||
|
var cls = null;
|
||||||
var globals = self.globals = new Dictionary();
|
var globals = self.globals = new Dictionary();
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
|
|
@ -191,6 +243,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
func = prev_func;
|
func = prev_func;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Class) {
|
||||||
|
var prev_cls = cls;
|
||||||
|
cls = node;
|
||||||
|
descend();
|
||||||
|
cls = prev_cls;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_LoopControl && node.label) {
|
if (node instanceof AST_LoopControl && node.label) {
|
||||||
node.label.thedef.references.push(node);
|
node.label.thedef.references.push(node);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -242,6 +301,14 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||||
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Block.DEFMETHOD("creates_block_scope", function() {
|
||||||
|
return (
|
||||||
|
!(this instanceof AST_Lambda) &&
|
||||||
|
!(this instanceof AST_Toplevel) &&
|
||||||
|
!(this instanceof AST_Class)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
|
|
@ -269,16 +336,20 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export){
|
||||||
this.functions.set(symbol.name, this.def_variable(symbol));
|
this.functions.set(symbol.name, this.def_variable(symbol, in_export));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export){
|
||||||
var def;
|
var def;
|
||||||
if (!this.variables.has(symbol.name)) {
|
if (!this.variables.has(symbol.name)) {
|
||||||
def = new SymbolDef(this, this.variables.size(), symbol);
|
def = new SymbolDef(this, this.variables.size(), symbol);
|
||||||
this.variables.set(symbol.name, def);
|
this.variables.set(symbol.name, def);
|
||||||
def.global = !this.parent_scope;
|
def.object_destructuring_arg = symbol.object_destructuring_arg;
|
||||||
|
if (in_export) {
|
||||||
|
def.export = true;
|
||||||
|
}
|
||||||
|
def.global = !this.parent_scope && !(symbol instanceof AST_SymbolBlockDeclaration);
|
||||||
} else {
|
} else {
|
||||||
def = this.variables.get(symbol.name);
|
def = this.variables.get(symbol.name);
|
||||||
def.orig.push(symbol);
|
def.orig.push(symbol);
|
||||||
|
|
@ -331,7 +402,8 @@ AST_Scope.DEFMETHOD("references", function(sym){
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||||
return this.definition().unmangleable(options);
|
var def = this.definition();
|
||||||
|
return def && def.unmangleable(options);
|
||||||
});
|
});
|
||||||
|
|
||||||
// property accessors are not mangleable
|
// property accessors are not mangleable
|
||||||
|
|
@ -382,7 +454,8 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
sort : false, // Ignored. Flag retained for backwards compatibility.
|
sort : false, // Ignored. Flag retained for backwards compatibility.
|
||||||
toplevel : false,
|
toplevel : false,
|
||||||
screw_ie8 : true,
|
screw_ie8 : true,
|
||||||
keep_fnames : false
|
keep_fnames : false,
|
||||||
|
keep_classnames : false
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -431,13 +504,19 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
var mangle_with_block_scope =
|
||||||
|
(options.screw_ie8 && node instanceof AST_SymbolCatch) ||
|
||||||
|
node instanceof AST_SymbolBlockDeclaration;
|
||||||
|
if (mangle_with_block_scope) {
|
||||||
to_mangle.push(node.definition());
|
to_mangle.push(node.definition());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
to_mangle.forEach(function(def){
|
||||||
|
if (def.destructuring && !def.destructuring.is_array) return;
|
||||||
|
def.mangle(options);
|
||||||
|
});
|
||||||
|
|
||||||
if (options.cache) {
|
if (options.cache) {
|
||||||
options.cache.cname = this.cname;
|
options.cache.cname = this.cname;
|
||||||
|
|
@ -497,12 +576,16 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||||
base54.consider("new");
|
base54.consider("new");
|
||||||
else if (node instanceof AST_This)
|
else if (node instanceof AST_This)
|
||||||
base54.consider("this");
|
base54.consider("this");
|
||||||
|
else if (node instanceof AST_Super)
|
||||||
|
base54.consider("super");
|
||||||
else if (node instanceof AST_Try)
|
else if (node instanceof AST_Try)
|
||||||
base54.consider("try");
|
base54.consider("try");
|
||||||
else if (node instanceof AST_Catch)
|
else if (node instanceof AST_Catch)
|
||||||
base54.consider("catch");
|
base54.consider("catch");
|
||||||
else if (node instanceof AST_Finally)
|
else if (node instanceof AST_Finally)
|
||||||
base54.consider("finally");
|
base54.consider("finally");
|
||||||
|
else if (node instanceof AST_Yield)
|
||||||
|
base54.consider("yield");
|
||||||
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
||||||
base54.consider(node.name);
|
base54.consider(node.name);
|
||||||
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
||||||
|
|
|
||||||
|
|
@ -163,10 +163,18 @@ TreeTransformer.prototype = new TreeWalker;
|
||||||
if (self.value) self.value = self.value.transform(tw);
|
if (self.value) self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_(AST_Destructuring, function(self, tw) {
|
||||||
|
self.names = do_list(self.names, tw);
|
||||||
|
});
|
||||||
|
|
||||||
_(AST_Lambda, function(self, tw){
|
_(AST_Lambda, function(self, tw){
|
||||||
if (self.name) self.name = self.name.transform(tw);
|
if (self.name) self.name = self.name.transform(tw);
|
||||||
self.argnames = do_list(self.argnames, tw);
|
self.argnames = do_list(self.argnames, tw);
|
||||||
self.body = do_list(self.body, tw);
|
if (self.body instanceof AST_Node) {
|
||||||
|
self.body = self.body.transform(tw);
|
||||||
|
} else {
|
||||||
|
self.body = do_list(self.body, tw);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_(AST_Call, function(self, tw){
|
_(AST_Call, function(self, tw){
|
||||||
|
|
@ -188,6 +196,10 @@ TreeTransformer.prototype = new TreeWalker;
|
||||||
self.property = self.property.transform(tw);
|
self.property = self.property.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_(AST_Yield, function(self, tw){
|
||||||
|
if (self.expression) self.expression = self.expression.transform(tw);
|
||||||
|
});
|
||||||
|
|
||||||
_(AST_Unary, function(self, tw){
|
_(AST_Unary, function(self, tw){
|
||||||
self.expression = self.expression.transform(tw);
|
self.expression = self.expression.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
@ -215,4 +227,8 @@ TreeTransformer.prototype = new TreeWalker;
|
||||||
self.value = self.value.transform(tw);
|
self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_(AST_Expansion, function(self, tw){
|
||||||
|
self.expression = self.expression.transform(tw);
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -72,3 +72,76 @@ constant_join_2: {
|
||||||
var f = "strstr" + variable + "foobarmoo" + foo;
|
var f = "strstr" + variable + "foobarmoo" + foo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spread_with_variable_as_last_element: {
|
||||||
|
input: {
|
||||||
|
var values = [4, 5, 6];
|
||||||
|
var a = [1, 2, 3, ...values];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var values = [4, 5, 6];
|
||||||
|
var a = [1, 2, 3, ...values];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_variable_in_middle: {
|
||||||
|
input: {
|
||||||
|
var values = [4, 5, 6];
|
||||||
|
var a = [1, 2, 3, ...values, 7,,,];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var values = [4, 5, 6];
|
||||||
|
var a = [1, 2, 3, ...values, 7,,,];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_variable_at_front: {
|
||||||
|
input: {
|
||||||
|
var values = [1, 2, 3];
|
||||||
|
var a = [...values, 4, 5, 6];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var values = [1, 2, 3];
|
||||||
|
var a = [...values, 4, 5, 6];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_variable_at_front_after_elisions: {
|
||||||
|
input: {
|
||||||
|
var values = [1, 2, 3];
|
||||||
|
var a = [,,,...values, 4, 5, 6];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var values = [1, 2, 3];
|
||||||
|
var a = [,,,...values, 4, 5, 6];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_array_at_end: {
|
||||||
|
input: {
|
||||||
|
var a = [1, 2, ...[4, 5, 6]];
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = [1, 2, ...[4, 5, 6]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_logical_expression_at_end: {
|
||||||
|
options = { evaluate: true }
|
||||||
|
input: {
|
||||||
|
var a = [1, 2, 3, ...[2+2]]
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = [1, 2, 3, ...[4]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spread_with_logical_expression_at_middle: {
|
||||||
|
options = { evaluate: true }
|
||||||
|
input: {
|
||||||
|
var a = [1, 1, ...[1+1, 1+2, 2+3], 8]
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = [1, 1, ...[2, 3, 5], 8]
|
||||||
|
}
|
||||||
|
}
|
||||||
92
test/compress/arrow.js
Normal file
92
test/compress/arrow.js
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
arrow_functions_without_body: {
|
||||||
|
input: {
|
||||||
|
var a1 = () => 42;
|
||||||
|
var a2 = (p) => p;
|
||||||
|
var a3 = p => p;
|
||||||
|
var a4 = (...p) => p;
|
||||||
|
var a5 = (b, c) => b + c;
|
||||||
|
var a6 = (b, ...c) => b + c[0];
|
||||||
|
var a7 = (...b) => b.join();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a1 = () => 42;
|
||||||
|
var a2 = (p) => p;
|
||||||
|
var a3 = p => p;
|
||||||
|
var a4 = (...p) => p;
|
||||||
|
var a5 = (b, c) => b + c;
|
||||||
|
var a6 = (b, ...c) => b + c[0];
|
||||||
|
var a7 = (...b) => b.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_functions_with_body: {
|
||||||
|
input: {
|
||||||
|
var a1 = () => {
|
||||||
|
var a = 42 * Math.random();
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a2 = (p) => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a3 = p => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a4 = (...p) => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a5 = (b, c) => {
|
||||||
|
var result = b * c + b / c;
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
var a6 = (b, ...c) => {
|
||||||
|
var result = b;
|
||||||
|
for (var i = 0; i < c.length; i++)
|
||||||
|
result += c[i];
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
var a7 = (...b) => {
|
||||||
|
b.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a1 = () => {
|
||||||
|
var a = 42 * Math.random();
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a2 = (p) => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a3 = p => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a4 = (...p) => {
|
||||||
|
var a = Math.random() * p;
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var a5 = (b, c) => {
|
||||||
|
var result = b * c + b / c;
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
var a6 = (b, ...c) => {
|
||||||
|
var result = b;
|
||||||
|
for (var i = 0; i < c.length; i++)
|
||||||
|
result += c[i];
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
var a7 = (...b) => {
|
||||||
|
b.join();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_function_with_single_parameter_with_default: {
|
||||||
|
input: {
|
||||||
|
var foo = (a = 0) => doSomething(a);
|
||||||
|
}
|
||||||
|
expect_exact: "var foo=(a=0)=>doSomething(a);"
|
||||||
|
}
|
||||||
132
test/compress/block-scope.js
Normal file
132
test/compress/block-scope.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
|
||||||
|
let_statement: {
|
||||||
|
input: {
|
||||||
|
let x = 6;
|
||||||
|
}
|
||||||
|
expect_exact: "let x=6;"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_not_hoist_let: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
if (FOO) {
|
||||||
|
let let1;
|
||||||
|
let let2;
|
||||||
|
var var1;
|
||||||
|
var var2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x() {
|
||||||
|
var var1, var2;
|
||||||
|
if (FOO) {
|
||||||
|
let let1;
|
||||||
|
let let2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do_not_remove_anon_blocks_if_they_have_decls: {
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
{
|
||||||
|
let x;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const y;
|
||||||
|
class Zee {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let y;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x(){
|
||||||
|
{
|
||||||
|
let x
|
||||||
|
}
|
||||||
|
var x;
|
||||||
|
{
|
||||||
|
const y;
|
||||||
|
class Zee {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let y
|
||||||
|
}
|
||||||
|
var y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_unused_in_global_block: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
{
|
||||||
|
let x;
|
||||||
|
const y;
|
||||||
|
class Zee {};
|
||||||
|
var w;
|
||||||
|
}
|
||||||
|
let ex;
|
||||||
|
const why;
|
||||||
|
class Zed {};
|
||||||
|
var wut;
|
||||||
|
console.log(x, y, Zee);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var w;
|
||||||
|
var wut;
|
||||||
|
console.log(x, y, Zee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regression_block_scope_resolves: {
|
||||||
|
mangle = { };
|
||||||
|
options = {
|
||||||
|
dead_code: false
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
(function () {
|
||||||
|
if(1) {
|
||||||
|
let x;
|
||||||
|
const y;
|
||||||
|
class Zee {};
|
||||||
|
}
|
||||||
|
if(1) {
|
||||||
|
let ex;
|
||||||
|
const why;
|
||||||
|
class Zi {};
|
||||||
|
}
|
||||||
|
console.log(x, y, Zee, ex, why, Zi);
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function () {
|
||||||
|
if (1) {
|
||||||
|
let o;
|
||||||
|
const n;
|
||||||
|
class c {};
|
||||||
|
}
|
||||||
|
if (1) {
|
||||||
|
let o;
|
||||||
|
const n;
|
||||||
|
class c {};
|
||||||
|
}
|
||||||
|
console.log(x, y, Zee, ex, why, Zi);
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -88,6 +88,28 @@ dead_code_constant_boolean_should_warn_more: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dead_code_block_decls_die: {
|
||||||
|
options = {
|
||||||
|
dead_code : true,
|
||||||
|
conditionals : true,
|
||||||
|
booleans : true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (0) {
|
||||||
|
let foo = 6;
|
||||||
|
const bar = 12;
|
||||||
|
class Baz {};
|
||||||
|
var qux;
|
||||||
|
}
|
||||||
|
console.log(foo, bar, Baz);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var qux;
|
||||||
|
console.log(foo, bar, Baz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dead_code_const_declaration: {
|
dead_code_const_declaration: {
|
||||||
options = {
|
options = {
|
||||||
dead_code : true,
|
dead_code : true,
|
||||||
|
|
|
||||||
43
test/compress/destructuring.js
Normal file
43
test/compress/destructuring.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
destructuring_arrays: {
|
||||||
|
input: {
|
||||||
|
var [aa, bb] = cc;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var[aa,bb]=cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destructuring_objects: {
|
||||||
|
input: {
|
||||||
|
var {aa, bb} = {aa:1, bb:2};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var{aa,bb}={aa:1,bb:2};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nested_destructuring_objects: {
|
||||||
|
input: {
|
||||||
|
var [{a},b] = c;
|
||||||
|
}
|
||||||
|
expect_exact: 'var[{a},b]=c;';
|
||||||
|
}
|
||||||
|
|
||||||
|
destructuring_vardef_in_loops: {
|
||||||
|
input: {
|
||||||
|
for (var [x,y] in pairs);
|
||||||
|
for (var [a] = 0;;);
|
||||||
|
for (var {c} of cees);
|
||||||
|
}
|
||||||
|
expect_exact: "for(var[x,y]in pairs);for(var[a]=0;;);for(var{c}of cees);"
|
||||||
|
}
|
||||||
|
destructuring_expressions: {
|
||||||
|
input: {
|
||||||
|
({a, b});
|
||||||
|
[{a}];
|
||||||
|
f({x});
|
||||||
|
}
|
||||||
|
expect_exact: "({a,b});[{a}];f({x});"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +164,87 @@ used_var_in_catch: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unused_block_decls_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
let x = 10;
|
||||||
|
const y = 10;
|
||||||
|
class Zee {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
used_block_decls_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
let x = 10;
|
||||||
|
const y = 10;
|
||||||
|
class Zee {};
|
||||||
|
}
|
||||||
|
console.log(x, y, Zee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {}
|
||||||
|
console.log(x, y, Zee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_block_decls: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
{
|
||||||
|
const x;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let y;
|
||||||
|
}
|
||||||
|
console.log(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
console.log(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_keep_harmony_destructuring: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
var {x, y} = foo;
|
||||||
|
var a = foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
var {x, y} = foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
keep_fnames: {
|
keep_fnames: {
|
||||||
options = { unused: true, keep_fnames: true, unsafe: true };
|
options = { unused: true, keep_fnames: true, unsafe: true };
|
||||||
input: {
|
input: {
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,91 @@ positive_zero: {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pow: {
|
||||||
|
options = { evaluate: true }
|
||||||
|
input: {
|
||||||
|
var a = 5 ** 3;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 125;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_sequence: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 2 ** 3 ** 2;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 512;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_mixed: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 5 + 2 ** 3 + 5;
|
||||||
|
var b = 5 * 3 ** 2;
|
||||||
|
var c = 5 ** 3 * 2;
|
||||||
|
var d = 5 ** +3;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 18;
|
||||||
|
var b = 45;
|
||||||
|
var c = 250;
|
||||||
|
var d = 125;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_with_right_side_evaluating_to_unary: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = (4 - 7) ** foo;
|
||||||
|
var b = ++bar ** 3;
|
||||||
|
var c = --baz ** 2;
|
||||||
|
}
|
||||||
|
expect_exact: "var a=(-3)**foo;var b=++bar**3;var c=--baz**2;"
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_with_number_constants: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 5 ** NaN; /* NaN exponent results to NaN */
|
||||||
|
var b = 42 ** +0; /* +0 exponent results to NaN */
|
||||||
|
var c = 42 ** -0; /* -0 exponent results to NaN */
|
||||||
|
var d = NaN ** 1; /* NaN with non-zero exponent is NaN */
|
||||||
|
var e = 2 ** Infinity; /* abs(base) > 1 with Infinity as exponent is Infinity */
|
||||||
|
var f = 2 ** -Infinity; /* abs(base) > 1 with -Infinity as exponent is +0 */
|
||||||
|
var g = (-7) ** (0.5);
|
||||||
|
var h = 2324334 ** 34343443;
|
||||||
|
var i = (-2324334) ** 34343443;
|
||||||
|
var j = 2 ** (-3);
|
||||||
|
var k = 2.0 ** -3;
|
||||||
|
var l = 2.0 ** (5 - 7);
|
||||||
|
var m = 3 ** -10; // Result will be 0.000016935087808430286, which is too long
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = NaN;
|
||||||
|
var b = 1;
|
||||||
|
var c = 1;
|
||||||
|
var d = NaN;
|
||||||
|
var e = Infinity;
|
||||||
|
var f = 0;
|
||||||
|
var g = NaN;
|
||||||
|
var h = Infinity;
|
||||||
|
var i = -Infinity;
|
||||||
|
var j = .125;
|
||||||
|
var k = .125;
|
||||||
|
var l = .25;
|
||||||
|
var m = 3 ** -10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
17
test/compress/expansions.js
Normal file
17
test/compress/expansions.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
expand_arguments: {
|
||||||
|
input: {
|
||||||
|
func(a, ...rest);
|
||||||
|
func(...all);
|
||||||
|
}
|
||||||
|
expect_exact: "func(a,...rest);func(...all);"
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_parameters: {
|
||||||
|
input: {
|
||||||
|
(function (a, ...b){});
|
||||||
|
(function (...args){});
|
||||||
|
}
|
||||||
|
expect_exact: "(function(a,...b){});(function(...args){});"
|
||||||
|
}
|
||||||
|
|
||||||
52
test/compress/expression.js
Normal file
52
test/compress/expression.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
pow: {
|
||||||
|
input: {
|
||||||
|
var a = 2 ** 7;
|
||||||
|
var b = 3;
|
||||||
|
b **= 2;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 2 ** 7;
|
||||||
|
var b = 3;
|
||||||
|
b **= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_with_number_constants: {
|
||||||
|
input: {
|
||||||
|
var a = 5 ** NaN;
|
||||||
|
var b = 42 ** +0;
|
||||||
|
var c = 42 ** -0;
|
||||||
|
var d = NaN ** 1;
|
||||||
|
var e = 2 ** Infinity;
|
||||||
|
var f = 2 ** -Infinity;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 5 ** NaN;
|
||||||
|
var b = 42 ** +0;
|
||||||
|
var c = 42 ** -0;
|
||||||
|
var d = NaN ** 1;
|
||||||
|
var e = 2 ** (1/0);
|
||||||
|
var f = 2 ** -(1/0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_with_parentheses: {
|
||||||
|
input: {
|
||||||
|
var g = (-7) ** (0.5);
|
||||||
|
var h = 2324334 ** 34343443;
|
||||||
|
var i = (-2324334) ** 34343443;
|
||||||
|
var j = 2 ** (-3);
|
||||||
|
var k = 2.0 ** -3;
|
||||||
|
var l = 2.0 ** (5 - 7);
|
||||||
|
}
|
||||||
|
expect_exact: "var g=(-7)**.5;var h=2324334**34343443;var i=(-2324334)**34343443;var j=2**-3;var k=2**-3;var l=2**(5-7);"
|
||||||
|
}
|
||||||
|
|
||||||
|
pow_with_unary_between_brackets: {
|
||||||
|
input: {
|
||||||
|
var a = (-(+5)) ** 3;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = (-+5)**3;
|
||||||
|
}
|
||||||
|
}
|
||||||
527
test/compress/harmony.js
Normal file
527
test/compress/harmony.js
Normal file
|
|
@ -0,0 +1,527 @@
|
||||||
|
arrow_functions: {
|
||||||
|
input: {
|
||||||
|
(a) => b; // 1 args
|
||||||
|
(a, b) => c; // n args
|
||||||
|
() => b; // 0 args
|
||||||
|
(a) => (b) => c; // func returns func returns func
|
||||||
|
(a) => ((b) => c); // So these parens are dropped
|
||||||
|
() => (b,c) => d; // func returns func returns func
|
||||||
|
a=>{return b;}
|
||||||
|
a => 'lel'; // Dropping the parens
|
||||||
|
}
|
||||||
|
expect_exact: "a=>b;(a,b)=>c;()=>b;a=>b=>c;a=>b=>c;()=>(b,c)=>d;a=>{return b};a=>\"lel\";"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_function_parens: {
|
||||||
|
input: {
|
||||||
|
something && (() => {});
|
||||||
|
}
|
||||||
|
expect_exact: "something&&(()=>{});"
|
||||||
|
}
|
||||||
|
arrow_function_parens_2: {
|
||||||
|
input: {
|
||||||
|
(() => null)();
|
||||||
|
}
|
||||||
|
expect_exact: "(()=>null)();"
|
||||||
|
}
|
||||||
|
|
||||||
|
regression_arrow_functions_and_hoist: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true,
|
||||||
|
hoist_funs: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(a) => b;
|
||||||
|
}
|
||||||
|
expect_exact: "a=>b;"
|
||||||
|
}
|
||||||
|
|
||||||
|
regression_assign_arrow_functions: {
|
||||||
|
input: {
|
||||||
|
oninstall = e => false;
|
||||||
|
oninstall = () => false;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
oninstall=e=>false;
|
||||||
|
oninstall=()=>false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
computed_property_names: {
|
||||||
|
input: {
|
||||||
|
obj({ ["x" + "x"]: 6 });
|
||||||
|
}
|
||||||
|
expect_exact: 'obj({["x"+"x"]:6});'
|
||||||
|
}
|
||||||
|
|
||||||
|
shorthand_properties: {
|
||||||
|
mangle = true;
|
||||||
|
input: (function() {
|
||||||
|
var prop = 1;
|
||||||
|
const value = {prop};
|
||||||
|
return value;
|
||||||
|
})();
|
||||||
|
expect: (function() {
|
||||||
|
var n = 1;
|
||||||
|
const r = {prop:n};
|
||||||
|
return r;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_arrow_functions: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var foo = typeof (x) => null;
|
||||||
|
}
|
||||||
|
expect_exact: "var foo=\"function\";"
|
||||||
|
}
|
||||||
|
|
||||||
|
template_strings: {
|
||||||
|
input: {
|
||||||
|
``;
|
||||||
|
`xx\`x`;
|
||||||
|
`${ foo + 2 }`;
|
||||||
|
` foo ${ bar + `baz ${ qux }` }`;
|
||||||
|
}
|
||||||
|
expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;";
|
||||||
|
}
|
||||||
|
|
||||||
|
template_string_prefixes: {
|
||||||
|
input: {
|
||||||
|
String.raw`foo`;
|
||||||
|
foo `bar`;
|
||||||
|
}
|
||||||
|
expect_exact: "String.raw`foo`;foo`bar`;";
|
||||||
|
}
|
||||||
|
|
||||||
|
destructuring_arguments: {
|
||||||
|
input: {
|
||||||
|
(function ( a ) { });
|
||||||
|
(function ( [ a ] ) { });
|
||||||
|
(function ( [ a, b ] ) { });
|
||||||
|
(function ( [ [ a ] ] ) { });
|
||||||
|
(function ( [ [ a, b ] ] ) { });
|
||||||
|
(function ( [ a, [ b ] ] ) { });
|
||||||
|
(function ( [ [ b ], a ] ) { });
|
||||||
|
|
||||||
|
(function ( { a } ) { });
|
||||||
|
(function ( { a, b } ) { });
|
||||||
|
|
||||||
|
(function ( [ { a } ] ) { });
|
||||||
|
(function ( [ { a, b } ] ) { });
|
||||||
|
(function ( [ a, { b } ] ) { });
|
||||||
|
(function ( [ { b }, a ] ) { });
|
||||||
|
|
||||||
|
( [ a ] ) => { };
|
||||||
|
( [ a, b ] ) => { };
|
||||||
|
|
||||||
|
( { a } ) => { };
|
||||||
|
( { a, b, c, d, e } ) => { };
|
||||||
|
|
||||||
|
( [ a ] ) => b;
|
||||||
|
( [ a, b ] ) => c;
|
||||||
|
|
||||||
|
( { a } ) => b;
|
||||||
|
( { a, b } ) => c;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a){});
|
||||||
|
(function([a]){});
|
||||||
|
(function([a,b]){});
|
||||||
|
(function([[a]]){});
|
||||||
|
(function([[a,b]]){});
|
||||||
|
(function([a,[b]]){});
|
||||||
|
(function([[b],a]){});
|
||||||
|
|
||||||
|
(function({a}){});
|
||||||
|
(function({a,b}){});
|
||||||
|
|
||||||
|
(function([{a}]){});
|
||||||
|
(function([{a,b}]){});
|
||||||
|
(function([a,{b}]){});
|
||||||
|
(function([{b},a]){});
|
||||||
|
|
||||||
|
([a])=>{};
|
||||||
|
([a,b])=>{};
|
||||||
|
|
||||||
|
({a})=>{};
|
||||||
|
({a,b,c,d,e})=>{};
|
||||||
|
|
||||||
|
([a])=>b;
|
||||||
|
([a,b])=>c;
|
||||||
|
|
||||||
|
({a})=>b;
|
||||||
|
({a,b})=>c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_arguments: {
|
||||||
|
input: {
|
||||||
|
function x(a = 6) { }
|
||||||
|
function x(a = (6 + 5)) { }
|
||||||
|
function x({ foo } = {}, [ bar ] = [ 1 ]) { }
|
||||||
|
}
|
||||||
|
expect_exact: "function x(a=6){}function x(a=6+5){}function x({foo}={},[bar]=[1]){}"
|
||||||
|
}
|
||||||
|
|
||||||
|
default_values_in_destructurings: {
|
||||||
|
input: {
|
||||||
|
function x({a=(4), b}) {}
|
||||||
|
function x([b, c=(12)]) {}
|
||||||
|
var { x = (6), y } = x;
|
||||||
|
var [ x, y = (6) ] = x;
|
||||||
|
}
|
||||||
|
expect_exact: "function x({a=4,b}){}function x([b,c=12]){}var{x=6,y}=x;var[x,y=6]=x;"
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_methods: {
|
||||||
|
input: {
|
||||||
|
x = {
|
||||||
|
foo(a, b) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = {
|
||||||
|
foo([{a}]) {
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
bar(){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "x={foo(a,b){return x}};y={foo([{a}]){return a},bar(){}};"
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_methods_with_computed_property: {
|
||||||
|
options = {
|
||||||
|
evaluate: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var foo = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return { /* stuff */ }
|
||||||
|
},
|
||||||
|
[1 + 2]() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var foo = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return { /* stuff */ }
|
||||||
|
},
|
||||||
|
[3]() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_methods_and_mangle_props: {
|
||||||
|
mangle_props = {
|
||||||
|
regex: /_/
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
obj = {
|
||||||
|
_foo() { return 1; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x() {
|
||||||
|
obj = {
|
||||||
|
a() { return 1; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_generators: {
|
||||||
|
input: {
|
||||||
|
x = {
|
||||||
|
*foo(a, b) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = {
|
||||||
|
*foo([{a}]) {
|
||||||
|
yield a;
|
||||||
|
},
|
||||||
|
bar(){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "x={*foo(a,b){return x}};y={*foo([{a}]){yield a},bar(){}};"
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_methods_and_keyword_names: {
|
||||||
|
input: {
|
||||||
|
x = {
|
||||||
|
catch() {},
|
||||||
|
throw() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x={catch(){},throw(){}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes: {
|
||||||
|
input: {
|
||||||
|
class SomeClass {
|
||||||
|
constructor() {
|
||||||
|
};
|
||||||
|
foo() {};
|
||||||
|
};
|
||||||
|
class NoSemi {
|
||||||
|
constructor(...args) {
|
||||||
|
}
|
||||||
|
foo() {}
|
||||||
|
};
|
||||||
|
class ChildClass extends SomeClass {};
|
||||||
|
var asExpression = class AsExpression {};
|
||||||
|
var nameless = class {};
|
||||||
|
}
|
||||||
|
expect_exact: "class SomeClass{constructor(){}foo(){}}class NoSemi{constructor(...args){}foo(){}}class ChildClass extends SomeClass{}var asExpression=class AsExpression{};var nameless=class{};"
|
||||||
|
}
|
||||||
|
|
||||||
|
getter_setter_with_computed_value: {
|
||||||
|
input: {
|
||||||
|
class C {
|
||||||
|
get ['a']() {
|
||||||
|
return 'A';
|
||||||
|
}
|
||||||
|
set ['a'](value) {
|
||||||
|
do_something(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
class C {
|
||||||
|
get ['a']() {
|
||||||
|
return 'A';
|
||||||
|
}
|
||||||
|
set ['a'](value) {
|
||||||
|
do_something(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class_statics: {
|
||||||
|
input: {
|
||||||
|
x = class {
|
||||||
|
static staticMethod() {}
|
||||||
|
static get foo() {}
|
||||||
|
static set bar() {}
|
||||||
|
static() { /* "static" can be a method name! */ }
|
||||||
|
get() { /* "get" can be a method name! */ }
|
||||||
|
set() { /* "set" can be a method name! */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "x=class{static staticMethod(){}static get foo(){}static set bar(){}static(){}get(){}set(){}};"
|
||||||
|
}
|
||||||
|
|
||||||
|
class_name_can_be_mangled: {
|
||||||
|
mangle = { };
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
class Foo {
|
||||||
|
}
|
||||||
|
var class1 = Foo
|
||||||
|
var class2 = class Bar {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x() {
|
||||||
|
class a { }
|
||||||
|
var n = a
|
||||||
|
var r = class a {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class_name_can_be_preserved: {
|
||||||
|
mangle = {
|
||||||
|
keep_classnames: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
(class Baz { });
|
||||||
|
class Foo {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x() {
|
||||||
|
(class Baz { });
|
||||||
|
class Foo {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes_can_have_generators: {
|
||||||
|
input: {
|
||||||
|
class Foo {
|
||||||
|
*bar() {}
|
||||||
|
static *baz() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
class Foo {
|
||||||
|
*bar() {}
|
||||||
|
static *baz() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes_can_have_computed_generators: {
|
||||||
|
input: {
|
||||||
|
class C4 {
|
||||||
|
*['constructor']() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
class C4 {
|
||||||
|
*['constructor']() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classes_can_have_computed_static: {
|
||||||
|
input: {
|
||||||
|
class C4 {
|
||||||
|
static ['constructor']() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
class C4 {
|
||||||
|
static ['constructor']() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_target: {
|
||||||
|
input: {
|
||||||
|
new.target;
|
||||||
|
new.target.name;
|
||||||
|
}
|
||||||
|
expect_exact: "new.target;new.target.name;"
|
||||||
|
}
|
||||||
|
|
||||||
|
number_literals: {
|
||||||
|
input: {
|
||||||
|
0b1001;
|
||||||
|
0B1001;
|
||||||
|
0o11;
|
||||||
|
0O11;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect: {
|
||||||
|
9;
|
||||||
|
9;
|
||||||
|
9;
|
||||||
|
9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import_statement: {
|
||||||
|
input: {
|
||||||
|
import "mod-name";
|
||||||
|
import Foo from "bar";
|
||||||
|
import { Bar, Baz } from 'lel';
|
||||||
|
import Bar, { Foo } from 'lel';
|
||||||
|
import { Bar as kex, Baz as food } from 'lel';
|
||||||
|
}
|
||||||
|
expect_exact: "import\"mod-name\";import Foo from\"bar\";import{Bar,Baz}from\"lel\";import Bar,{Foo}from\"lel\";import{Bar as kex,Baz as food}from\"lel\";"
|
||||||
|
}
|
||||||
|
|
||||||
|
export_statement: {
|
||||||
|
input: {
|
||||||
|
export default 1;
|
||||||
|
export var foo = 4;
|
||||||
|
export let foo = 6;
|
||||||
|
export const foo = 6;
|
||||||
|
export function foo() {};
|
||||||
|
export class foo { };
|
||||||
|
}
|
||||||
|
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
|
||||||
|
}
|
||||||
|
|
||||||
|
import_statement_mangling: {
|
||||||
|
mangle = { };
|
||||||
|
input: {
|
||||||
|
import Foo from "foo";
|
||||||
|
import Bar, {Food} from "lel";
|
||||||
|
import {What as Whatever} from "lel";
|
||||||
|
Foo();
|
||||||
|
Bar();
|
||||||
|
Food();
|
||||||
|
Whatever();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
import l from "foo";
|
||||||
|
import e, {Food as o} from "lel";
|
||||||
|
import {What as f} from "lel";
|
||||||
|
l();
|
||||||
|
e();
|
||||||
|
o();
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export_statement_mangling: {
|
||||||
|
mangle = { };
|
||||||
|
input: {
|
||||||
|
export var foo = 6;
|
||||||
|
export function bar() { }
|
||||||
|
export class Baz { }
|
||||||
|
bar(foo, Baz)
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
export var foo = 6;
|
||||||
|
export function bar() { }
|
||||||
|
export class Baz { }
|
||||||
|
bar(foo, Baz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/1021
|
||||||
|
regression_for_of_const: {
|
||||||
|
input: {
|
||||||
|
for (const x of y) {}
|
||||||
|
for (const x in y) {}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (const x of y);for (const x in y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fabio: My patches accidentally caused a crash whenever
|
||||||
|
// there's an extraneous set of parens around an object.
|
||||||
|
regression_cannot_destructure: {
|
||||||
|
input: {
|
||||||
|
var x = ({ x : 3 });
|
||||||
|
x(({ x: 3 }));
|
||||||
|
}
|
||||||
|
expect_exact: "var x={x:3};x({x:3});";
|
||||||
|
}
|
||||||
|
|
||||||
|
regression_cannot_use_of: {
|
||||||
|
input: {
|
||||||
|
function of() {
|
||||||
|
}
|
||||||
|
var of = "is a valid variable name";
|
||||||
|
of = { of: "is ok" };
|
||||||
|
x.of;
|
||||||
|
of: foo()
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function of(){}
|
||||||
|
var of="is a valid variable name";
|
||||||
|
of={of:"is ok"};
|
||||||
|
x.of;
|
||||||
|
foo(); /* Label statement missing? No prob. */
|
||||||
|
}
|
||||||
|
}
|
||||||
85
test/compress/hoist.js
Normal file
85
test/compress/hoist.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
hoist_vars: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function a() {
|
||||||
|
bar();
|
||||||
|
var var1;
|
||||||
|
var var2;
|
||||||
|
}
|
||||||
|
function b(anArg) {
|
||||||
|
bar();
|
||||||
|
var var1;
|
||||||
|
var anArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a() {
|
||||||
|
var var1, var2; // Vars go up and are joined
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
function b(anArg) {
|
||||||
|
var var1;
|
||||||
|
bar();
|
||||||
|
// But vars named like arguments go away!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hoist_funs: {
|
||||||
|
options = {
|
||||||
|
hoist_funs: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function a() {
|
||||||
|
bar();
|
||||||
|
function foo() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a() {
|
||||||
|
function foo() {} // Funs go up
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hoist_no_destructurings: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true,
|
||||||
|
hoist_funs: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function a([anArg]) {
|
||||||
|
bar();
|
||||||
|
var var1;
|
||||||
|
var anArg; // Because anArg is already declared, this goes away!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a([anArg]) {
|
||||||
|
var var1;
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dont_hoist_var_destructurings: {
|
||||||
|
options = {
|
||||||
|
hoist_vars: true,
|
||||||
|
hoist_funs: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function x() {
|
||||||
|
// If foo is null or undefined, this should be an exception
|
||||||
|
var {x,y} = foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function x() {
|
||||||
|
var {x,y} = foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
test/compress/issue-1001.js
Normal file
8
test/compress/issue-1001.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
parenthesis_strings_in_parenthesis: {
|
||||||
|
input: {
|
||||||
|
var foo = ('(');
|
||||||
|
a(')');
|
||||||
|
|
||||||
|
}
|
||||||
|
expect_exact: 'var foo="(";a(")");'
|
||||||
|
}
|
||||||
30
test/compress/issue-1043.js
Normal file
30
test/compress/issue-1043.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
issue_1043: {
|
||||||
|
options = {
|
||||||
|
side_effects: true
|
||||||
|
};
|
||||||
|
|
||||||
|
input: {
|
||||||
|
function* range(start = 0, end = null, step = 1) {
|
||||||
|
if (end == null) {
|
||||||
|
end = start;
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = start; i < end; i += step) {
|
||||||
|
yield i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect: {
|
||||||
|
function* range(start = 0, end = null, step = 1) {
|
||||||
|
if (null == end) {
|
||||||
|
end = start;
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = start; i < end; i += step)
|
||||||
|
yield i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
test/compress/issue-1044.js
Normal file
9
test/compress/issue-1044.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
issue_1044: {
|
||||||
|
options = { evaluate: true, conditionals: true };
|
||||||
|
input: {
|
||||||
|
const mixed = Base ? class extends Base {} : class {}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
const mixed = Base ? class extends Base {} : class {}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
test/compress/issue-203.js
Normal file
30
test/compress/issue-203.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
compress_new_function: {
|
||||||
|
options = {
|
||||||
|
unsafe: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
new Function("aa, bb", 'return aa;');
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
Function("a", "b", "return a");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compress_new_function_with_destruct: {
|
||||||
|
options = {
|
||||||
|
unsafe: true
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
new Function("aa, [bb]", 'return aa;');
|
||||||
|
new Function("aa, {bb}", 'return aa;');
|
||||||
|
new Function("[[aa]], [{bb}]", 'return aa;');
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
Function("a", "[b]", "return a");
|
||||||
|
Function("a", "{bb}", "return a");
|
||||||
|
Function("[[a]]", "[{bb}]", 'return a');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
9
test/compress/issue-926.js
Normal file
9
test/compress/issue-926.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
template_strings: {
|
||||||
|
input: {
|
||||||
|
foo(
|
||||||
|
`<span>${contents}</span>`,
|
||||||
|
`<a href="${url}">${text}</a>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect_exact: "foo(`<span>${contents}</span>`,`<a href=\"${url}\">${text}</a>`);"
|
||||||
|
}
|
||||||
40
test/compress/join-vars.js
Normal file
40
test/compress/join-vars.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
only_vars: {
|
||||||
|
options = { join_vars: true };
|
||||||
|
input: {
|
||||||
|
let netmaskBinary = '';
|
||||||
|
for (let i = 0; i < netmaskBits; ++i) {
|
||||||
|
netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
let netmaskBinary = '';
|
||||||
|
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1079_with_vars: {
|
||||||
|
options = { join_vars: true };
|
||||||
|
input: {
|
||||||
|
var netmaskBinary = '';
|
||||||
|
for (var i = 0; i < netmaskBits; ++i) {
|
||||||
|
netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (var netmaskBinary = '', i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1079_with_mixed: {
|
||||||
|
options = { join_vars: true };
|
||||||
|
input: {
|
||||||
|
var netmaskBinary = '';
|
||||||
|
for (let i = 0; i < netmaskBits; ++i) {
|
||||||
|
netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var netmaskBinary = ''
|
||||||
|
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
9
test/compress/super.js
Normal file
9
test/compress/super.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
super_can_be_parsed: {
|
||||||
|
input: {
|
||||||
|
super(1,2);
|
||||||
|
super.meth();
|
||||||
|
}
|
||||||
|
expect_exact: "super(1,2);super.meth();"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -15,3 +15,88 @@ unicode_parse_variables: {
|
||||||
var l০ = 3;
|
var l০ = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unicode_escaped_identifier: {
|
||||||
|
beautify = {ecma: 6}
|
||||||
|
input: {
|
||||||
|
var \u{61} = "foo";
|
||||||
|
var \u{10000} = "bar";
|
||||||
|
}
|
||||||
|
expect_exact: 'var a="foo";var \u{10000}="bar";';
|
||||||
|
}
|
||||||
|
|
||||||
|
unicode_identifier_ascii_only: {
|
||||||
|
beautify = {ascii_only: true, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var \u{0061} = "hi";
|
||||||
|
var bar = "h\u{0065}llo";
|
||||||
|
var \u{10000} = "testing \u{101111}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var a="hi";var bar="hello";var \\u{10000}="testing \\u{101111}";'
|
||||||
|
}
|
||||||
|
|
||||||
|
unicode_string_literals: {
|
||||||
|
beautify = {ascii_only: true, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var a = "6 length unicode character: \u{101111}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var a="6 length unicode character: \\u{101111}";'
|
||||||
|
}
|
||||||
|
|
||||||
|
unicode_output_es5_surrogates: {
|
||||||
|
beautify = {ascii_only: true, ecma: 5}
|
||||||
|
input: {
|
||||||
|
var \u{10000} = "6 length unicode character: \u{10FFFF}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var \\ud800\\udc00="6 length unicode character: \\udbff\\udfff";'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_escape_style: {
|
||||||
|
beautify = {ascii_only: true, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var a = "\x01";
|
||||||
|
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
|
||||||
|
var \u0100 = "\u0100";
|
||||||
|
var \u1000 = "\u1000";
|
||||||
|
var \u{10000} = "\u{10000}";
|
||||||
|
var \u{2f800} = "\u{100000}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u{10000}="\\u{10000}";var \\u{2f800}="\\u{100000}";'
|
||||||
|
}
|
||||||
|
|
||||||
|
check_escape_style_es5: {
|
||||||
|
beautify = {ascii_only: true, ecma: 5}
|
||||||
|
input: {
|
||||||
|
var a = "\x01";
|
||||||
|
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
|
||||||
|
var \u0100 = "\u0100";
|
||||||
|
var \u1000 = "\u1000";
|
||||||
|
var \u{10000} = "\u{10000}";
|
||||||
|
var \u{2f800} = "\u{100000}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\ud800\\udc00="\\ud800\\udc00";var \\ud87e\\udc00="\\udbc0\\udc00";'
|
||||||
|
}
|
||||||
|
|
||||||
|
ID_continue_with_surrogate_pair: {
|
||||||
|
beautify = {ascii_only: true, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var \u{2f800}\u{2f800}\u{2f800}\u{2f800} = "\u{100000}\u{100000}\u{100000}\u{100000}\u{100000}";
|
||||||
|
}
|
||||||
|
expect_exact: 'var \\u{2f800}\\u{2f800}\\u{2f800}\\u{2f800}="\\u{100000}\\u{100000}\\u{100000}\\u{100000}\\u{100000}";'
|
||||||
|
}
|
||||||
|
|
||||||
|
escape_non_escaped_identifier: {
|
||||||
|
beautify = {ascii_only: true, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var µþ = "µþ";
|
||||||
|
}
|
||||||
|
expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";'
|
||||||
|
}
|
||||||
|
|
||||||
|
non_escape_2_non_escape: {
|
||||||
|
beautify = {ascii_only: false, ecma: 6}
|
||||||
|
input: {
|
||||||
|
var µþ = "µþ";
|
||||||
|
}
|
||||||
|
expect_exact: 'var µþ="µþ";'
|
||||||
|
}
|
||||||
142
test/compress/yield.js
Normal file
142
test/compress/yield.js
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
generators: {
|
||||||
|
input: {
|
||||||
|
function* fn() {};
|
||||||
|
}
|
||||||
|
expect_exact: "function*fn(){}"
|
||||||
|
}
|
||||||
|
|
||||||
|
generators_yield: {
|
||||||
|
input: {
|
||||||
|
function* fn() {
|
||||||
|
yield remote();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function*fn(){yield remote()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
generators_yield_assign: {
|
||||||
|
input: {
|
||||||
|
function* fn() {
|
||||||
|
var x = {};
|
||||||
|
x.prop = yield 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
|
||||||
|
}
|
||||||
|
|
||||||
|
generator_yield_undefined: {
|
||||||
|
input: {
|
||||||
|
function* fn() {
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function*fn(){yield}"
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_optimize_expression: {
|
||||||
|
options = {
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function* f1() { yield; }
|
||||||
|
function* f2() { yield undefined; }
|
||||||
|
function* f3() { yield null; }
|
||||||
|
function* f4() { yield* undefined; }
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function* f1() { yield }
|
||||||
|
function* f2() { yield; }
|
||||||
|
function* f3() { yield null; }
|
||||||
|
function* f4() { yield* void 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_statements: {
|
||||||
|
input: {
|
||||||
|
function* fn() {
|
||||||
|
var a = (yield 1) + (yield 2);
|
||||||
|
var b = (yield 3) === (yield 4);
|
||||||
|
var c = (yield 5) << (yield 6);
|
||||||
|
var d = yield 7;
|
||||||
|
var e = (yield 8) ? yield 9 : yield 10;
|
||||||
|
var f = -(yield 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_as_identifier_in_function_in_generator: {
|
||||||
|
input: {
|
||||||
|
var g = function*() {
|
||||||
|
function h() {
|
||||||
|
yield = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var g = function*() {
|
||||||
|
function h() {
|
||||||
|
yield = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_before_punctuators: {
|
||||||
|
input: {
|
||||||
|
iter = (function*() {
|
||||||
|
assignmentResult = [ x = yield ] = value;
|
||||||
|
})();
|
||||||
|
function* g1() { (yield) }
|
||||||
|
function* g2() { [yield] }
|
||||||
|
function* g3() { return {yield} } // Added return to avoid {} drop
|
||||||
|
function* g4() { yield, yield; }
|
||||||
|
function* g5() { (yield) ? yield : yield; }
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
iter = (function*() {
|
||||||
|
assignmentResult = [ x = yield ] = value;
|
||||||
|
})();
|
||||||
|
function* g1() { (yield) }
|
||||||
|
function* g2() { [yield] }
|
||||||
|
function* g3() { return {yield} }
|
||||||
|
function* g4() { yield, yield; }
|
||||||
|
function* g5() { (yield) ? yield : yield; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_as_identifier_outside_strict_mode: {
|
||||||
|
input: {
|
||||||
|
import yield from "bar";
|
||||||
|
yield = 123;
|
||||||
|
while (true) {
|
||||||
|
yield:
|
||||||
|
for(;;) break yield;
|
||||||
|
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
yield: for(;;) continue yield;
|
||||||
|
function yield(){}
|
||||||
|
function foo(...yield){}
|
||||||
|
try { new Error("") } catch (yield) {}
|
||||||
|
var yield = "foo";
|
||||||
|
class yield {}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
import yield from "bar";
|
||||||
|
yield = 123;
|
||||||
|
while (true) {
|
||||||
|
yield:
|
||||||
|
for(;;) break yield;
|
||||||
|
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
while (true)
|
||||||
|
yield: for(;;) continue yield;
|
||||||
|
function yield(){}
|
||||||
|
function foo(...yield){}
|
||||||
|
try { new Error("") } catch (yield) {}
|
||||||
|
var yield = "foo";
|
||||||
|
class yield {}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
test/mocha/arrow.js
Normal file
25
test/mocha/arrow.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Arrow functions", function() {
|
||||||
|
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {
|
||||||
|
var tests = [
|
||||||
|
"var a = ...a => {return a.join()}",
|
||||||
|
"var b = (a, ...b, c) => { return a + b.join() + c}",
|
||||||
|
"var c = (...a, b) => a.join()"
|
||||||
|
];
|
||||||
|
var test = function(code) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(code, {fromString: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var error = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(test(tests[i]), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
26
test/mocha/builtins.js
Normal file
26
test/mocha/builtins.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
var UglifyJS = require("../../");
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("builtins", function() {
|
||||||
|
it ("Should not mangle builtins", function() {
|
||||||
|
var test = "function foo(something){\n" +
|
||||||
|
" return [Object,Array,Function,Number,String,Boolean,Error,Math,Date,RegExp,Symbol,something];\n" +
|
||||||
|
"};";
|
||||||
|
|
||||||
|
var result = UglifyJS.minify(test, {fromString: true, parse: {bare_returns: true}}).code;
|
||||||
|
|
||||||
|
assert.strictEqual(result.indexOf("something"), -1);
|
||||||
|
|
||||||
|
assert.notEqual(result.indexOf("Object"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Array"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Function"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Number"), -1);
|
||||||
|
assert.notEqual(result.indexOf("String"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Boolean"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Error"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Math"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Date"), -1);
|
||||||
|
assert.notEqual(result.indexOf("RegExp"), -1);
|
||||||
|
assert.notEqual(result.indexOf("Symbol"), -1);
|
||||||
|
});
|
||||||
|
});
|
||||||
26
test/mocha/class.js
Normal file
26
test/mocha/class.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Class", function() {
|
||||||
|
it("Should not accept spread on non-last parameters in methods", function() {
|
||||||
|
var tests = [
|
||||||
|
"class foo { bar(...a, b) { return a.join(b) } }",
|
||||||
|
"class foo { bar(a, b, ...c, d) { return c.join(a + b) + d } }",
|
||||||
|
"class foo { *bar(...a, b) { return a.join(b) } }",
|
||||||
|
"class foo { *bar(a, b, ...c, d) { return c.join(a + b) + d } }"
|
||||||
|
];
|
||||||
|
var test = function(code) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(code, {fromString: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var error = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(test(tests[i]), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
32
test/mocha/expression.js
Normal file
32
test/mocha/expression.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Expression", function() {
|
||||||
|
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {
|
||||||
|
var tests = [
|
||||||
|
"+5 ** 3",
|
||||||
|
"-5 ** 3",
|
||||||
|
"~5 ** 3",
|
||||||
|
"!5 ** 3",
|
||||||
|
"void 5 ** 3",
|
||||||
|
"typeof 5 ** 3",
|
||||||
|
"delete 5 ** 3",
|
||||||
|
"var a = -(5) ** 3;"
|
||||||
|
];
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error &&
|
||||||
|
/^SyntaxError: Unexpected token: operator \((?:[!+~-]|void|typeof|delete)\)/.test(e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail, tests[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
30
test/mocha/function.js
Normal file
30
test/mocha/function.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Function", function() {
|
||||||
|
it("Should not accept spread on non-last parameters", function() {
|
||||||
|
var tests = [
|
||||||
|
"var a = function(...a, b) { return a.join(b) }",
|
||||||
|
"var b = function(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||||
|
"function foo(...a, b) { return a.join(b) }",
|
||||||
|
"function bar(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||||
|
"var a = function*(...a, b) { return a.join(b) }",
|
||||||
|
"var b = function*(a, b, ...c, d) { return c.join(a + b) + d }",
|
||||||
|
"function* foo(...a, b) { return a.join(b) }",
|
||||||
|
"function* bar(a, b, ...c, d) { return c.join(a + b) + d }"
|
||||||
|
];
|
||||||
|
var test = function(code) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(code, {fromString: true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var error = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected token: expand (...)";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(test(tests[i]), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -85,4 +85,11 @@ describe("New", function() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should check target in new.target", function() {
|
||||||
|
assert.throws(function() {uglify.parse("new.blah")}, function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: Unexpected token name «blah», expected name «target»";
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
20
test/mocha/object.js
Normal file
20
test/mocha/object.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
var Uglify = require("../../");
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("Object", function() {
|
||||||
|
it ("Should allow objects to have a methodDefinition as property", function() {
|
||||||
|
var code = "var a = {test() {return true;}}";
|
||||||
|
assert.equal(Uglify.minify(code, {fromString: true}).code, "var a={test(){return!0}};");
|
||||||
|
});
|
||||||
|
|
||||||
|
it ("Should not allow objects to use static keywords like in classes", function() {
|
||||||
|
var code = "{static test() {}}";
|
||||||
|
var parse = function() {
|
||||||
|
Uglify.parse(code);
|
||||||
|
}
|
||||||
|
var expect = function(e) {
|
||||||
|
return e instanceof Uglify.JS_Parse_Error;
|
||||||
|
}
|
||||||
|
assert.throws(parse, expect);
|
||||||
|
});
|
||||||
|
});
|
||||||
134
test/mocha/unicode.js
Normal file
134
test/mocha/unicode.js
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
var assert = require("assert");
|
||||||
|
var uglify = require("../../");
|
||||||
|
|
||||||
|
describe("Unicode", function() {
|
||||||
|
it("Should not accept invalid code ranges in unicode escape", function() {
|
||||||
|
var tests = [
|
||||||
|
"\\u{110000}", // A bit over the unicode range
|
||||||
|
"\\u{100000061} = 'foo'", // 32-bit overflow resulting in "a"
|
||||||
|
"\\u{fffffffffff}", // A bit too much over the unicode range
|
||||||
|
];
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: Unicode reference out of bounce";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not accept invalid unicode sequences", function() {
|
||||||
|
var tests = [
|
||||||
|
"var foo = '\\u-111'",
|
||||||
|
"var bar = '\\u{-1}'",
|
||||||
|
"var baz = '\\ugggg'"
|
||||||
|
];
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: Invalid hex-character pattern in string";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should throw error if escaped first identifier char is not part of ID_start", function() {
|
||||||
|
var tests = [
|
||||||
|
'var \\u{0} = "foo";',
|
||||||
|
'var \\u{10ffff} = "bar";',
|
||||||
|
'var \\u000a = "what\'s up";'
|
||||||
|
];
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: First identifier char is an invalid identifier char";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should throw error if escaped non-first identifier char is not part of ID_start", function() {
|
||||||
|
var tests = [
|
||||||
|
'var a\\u{0} = "foo";',
|
||||||
|
'var a\\u{10ffff} = "bar";',
|
||||||
|
'var z\\u000a = "what\'s up";'
|
||||||
|
];
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: Invalid escaped identifier char";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should throw error if identifier is a keyword with a escape sequences", function() {
|
||||||
|
var tests = [
|
||||||
|
'var \\u0069\\u006e = "foo"', // in
|
||||||
|
'var \\u0076\\u0061\\u0072 = "bar"', // var
|
||||||
|
'var \\u{66}\\u{6f}\\u{72} = "baz"', // for
|
||||||
|
'var \\u0069\\u{66} = "foobar"', // if
|
||||||
|
'var \\u{73}uper' // super
|
||||||
|
];
|
||||||
|
|
||||||
|
var exec = function(test) {
|
||||||
|
return function() {
|
||||||
|
uglify.parse(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof uglify.JS_Parse_Error
|
||||||
|
&& e.message === "SyntaxError: Escaped characters are not allowed in keywords";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(exec(tests[i]), fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should read strings containing surigates correctly", function() {
|
||||||
|
var tests = [
|
||||||
|
['var a = "\ud800\udc00";', 'var a="\\u{10000}";'],
|
||||||
|
['var b = "\udbff\udfff";', 'var b="\\u{10ffff}";']
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.strictEqual(uglify.minify(tests[i][0], {
|
||||||
|
fromString: true, output: { ascii_only: true, ecma: 6}
|
||||||
|
}).code, tests[i][1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
106
test/mocha/yield.js
Normal file
106
test/mocha/yield.js
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
var UglifyJS = require("../../");
|
||||||
|
var assert = require("assert");
|
||||||
|
|
||||||
|
describe("Yield", function() {
|
||||||
|
it("Should not delete statements after yield", function() {
|
||||||
|
var js = 'function *foo(bar) { yield 1; yield 2; return 3; }';
|
||||||
|
var result = UglifyJS.minify(js, {fromString: true});
|
||||||
|
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not allow yield as labelIdentifier within generators", function() {
|
||||||
|
var js = "function* g() {yield: 1}"
|
||||||
|
var test = function() {
|
||||||
|
UglifyJS.parse(js);
|
||||||
|
}
|
||||||
|
var expect = function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Yield cannot be used as label inside generators";
|
||||||
|
}
|
||||||
|
assert.throws(test, expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not allow yield* followed by a semicolon in generators", function() {
|
||||||
|
var js = "function* test() {yield*\n;}";
|
||||||
|
var test = function() {
|
||||||
|
UglifyJS.parse(js);
|
||||||
|
}
|
||||||
|
var expect = function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected token: punc (;)";
|
||||||
|
}
|
||||||
|
assert.throws(test, expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not allow yield with next token star on next line", function() {
|
||||||
|
var js = "function* test() {yield\n*123;}";
|
||||||
|
var test = function() {
|
||||||
|
UglifyJS.parse(js);
|
||||||
|
}
|
||||||
|
var expect = function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected token: operator (*)";
|
||||||
|
}
|
||||||
|
assert.throws(test, expect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to compress its expression", function() {
|
||||||
|
assert.strictEqual(
|
||||||
|
UglifyJS.minify("function *f() { yield 3-4; }", {fromString: true, compress: true}).code,
|
||||||
|
"function*f(){yield-1}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should keep undefined after yield without compression if found in ast", function() {
|
||||||
|
assert.strictEqual(
|
||||||
|
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {fromString: true, compress: false}).code,
|
||||||
|
"function*f(){yield undefined;yield;yield*undefined;yield void 0}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should be able to drop undefined after yield if necessary with compression", function() {
|
||||||
|
assert.strictEqual(
|
||||||
|
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {fromString: true, compress: true}).code,
|
||||||
|
"function*f(){yield,yield,yield*void 0,yield}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
|
||||||
|
var tests = [
|
||||||
|
// Fail as as_symbol
|
||||||
|
'"use strict"; import yield from "bar";',
|
||||||
|
'"use strict"; yield = 123;',
|
||||||
|
'"use strict"; yield: "123";',
|
||||||
|
'"use strict"; for(;;){break yield;}',
|
||||||
|
'"use strict"; for(;;){continue yield;}',
|
||||||
|
'"use strict"; function yield(){}',
|
||||||
|
'"use strict"; function foo(...yield){}',
|
||||||
|
'"use strict"; try { new Error("")} catch (yield) {}',
|
||||||
|
'"use strict"; var yield = "foo";',
|
||||||
|
'"use strict"; class yield {}',
|
||||||
|
|
||||||
|
// Fail as maybe_assign
|
||||||
|
'"use strict"; var foo = yield;',
|
||||||
|
'"use strict"; var foo = bar = yield',
|
||||||
|
|
||||||
|
// Fail as as_property_name
|
||||||
|
'"use strict"; var foo = {yield};',
|
||||||
|
'"use strict"; var bar = {yield: "foo"};'
|
||||||
|
];
|
||||||
|
|
||||||
|
var fail = function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
|
e.message === "SyntaxError: Unexpected yield identifier inside strict mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
var test = function(input) {
|
||||||
|
return function() {
|
||||||
|
UglifyJS.parse(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < tests.length; i++) {
|
||||||
|
assert.throws(test(tests[i]), fail, tests[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
138
test/parser.js
Normal file
138
test/parser.js
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
|
||||||
|
var UglifyJS = require("..");
|
||||||
|
var ok = require('assert');
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
console.log("--- Parser tests");
|
||||||
|
|
||||||
|
// Destructuring arguments
|
||||||
|
|
||||||
|
// Function argument nodes are correct
|
||||||
|
function get_args(args) {
|
||||||
|
return args.map(function (arg) {
|
||||||
|
return [arg.TYPE, arg.name];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructurings as arguments
|
||||||
|
var destr_fun1 = UglifyJS.parse('(function ({a, b}) {})').body[0].body;
|
||||||
|
var destr_fun2 = UglifyJS.parse('(function ([a, [b]]) {})').body[0].body;
|
||||||
|
|
||||||
|
ok.equal(destr_fun1.argnames.length, 1);
|
||||||
|
ok.equal(destr_fun2.argnames.length, 1);
|
||||||
|
|
||||||
|
var destr_fun1 = UglifyJS.parse('({a, b}) => null').body[0].body;
|
||||||
|
var destr_fun2 = UglifyJS.parse('([a, [b]]) => null').body[0].body;
|
||||||
|
|
||||||
|
ok.equal(destr_fun1.argnames.length, 1);
|
||||||
|
ok.equal(destr_fun2.argnames.length, 1);
|
||||||
|
|
||||||
|
var destruct1 = destr_fun1.argnames[0];
|
||||||
|
var destruct2 = destr_fun2.argnames[0];
|
||||||
|
|
||||||
|
ok(destruct1 instanceof UglifyJS.AST_Destructuring);
|
||||||
|
ok(destruct2 instanceof UglifyJS.AST_Destructuring);
|
||||||
|
ok(destruct2.names[1] instanceof UglifyJS.AST_Destructuring);
|
||||||
|
|
||||||
|
ok.equal(destruct1.start.value, '{');
|
||||||
|
ok.equal(destruct1.end.value, '}');
|
||||||
|
ok.equal(destruct2.start.value, '[');
|
||||||
|
ok.equal(destruct2.end.value, ']');
|
||||||
|
|
||||||
|
ok.equal(destruct1.is_array, false);
|
||||||
|
ok.equal(destruct2.is_array, true);
|
||||||
|
|
||||||
|
var aAndB = [
|
||||||
|
['SymbolFunarg', 'a'],
|
||||||
|
['SymbolFunarg', 'b']
|
||||||
|
];
|
||||||
|
|
||||||
|
ok.deepEqual(
|
||||||
|
[
|
||||||
|
destruct1.names[0].TYPE,
|
||||||
|
destruct1.names[0].name],
|
||||||
|
aAndB[0]);
|
||||||
|
|
||||||
|
ok.deepEqual(
|
||||||
|
[
|
||||||
|
destruct2.names[1].names[0].TYPE,
|
||||||
|
destruct2.names[1].names[0].name
|
||||||
|
],
|
||||||
|
aAndB[1]);
|
||||||
|
|
||||||
|
ok.deepEqual(
|
||||||
|
get_args(destr_fun1.args_as_names()),
|
||||||
|
aAndB)
|
||||||
|
ok.deepEqual(
|
||||||
|
get_args(destr_fun2.args_as_names()),
|
||||||
|
aAndB)
|
||||||
|
|
||||||
|
// Making sure we don't accidentally accept things which
|
||||||
|
// Aren't argument destructurings
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function ([]) {})');
|
||||||
|
}, /Invalid destructuring function parameter/);
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function ( { a, [ b ] } ) { })')
|
||||||
|
});
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function (1) { })');
|
||||||
|
}, /Invalid function parameter/);
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function (this) { })');
|
||||||
|
});
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function ([1]) { })');
|
||||||
|
}, /Invalid function parameter/);
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('(function [a] { })');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Destructuring variable declaration
|
||||||
|
|
||||||
|
var decls = UglifyJS.parse('var {a,b} = foo, { c, d } = bar');
|
||||||
|
|
||||||
|
ok.equal(decls.body[0].TYPE, 'Var');
|
||||||
|
ok.equal(decls.body[0].definitions.length, 2);
|
||||||
|
ok.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
|
||||||
|
ok.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
|
||||||
|
|
||||||
|
var nested_def = UglifyJS.parse('var [{x}] = foo').body[0].definitions[0];
|
||||||
|
|
||||||
|
ok.equal(nested_def.name.names[0].names[0].TYPE, 'SymbolVar');
|
||||||
|
ok.equal(nested_def.name.names[0].names[0].name, 'x');
|
||||||
|
|
||||||
|
var holey_def = UglifyJS.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
|
||||||
|
|
||||||
|
ok.equal(holey_def.name.names[0].TYPE, 'Hole');
|
||||||
|
ok.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
|
||||||
|
|
||||||
|
var expanding_def = UglifyJS.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
|
||||||
|
|
||||||
|
ok.equal(expanding_def.name.names[0].TYPE, 'SymbolVar');
|
||||||
|
ok.equal(expanding_def.name.names[1].TYPE, 'Expansion');
|
||||||
|
ok.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
|
||||||
|
|
||||||
|
// generators
|
||||||
|
var generators_def = UglifyJS.parse('function* fn() {}').body[0];
|
||||||
|
ok.equal(generators_def.is_generator, true);
|
||||||
|
|
||||||
|
ok.throws(function () {
|
||||||
|
UglifyJS.parse('function* (){ }');
|
||||||
|
});
|
||||||
|
|
||||||
|
var generators_yield_def = UglifyJS.parse('function* fn() {\nyield remote();\}').body[0].body[0];
|
||||||
|
ok.strictEqual(generators_yield_def.body.is_star, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run standalone
|
||||||
|
if (module.parent === null) {
|
||||||
|
module.exports();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -30,6 +30,10 @@ run_ast_conversion_tests({
|
||||||
iterations: 1000
|
iterations: 1000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var run_parser_tests = require('./parser.js');
|
||||||
|
|
||||||
|
run_parser_tests();
|
||||||
|
|
||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function tmpl() {
|
function tmpl() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user