/*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. https://github.com/mishoo/UglifyJS2 -------------------------------- (C) --------------------------------- Author: Mihai Bazon http://mihai.bazon.net/blog Distributed under the BSD license: Copyright 2012 (c) Mihai Bazon Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************/ function OutputStream(options) { options = defaults(options, { indent_start : 0, indent_level : 4, quote_keys : false, space_colon : true, ascii_only : false, inline_script : false, width : 80, max_line_len : 32000, ie_proof : true, beautify : true, source_map : null, }); var indentation = 0; var current_col = 0; var current_line = 0; var current_pos = 0; var OUTPUT = ""; function to_ascii(str) { return str.replace(/[\u0080-\uffff]/g, function(ch) { var code = ch.charCodeAt(0).toString(16); while (code.length < 4) code = "0" + code; return "\\u" + code; }); }; function make_string(str) { var dq = 0, sq = 0; str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){ switch (s) { case "\\": return "\\\\"; case "\b": return "\\b"; case "\f": return "\\f"; case "\n": return "\\n"; case "\r": return "\\r"; case "\u2028": return "\\u2028"; case "\u2029": return "\\u2029"; case '"': ++dq; return '"'; case "'": ++sq; return "'"; case "\0": return "\\0"; } return s; }); if (options.ascii_only) str = to_ascii(str); if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; else return '"' + str.replace(/\x22/g, '\\"') + '"'; }; function encode_string(str) { var ret = make_string(str); if (options.inline_script) ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); return ret; }; function make_name(name) { name = name.toString(); if (options.ascii_only) name = to_ascii(name); return name; }; function make_indent(back) { return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); }; /* -----[ beautification/minification ]----- */ var might_need_space = false; var might_need_semicolon = false; var last = null; function last_char() { return last.charAt(last.length - 1); }; function maybe_newline() { if (options.max_line_len && current_col > options.max_line_len) print("\n"); }; function print(str) { str = String(str); var ch = str.charAt(0); if (might_need_semicolon) { if (";}".indexOf(ch) < 0 && !/[;]$/.test(last)) { OUTPUT += ";"; current_col++; current_pos++; if (!options.beautify) might_need_space = false; } might_need_semicolon = false; maybe_newline(); } if (might_need_space) { var prev = last_char(); if ((is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\")) || (/^[\+\-\/]$/.test(ch) && ch == prev)) { OUTPUT += " "; current_col++; current_pos++; } might_need_space = false; } var a = str.split(/\r?\n/), n = a.length - 1; current_line += n; if (n == 0) { current_col += a[n].length; } else { current_col = a[n].length; } current_pos += str.length; last = str; OUTPUT += str; }; var space = options.beautify ? function() { print(" "); } : function() { might_need_space = true; }; var indent = options.beautify ? function(half) { if (options.beautify) { print(make_indent(half ? 0.5 : 0)); } } : noop; var with_indent = options.beautify ? function(col, cont) { if (col === true) col = next_indent(); var save_indentation = indentation; indentation = col; var ret = cont(); indentation = save_indentation; return ret; } : function(col, cont) { return cont() }; var newline = options.beautify ? function() { print("\n"); } : noop; var semicolon = options.beautify ? function() { print(";"); } : function() { might_need_semicolon = true; }; function force_semicolon() { might_need_semicolon = false; print(";"); }; function next_indent() { return indentation + options.indent_level; }; function with_block(cont) { var ret; print("{"); newline(); with_indent(next_indent(), function(){ ret = cont(); }); indent(); print("}"); return ret; }; function with_parens(cont) { print("("); //XXX: still nice to have that for argument lists //var ret = with_indent(current_col, cont); var ret = cont(); print(")"); return ret; }; function with_square(cont) { print("["); //var ret = with_indent(current_col, cont); var ret = cont(); print("]"); return ret; }; function comma() { print(","); space(); }; function colon() { print(":"); if (options.space_colon) space(); }; var add_mapping = options.source_map ? function(token, name) { options.source_map.add( token.file, current_line, current_col, token.line, token.col, (!name && token.type == "name") ? token.value : name ); } : noop; function get() { return OUTPUT; }; var stack = []; return { get : get, toString : get, indent : indent, newline : newline, print : print, space : space, comma : comma, colon : colon, last : function() { return last }, semicolon : semicolon, force_semicolon : force_semicolon, print_name : function(name) { print(make_name(name)) }, print_string : function(str) { print(encode_string(str)) }, with_indent : with_indent, with_block : with_block, with_parens : with_parens, with_square : with_square, add_mapping : add_mapping, option : function(opt) { return options[opt] }, line : function() { return current_line }, col : function() { return current_col }, pos : function() { return current_pos }, push_node : function(node) { stack.push(node) }, pop_node : function() { return stack.pop() }, stack : function() { return stack }, parent : function(n) { return stack[stack.length - 2 - (n || 0)]; } }; }; /* -----[ code generators ]----- */ (function(){ /* -----[ utils ]----- */ function DEFPRINT(nodetype, generator) { nodetype.DEFMETHOD("print", function(stream){ var self = this; stream.push_node(self); if (self.needs_parens(stream)) { stream.with_parens(function(){ self.add_source_map(stream); generator(self, stream); }); } else { self.add_source_map(stream); generator(self, stream); } stream.pop_node(); }); }; AST_Node.DEFMETHOD("print_to_string", function(options){ options = defaults(options, { beautify: false }); var s = OutputStream(options); this.print(s); return s.get(); }); function PARENS(nodetype, func) { nodetype.DEFMETHOD("needs_parens", func); }; /* -----[ PARENTHESES ]----- */ PARENS(AST_Node, function(){ return false; }); // a function expression needs parens around it when it's provably // the first token to appear in a statement. PARENS(AST_Function, function(output){ return first_in_statement(output); }); // same goes for an object literal, because otherwise it would be // interpreted as a block of code. PARENS(AST_Object, function(output){ return first_in_statement(output); }); PARENS(AST_Seq, function(output){ var p = output.parent(); return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 7 || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4 || p instanceof AST_Dot // (1, {foo:2}).foo ==> 2 || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) * ==> 20 (side effect, set a := 10 and b := 20) */ ; }); PARENS(AST_Binary, function(output){ var p = output.parent(); // (foo && bar)() if (p instanceof AST_Call && p.expression === this) return true; // typeof (foo && bar) if (p instanceof AST_Unary) return true; // (foo && bar)["prop"], (foo && bar).prop if (p instanceof AST_PropAccess && p.expression === this) return true; // this deals with precedence: 3 * (2 + 1) if (p instanceof AST_Binary) { var po = p.operator, pp = PRECEDENCE[po]; var so = this.operator, sp = PRECEDENCE[so]; if (pp > sp || (pp == sp && this === p.right && !(so == po && (so == "*" || so == "&&" || so == "||")))) { return true; } } // for (var i = (foo in bar);;); ← perhaps useless, but valid syntax if (this.operator == "in") { // the “NoIn” stuff :-\ // UglifyJS 1.3.3 misses this one. if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this) return true; if (p instanceof AST_VarDef) { var v = output.parent(1), p2 = output.parent(2); if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v) return true; } } }); PARENS(AST_New, function(output){ var p = output.parent(); // (new Date).getTime(); if (p instanceof AST_Dot && no_constructor_parens(this, output)) return true; }); function assign_and_conditional_paren_rules(output) { var p = output.parent(); // !(a = false) → true if (p instanceof AST_Unary) return true; // 1 + (a = 2) + 3 → 6, side effect setting a = 2 if (p instanceof AST_Binary && !(p instanceof AST_Assign)) return true; // (a = func)() —or— new (a = Object)() if (p instanceof AST_Call && p.expression === this) return true; // (a = foo) ? bar : baz if (p instanceof AST_Conditional && p.condition === this) return true; // (a = foo)["prop"] —or— (a = foo).prop if (p instanceof AST_PropAccess && p.expression === this) return true; }; PARENS(AST_Assign, assign_and_conditional_paren_rules); PARENS(AST_Conditional, assign_and_conditional_paren_rules); /* -----[ PRINTERS ]----- */ DEFPRINT(AST_Directive, function(self, output){ output.print_string(self.value); output.semicolon(); }); DEFPRINT(AST_Debugger, function(self, output){ output.print("debugger"); output.semicolon(); }); /* -----[ statements ]----- */ function display_body(body, is_toplevel, output) { var last = body.length - 1; body.forEach(function(stmt, i){ if (!(stmt instanceof AST_EmptyStatement)) { output.indent(); stmt.print(output); if (!(i == last && is_toplevel)) { output.newline(); if (is_toplevel) output.newline(); } } }); }; AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ force_statement(this.body, output); }); DEFPRINT(AST_Statement, function(self, output){ self.body.print(output); output.semicolon(); }); DEFPRINT(AST_Toplevel, function(self, output){ display_body(self.body, true, output); }); DEFPRINT(AST_LabeledStatement, function(self, output){ self.label.print(output); output.colon(); self.body.print(output); }); DEFPRINT(AST_SimpleStatement, function(self, output){ self.body.print(output); output.semicolon(); }); function print_bracketed(body, output) { if (body.length > 0) output.with_block(function(){ display_body(body, false, output); }); else output.print("{}"); }; DEFPRINT(AST_BlockStatement, function(self, output){ print_bracketed(self.body, output); }); DEFPRINT(AST_EmptyStatement, function(self, output){ output.semicolon(); }); DEFPRINT(AST_Do, function(self, output){ output.print("do"); output.space(); self._do_print_body(output); output.space(); output.print("while"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.semicolon(); }); DEFPRINT(AST_While, function(self, output){ output.print("while"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.space(); self._do_print_body(output); }); DEFPRINT(AST_For, function(self, output){ output.print("for"); output.space(); output.with_parens(function(){ if (self.init) { self.init.print(output); output.print(";"); output.space(); } else { output.print(";"); } if (self.condition) { self.condition.print(output); output.print(";"); output.space(); } else { output.print(";"); } if (self.step) { self.step.print(output); } }); output.space(); self._do_print_body(output); }); DEFPRINT(AST_ForIn, function(self, output){ output.print("for"); output.space(); output.with_parens(function(){ self.init.print(output); output.space(); output.print("in"); output.space(); self.object.print(output); }); output.space(); self._do_print_body(output); }); DEFPRINT(AST_With, function(self, output){ output.print("with"); output.space(); output.with_parens(function(){ self.expression.print(output); }); output.space(); self._do_print_body(output); }); /* -----[ functions ]----- */ AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ var self = this; if (!nokeyword) { output.print("function"); } if (self.name) { output.space(); self.name.print(output); } output.with_parens(function(){ self.argnames.forEach(function(arg, i){ if (i) output.comma(); arg.print(output); }); }); output.space(); print_bracketed(self.body, output); }); DEFPRINT(AST_Lambda, function(self, output){ self._do_print(output); }); /* -----[ exits ]----- */ AST_Exit.DEFMETHOD("_do_print", function(output, kind){ output.print(kind); if (this.value) { output.space(); this.value.print(output); } output.semicolon(); }); DEFPRINT(AST_Return, function(self, output){ self._do_print(output, "return"); }); DEFPRINT(AST_Throw, function(self, output){ self._do_print(output, "throw"); }); /* -----[ loop control ]----- */ AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ output.print(kind); if (this.label) { output.space(); this.label.print(output); } output.semicolon(); }); DEFPRINT(AST_Break, function(self, output){ self._do_print(output, "break"); }); DEFPRINT(AST_Continue, function(self, output){ self._do_print(output, "continue"); }); /* -----[ if ]----- */ function make_then(self, output) { // The squeezer replaces "block"-s that contain only a single // statement with the statement itself; technically, the AST // is correct, but this can create problems when we output an // IF having an ELSE clause where the THEN clause ends in an // IF *without* an ELSE block (then the outer ELSE would refer // to the inner IF). This function checks for this case and // adds the block brackets if needed. if (!self.body) return output.semicolon(); if (self.body instanceof AST_Do && output.option("ie_proof")) { // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE // croaks with "syntax error" on code like this: if (foo) // do ... while(cond); else ... we need block brackets // around do/while make_block(self.body, output); return; } var b = self.body; while (true) { if (b instanceof AST_If) { if (!b.alternative) { make_block(self.body, output); return; } b = b.alternative; } else if (b instanceof AST_StatementWithBody) { b = b.body; } else break; } self.body.print(output); }; DEFPRINT(AST_If, function(self, output){ output.print("if"); output.space(); output.with_parens(function(){ self.condition.print(output); }); output.space(); if (self.alternative) { make_then(self, output); output.space(); output.print("else"); output.space(); force_statement(self.alternative, output); } else { self._do_print_body(output); } }); /* -----[ switch ]----- */ DEFPRINT(AST_Switch, function(self, output){ output.print("switch"); output.space(); output.with_parens(function(){ self.expression.print(output); }); output.space(); self.body.print(output); }); DEFPRINT(AST_SwitchBlock, function(self, output){ if (self.body.length > 0) output.with_block(function(){ self.body.forEach(function(stmt, i){ if (i) output.newline(); output.indent(true); stmt.print(output); }); }); else output.print("{}"); }); AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ if (this.body.length > 0) { output.newline(); this.body.forEach(function(stmt){ output.indent(); stmt.print(output); output.newline(); }); } }); DEFPRINT(AST_Default, function(self, output){ output.print("default:"); self._do_print_body(output); }); DEFPRINT(AST_Case, function(self, output){ output.print("case"); output.space(); self.expression.print(output); output.print(":"); self._do_print_body(output); }); /* -----[ exceptions ]----- */ DEFPRINT(AST_Try, function(self, output){ output.print("try"); output.space(); print_bracketed(self.body, output); if (self.bcatch) { output.space(); self.bcatch.print(output); } if (self.bfinally) { output.space(); self.bfinally.print(output); } }); DEFPRINT(AST_Catch, function(self, output){ output.print("catch"); output.space(); output.with_parens(function(){ self.argname.print(output); }); output.space(); print_bracketed(self.body, output); }); DEFPRINT(AST_Finally, function(self, output){ output.print("finally"); output.space(); print_bracketed(self.body, output); }); /* -----[ var/const ]----- */ AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ output.print(kind); output.space(); this.definitions.forEach(function(def, i){ if (i) output.comma(); def.print(output); }); var p = output.parent(); var in_for = p instanceof AST_For || p instanceof AST_ForIn; var avoid_semicolon = in_for && p.init === this; if (!avoid_semicolon) output.semicolon(); }); DEFPRINT(AST_Var, function(self, output){ self._do_print(output, "var"); }); DEFPRINT(AST_Const, function(self, output){ self._do_print(output, "const"); }); DEFPRINT(AST_VarDef, function(self, output){ self.name.print(output); if (self.value) { output.space(); output.print("="); output.space(); self.value.print(output); } }); /* -----[ other expressions ]----- */ DEFPRINT(AST_Call, function(self, output){ self.expression.print(output); if (self instanceof AST_New && no_constructor_parens(self, output)) return; output.with_parens(function(){ self.args.forEach(function(expr, i){ if (i) output.comma(); expr.print(output); }); }); }); DEFPRINT(AST_New, function(self, output){ output.print("new"); output.space(); AST_Call.prototype.print.call(self, output); }); DEFPRINT(AST_Seq, function(self, output){ self.car.print(output); if (self.cdr) { output.comma(); self.cdr.print(output); } }); DEFPRINT(AST_Dot, function(self, output){ var expr = self.expression; expr.print(output); if (expr instanceof AST_Number) { if (!/[xa-f.]/i.test(output.last())) { output.print("."); } } output.print("."); output.print_name(self.property); }); DEFPRINT(AST_Sub, function(self, output){ self.expression.print(output); output.print("["); self.property.print(output); output.print("]"); }); DEFPRINT(AST_UnaryPrefix, function(self, output){ var op = self.operator; output.print(op); if (/^[a-z]/i.test(op)) output.space(); self.expression.print(output); }); DEFPRINT(AST_UnaryPostfix, function(self, output){ self.expression.print(output); output.print(self.operator); }); DEFPRINT(AST_Binary, function(self, output){ self.left.print(output); output.space(); output.print(self.operator); output.space(); self.right.print(output); }); DEFPRINT(AST_Conditional, function(self, output){ self.condition.print(output); output.space(); output.print("?"); output.space(); self.consequent.print(output); output.space(); output.colon(); self.alternative.print(output); }); /* -----[ literals ]----- */ DEFPRINT(AST_Array, function(self, output){ output.with_square(function(){ var a = self.elements, len = a.length; if (len > 0) output.space(); a.forEach(function(exp, i){ if (i) output.comma(); if (!(exp instanceof AST_Undefined)) exp.print(output); }); if (len > 0) output.space(); }); }); DEFPRINT(AST_Object, function(self, output){ if (self.properties.length > 0) output.with_block(function(){ self.properties.forEach(function(prop, i){ if (i) { output.print(","); output.newline(); } output.indent(); prop.print(output); }); output.newline(); }); else output.print("{}"); }); DEFPRINT(AST_ObjectKeyVal, function(self, output){ var key = self.key; if (output.option("quote_keys")) { output.print_string(key); } else if ((typeof key == "number" || !output.option("beautify") && +key + "" == key) && parseFloat(key) >= 0) { output.print(make_num(key)); } else if (!is_identifier(key)) { output.print_string(key); } else { output.print_name(key); } output.colon(); self.value.print(output); }); DEFPRINT(AST_ObjectSetter, function(self, output){ output.print("set"); output.space(); self.value._do_print(output, true); }); DEFPRINT(AST_ObjectGetter, function(self, output){ output.print("get"); output.space(); self.value._do_print(output, true); }); DEFPRINT(AST_Symbol, function(self, output){ var def = self.definition(); output.print_name(def ? def.mangled_name || def.name : self.name); }); DEFPRINT(AST_Undefined, function(self, output){ // XXX: should add more options for this output.print("void 0"); //output.print("[][0]"); }); DEFPRINT(AST_NaN, function(self, output){ output.print("0/0"); }); DEFPRINT(AST_This, function(self, output){ output.print("this"); }); DEFPRINT(AST_Constant, function(self, output){ output.print(self.getValue()); }); DEFPRINT(AST_String, function(self, output){ output.print_string(self.getValue()); }); DEFPRINT(AST_Number, function(self, output){ output.print(make_num(self.getValue())); }); DEFPRINT(AST_RegExp, function(self, output){ output.print("/"); output.print(self.pattern); output.print("/"); if (self.mods) output.print(self.mods); }); function force_statement(stat, output) { if (stat instanceof AST_EmptyStatement) output.force_semicolon(); else stat.print(output); }; // return true if the node at the top of the stack (that means the // innermost node in the current output) is lexically the first in // a statement. function first_in_statement(output) { var a = output.stack(), i = a.length, node = a[--i], p = a[--i]; while (i > 0) { if (p instanceof AST_Statement && p.body === node) return true; if ((p instanceof AST_Seq && p.car === node ) || (p instanceof AST_Call && p.expression === node ) || (p instanceof AST_Dot && p.expression === node ) || (p instanceof AST_Sub && p.expression === node ) || (p instanceof AST_Conditional && p.condition === node ) || (p instanceof AST_Binary && p.left === node ) || (p instanceof AST_UnaryPostfix && p.expression === node )) { node = p; p = a[--i]; } else { return false; } } }; // self should be AST_New. decide if we want to show parens or not. function no_constructor_parens(self, output) { return self.args.length == 0 && !output.option("beautify"); }; function best_of(a) { var best = a[0], len = best.length; for (var i = 1; i < a.length; ++i) { if (a[i].length < len) { best = a[i]; len = best.length; } } return best; }; function make_num(num) { var str = num.toString(10), a = [ str.replace(/^0\./, ".").replace('e+', 'e') ], m; if (Math.floor(num) === num) { if (num >= 0) { a.push("0x" + num.toString(16).toLowerCase(), // probably pointless "0" + num.toString(8)); // same. } else { a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless "-0" + (-num).toString(8)); // same. } if ((m = /^(.*?)(0+)$/.exec(num))) { a.push(m[1] + "e" + m[2].length); } } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) { a.push(m[2] + "e-" + (m[1].length + m[2].length), str.substr(str.indexOf("."))); } return best_of(a); }; function make_block(stmt, output) { if (stmt instanceof AST_BlockStatement) { stmt.print(output); return; } output.with_block(function(){ output.indent(); stmt.print(output); output.newline(); }); }; /* -----[ source map generators ]----- */ function DEFMAP(nodetype, generator) { nodetype.DEFMETHOD("add_source_map", function(stream){ generator(this, stream); }); }; // We could easily add info for ALL nodes, but it seems to me that // would be quite wasteful, hence this noop in the base class. DEFMAP(AST_Node, noop); function basic_sourcemap_gen(self, output) { output.add_mapping(self.start); }; // XXX: I'm not exactly sure if we need it for all of these nodes, // or if we should add even more. DEFMAP(AST_Directive, basic_sourcemap_gen); DEFMAP(AST_Debugger, basic_sourcemap_gen); DEFMAP(AST_Symbol, basic_sourcemap_gen); DEFMAP(AST_Jump, basic_sourcemap_gen); DEFMAP(AST_StatementWithBody, basic_sourcemap_gen); DEFMAP(AST_Lambda, basic_sourcemap_gen); DEFMAP(AST_PropAccess, basic_sourcemap_gen); DEFMAP(AST_Switch, basic_sourcemap_gen); DEFMAP(AST_BlockStatement, basic_sourcemap_gen); DEFMAP(AST_Toplevel, noop); DEFMAP(AST_Try, basic_sourcemap_gen); DEFMAP(AST_Catch, basic_sourcemap_gen); DEFMAP(AST_Finally, basic_sourcemap_gen); DEFMAP(AST_Definitions, basic_sourcemap_gen); DEFMAP(AST_Constant, basic_sourcemap_gen); DEFMAP(AST_ObjectProperty, function(self, output){ output.add_mapping(self.start, self.key); }); })();