| | |
| | | if (base && base.PROPS) props = props.concat(base.PROPS); |
| | | var code = [ |
| | | "return function AST_", type, "(props){", |
| | | // not essential, but speeds up compress by a few percent |
| | | "this._bits=0;", |
| | | "if(props){", |
| | | ]; |
| | | props.forEach(function(prop) { |
| | |
| | | })); |
| | | }, |
| | | }, null); |
| | | |
| | | DEF_BITPROPS(AST_Node, [ |
| | | "_optimized", |
| | | "_squeezed", |
| | | // AST_Call |
| | | "call_only", |
| | | // AST_Lambda |
| | | "collapse_scanning", |
| | | // AST_SymbolRef |
| | | "defined", |
| | | "evaluating", |
| | | "falsy", |
| | | // AST_SymbolRef |
| | | "in_arg", |
| | | // AST_Return |
| | | "in_bool", |
| | | // AST_SymbolRef |
| | | "is_undefined", |
| | | // AST_LambdaExpression |
| | | // AST_LambdaDefinition |
| | | "inlined", |
| | | // AST_Lambda |
| | | "length_read", |
| | | // AST_Yield |
| | | "nested", |
| | | // AST_Lambda |
| | | "new", |
| | | // AST_Call |
| | | // AST_PropAccess |
| | | "optional", |
| | | // AST_ClassProperty |
| | | "private", |
| | | // AST_Call |
| | | "pure", |
| | | // AST_Assign |
| | | "redundant", |
| | | // AST_ClassProperty |
| | | "static", |
| | | // AST_Call |
| | | // AST_PropAccess |
| | | "terminal", |
| | | "truthy", |
| | | // AST_Scope |
| | | "uses_eval", |
| | | // AST_Scope |
| | | "uses_with", |
| | | ]); |
| | | |
| | | (AST_Node.log_function = function(fn, verbose) { |
| | | if (typeof fn != "function") { |
| | |
| | | }, |
| | | }, AST_Statement); |
| | | |
| | | var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", { |
| | | var AST_BlockScope = DEFNODE("BlockScope", "_var_names enclosed functions make_def parent_scope variables", { |
| | | $documentation: "Base class for all statements introducing a lexical scope", |
| | | $propdoc: { |
| | | enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", |
| | |
| | | |
| | | /* -----[ scope and functions ]----- */ |
| | | |
| | | var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", { |
| | | var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", { |
| | | $documentation: "Base class for all statements introducing a lexical scope", |
| | | $propdoc: { |
| | | uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", |
| | |
| | | } |
| | | }, AST_Scope); |
| | | |
| | | var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", { |
| | | var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", { |
| | | $documentation: "Base class for functions", |
| | | $propdoc: { |
| | | argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals", |
| | | length_read: "[boolean/S] whether length property of this function is accessed", |
| | | rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent", |
| | | uses_arguments: "[boolean/S] whether this function accesses the arguments array", |
| | | uses_arguments: "[boolean|number/S] whether this function accesses the arguments array", |
| | | }, |
| | | each_argname: function(visit) { |
| | | var tw = new TreeWalker(function(node) { |
| | |
| | | $documentation: "A class definition", |
| | | $propdoc: { |
| | | name: "[AST_SymbolDefClass] the name of this class", |
| | | }, |
| | | resolve: function(def_class) { |
| | | return def_class ? this : this.parent_scope.resolve(); |
| | | }, |
| | | _validate: function() { |
| | | if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass"); |
| | |
| | | args: "[AST_Node*] array of arguments", |
| | | expression: "[AST_Node] expression to invoke as function", |
| | | optional: "[boolean] whether the expression is optional chaining", |
| | | pure: "[string/S] marker for side-effect-free call expression", |
| | | pure: "[boolean/S] marker for side-effect-free call expression", |
| | | terminal: "[boolean] whether the chain has ended", |
| | | }, |
| | | walk: function(visitor) { |
| | |
| | | throw new Error("left must be assignable: " + node.TYPE); |
| | | } |
| | | }); |
| | | } else if (!(this.left instanceof AST_Infinity |
| | | || this.left instanceof AST_NaN |
| | | || this.left instanceof AST_PropAccess && !this.left.optional |
| | | || this.left instanceof AST_SymbolRef |
| | | || this.left instanceof AST_Undefined)) { |
| | | throw new Error("left must be assignable"); |
| | | } |
| | | }, |
| | | }, AST_Binary); |
| | |
| | | $documentation: "Symbol defining a variable", |
| | | }, AST_SymbolDeclaration); |
| | | |
| | | var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { |
| | | var AST_SymbolFunarg = DEFNODE("SymbolFunarg", "unused", { |
| | | $documentation: "Symbol naming a function argument", |
| | | }, AST_SymbolVar); |
| | | |
| | |
| | | |
| | | var AST_Null = DEFNODE("Null", null, { |
| | | $documentation: "The `null` atom", |
| | | value: null |
| | | value: null, |
| | | }, AST_Atom); |
| | | |
| | | var AST_NaN = DEFNODE("NaN", null, { |
| | | $documentation: "The impossible value", |
| | | value: 0/0 |
| | | value: 0/0, |
| | | }, AST_Atom); |
| | | |
| | | var AST_Undefined = DEFNODE("Undefined", null, { |
| | | $documentation: "The `undefined` value", |
| | | value: function(){}() |
| | | value: function(){}(), |
| | | }, AST_Atom); |
| | | |
| | | var AST_Hole = DEFNODE("Hole", null, { |
| | | $documentation: "A hole in an array", |
| | | value: function(){}() |
| | | value: function(){}(), |
| | | }, AST_Atom); |
| | | |
| | | var AST_Infinity = DEFNODE("Infinity", null, { |
| | | $documentation: "The `Infinity` value", |
| | | value: 1/0 |
| | | value: 1/0, |
| | | }, AST_Atom); |
| | | |
| | | var AST_Boolean = DEFNODE("Boolean", null, { |
| | |
| | | |
| | | var AST_False = DEFNODE("False", null, { |
| | | $documentation: "The `false` atom", |
| | | value: false |
| | | value: false, |
| | | }, AST_Boolean); |
| | | |
| | | var AST_True = DEFNODE("True", null, { |
| | | $documentation: "The `true` atom", |
| | | value: true |
| | | value: true, |
| | | }, AST_Boolean); |
| | | |
| | | /* -----[ TreeWalker ]----- */ |
| | |
| | | }, |
| | | find_parent: function(type) { |
| | | var stack = this.stack; |
| | | for (var i = stack.length; --i >= 0;) { |
| | | for (var i = stack.length - 1; --i >= 0;) { |
| | | var x = stack[i]; |
| | | if (x instanceof type) return x; |
| | | } |
| | |
| | | } |
| | | }, |
| | | in_boolean_context: function() { |
| | | var self = this.self(); |
| | | for (var i = 0, p; p = this.parent(i); i++) { |
| | | if (p instanceof AST_Conditional && p.condition === self |
| | | || p instanceof AST_DWLoop && p.condition === self |
| | | || p instanceof AST_For && p.condition === self |
| | | || p instanceof AST_If && p.condition === self |
| | | || p instanceof AST_Return && p.in_bool |
| | | || p instanceof AST_Sequence && p.tail_node() !== self |
| | | || p instanceof AST_SimpleStatement |
| | | || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { |
| | | return true; |
| | | } |
| | | if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||") |
| | | || p instanceof AST_Conditional |
| | | || p.tail_node() === self) { |
| | | self = p; |
| | | } else if (p instanceof AST_Return) { |
| | | for (var call, fn = p; call = this.parent(++i); fn = call) { |
| | | if (call.TYPE == "Call") { |
| | | if (!(fn instanceof AST_Lambda) || fn.name) return false; |
| | | } else if (fn instanceof AST_Lambda) { |
| | | return false; |
| | | } |
| | | } |
| | | } else { |
| | | for (var drop = true, level = 0, parent, self = this.self(); parent = this.parent(level++); self = parent) { |
| | | if (parent instanceof AST_Binary) switch (parent.operator) { |
| | | case "&&": |
| | | case "||": |
| | | if (parent.left === self) drop = false; |
| | | continue; |
| | | default: |
| | | return false; |
| | | } |
| | | if (parent instanceof AST_Conditional) { |
| | | if (parent.condition === self) return true; |
| | | continue; |
| | | } |
| | | if (parent instanceof AST_DWLoop) return parent.condition === self; |
| | | if (parent instanceof AST_For) return parent.condition === self; |
| | | if (parent instanceof AST_If) return parent.condition === self; |
| | | if (parent instanceof AST_Return) { |
| | | if (parent.in_bool) return true; |
| | | while (parent = this.parent(level++)) { |
| | | if (parent instanceof AST_Lambda) { |
| | | if (parent.name) return false; |
| | | parent = this.parent(level++); |
| | | if (parent.TYPE != "Call") return false; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (parent instanceof AST_Sequence) { |
| | | if (parent.tail_node() === self) continue; |
| | | return drop ? "d" : true; |
| | | } |
| | | if (parent instanceof AST_SimpleStatement) return drop ? "d" : true; |
| | | if (parent instanceof AST_UnaryPrefix) return parent.operator == "!"; |
| | | return false; |
| | | } |
| | | } |
| | | }; |