| /*********************************************************************** | |
| A JavaScript tokenizer / parser / beautifier / compressor. | |
| https://github.com/mishoo/UglifyJS2 | |
| -------------------------------- (C) --------------------------------- | |
| Author: Mihai Bazon | |
| <mihai.bazon@gmail.com> | |
| http://mihai.bazon.net/blog | |
| Distributed under the BSD license: | |
| Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> | |
| 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. | |
| ***********************************************************************/ | |
| "use strict"; | |
| var EXPECT_DIRECTIVE = /^$|[;{][\s\n]*$/; | |
| function is_some_comments(comment) { | |
| // multiline comment | |
| return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value); | |
| } | |
| function OutputStream(options) { | |
| var readonly = !options; | |
| options = defaults(options, { | |
| ascii_only : false, | |
| beautify : false, | |
| braces : false, | |
| comments : false, | |
| ie8 : false, | |
| indent_level : 4, | |
| indent_start : 0, | |
| inline_script : true, | |
| keep_quoted_props: false, | |
| max_line_len : false, | |
| preamble : null, | |
| preserve_line : false, | |
| quote_keys : false, | |
| quote_style : 0, | |
| semicolons : true, | |
| shebang : true, | |
| source_map : null, | |
| webkit : false, | |
| width : 80, | |
| wrap_iife : false, | |
| }, true); | |
| // Convert comment option to RegExp if neccessary and set up comments filter | |
| var comment_filter = return_false; // Default case, throw all comments away | |
| if (options.comments) { | |
| var comments = options.comments; | |
| if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { | |
| var regex_pos = options.comments.lastIndexOf("/"); | |
| comments = new RegExp( | |
| options.comments.substr(1, regex_pos - 1), | |
| options.comments.substr(regex_pos + 1) | |
| ); | |
| } | |
| if (comments instanceof RegExp) { | |
| comment_filter = function(comment) { | |
| return comment.type != "comment5" && comments.test(comment.value); | |
| }; | |
| } else if (typeof comments === "function") { | |
| comment_filter = function(comment) { | |
| return comment.type != "comment5" && comments(this, comment); | |
| }; | |
| } else if (comments === "some") { | |
| comment_filter = is_some_comments; | |
| } else { // NOTE includes "all" option | |
| comment_filter = return_true; | |
| } | |
| } | |
| var indentation = 0; | |
| var current_col = 0; | |
| var current_line = 1; | |
| var current_pos = 0; | |
| var OUTPUT = ""; | |
| var to_utf8 = options.ascii_only ? function(str, identifier) { | |
| return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { | |
| var code = ch.charCodeAt(0).toString(16); | |
| if (code.length <= 2 && !identifier) { | |
| while (code.length < 2) code = "0" + code; | |
| return "\\x" + code; | |
| } else { | |
| while (code.length < 4) code = "0" + code; | |
| return "\\u" + code; | |
| } | |
| }); | |
| } : function(str) { | |
| var s = ""; | |
| for (var i = 0; i < str.length; i++) { | |
| if (is_surrogate_pair_head(str[i]) && !is_surrogate_pair_tail(str[i + 1]) | |
| || is_surrogate_pair_tail(str[i]) && !is_surrogate_pair_head(str[i - 1])) { | |
| s += "\\u" + str.charCodeAt(i).toString(16); | |
| } else { | |
| s += str[i]; | |
| } | |
| } | |
| return s; | |
| }; | |
| function make_string(str, quote) { | |
| var dq = 0, sq = 0; | |
| str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) { | |
| switch (s) { | |
| case '"': ++dq; return '"'; | |
| case "'": ++sq; return "'"; | |
| case "\\": return "\\\\"; | |
| case "\n": return "\\n"; | |
| case "\r": return "\\r"; | |
| case "\t": return "\\t"; | |
| case "\b": return "\\b"; | |
| case "\f": return "\\f"; | |
| case "\x0B": return options.ie8 ? "\\x0B" : "\\v"; | |
| case "\u2028": return "\\u2028"; | |
| case "\u2029": return "\\u2029"; | |
| case "\ufeff": return "\\ufeff"; | |
| case "\0": | |
| return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; | |
| } | |
| return s; | |
| }); | |
| function quote_single() { | |
| return "'" + str.replace(/\x27/g, "\\'") + "'"; | |
| } | |
| function quote_double() { | |
| return '"' + str.replace(/\x22/g, '\\"') + '"'; | |
| } | |
| str = to_utf8(str); | |
| switch (options.quote_style) { | |
| case 1: | |
| return quote_single(); | |
| case 2: | |
| return quote_double(); | |
| case 3: | |
| return quote == "'" ? quote_single() : quote_double(); | |
| default: | |
| return dq > sq ? quote_single() : quote_double(); | |
| } | |
| } | |
| function encode_string(str, quote) { | |
| var ret = make_string(str, quote); | |
| if (options.inline_script) { | |
| ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2"); | |
| ret = ret.replace(/\x3c!--/g, "\\x3c!--"); | |
| ret = ret.replace(/--\x3e/g, "--\\x3e"); | |
| } | |
| return ret; | |
| } | |
| function make_name(name) { | |
| name = name.toString(); | |
| name = to_utf8(name, true); | |
| return name; | |
| } | |
| function make_indent(back) { | |
| return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); | |
| } | |
| /* -----[ beautification/minification ]----- */ | |
| var has_parens = false; | |
| var line_end = 0; | |
| var line_fixed = true; | |
| var might_need_space = false; | |
| var might_need_semicolon = false; | |
| var need_newline_indented = false; | |
| var need_space = false; | |
| var newline_insert = -1; | |
| var last = ""; | |
| var mapping_token, mapping_name, mappings = options.source_map && []; | |
| var adjust_mappings = mappings ? function(line, col) { | |
| mappings.forEach(function(mapping) { | |
| mapping.line += line; | |
| mapping.col += col; | |
| }); | |
| } : noop; | |
| var flush_mappings = mappings ? function() { | |
| mappings.forEach(function(mapping) { | |
| options.source_map.add( | |
| mapping.token.file, | |
| mapping.line, mapping.col, | |
| mapping.token.line, mapping.token.col, | |
| !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name | |
| ); | |
| }); | |
| mappings = []; | |
| } : noop; | |
| function insert_newlines(count) { | |
| var index = OUTPUT.lastIndexOf("\n"); | |
| if (line_end < index) line_end = index; | |
| var left = OUTPUT.slice(0, line_end); | |
| var right = OUTPUT.slice(line_end); | |
| adjust_mappings(count, right.length - current_col); | |
| current_line += count; | |
| current_pos += count; | |
| current_col = right.length; | |
| OUTPUT = left; | |
| while (count--) OUTPUT += "\n"; | |
| OUTPUT += right; | |
| } | |
| var fix_line = options.max_line_len ? function() { | |
| if (line_fixed) { | |
| if (current_col > options.max_line_len) { | |
| AST_Node.warn("Output exceeds {max_line_len} characters", options); | |
| } | |
| return; | |
| } | |
| if (current_col > options.max_line_len) insert_newlines(1); | |
| line_fixed = true; | |
| flush_mappings(); | |
| } : noop; | |
| var requireSemicolonChars = makePredicate("( [ + * / - , ."); | |
| function print(str) { | |
| str = String(str); | |
| var ch = str.charAt(0); | |
| if (need_newline_indented && ch) { | |
| need_newline_indented = false; | |
| if (ch != "\n") { | |
| print("\n"); | |
| indent(); | |
| } | |
| } | |
| if (need_space && ch) { | |
| need_space = false; | |
| if (!/[\s;})]/.test(ch)) { | |
| space(); | |
| } | |
| } | |
| newline_insert = -1; | |
| var prev = last.charAt(last.length - 1); | |
| if (might_need_semicolon) { | |
| might_need_semicolon = false; | |
| if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") { | |
| if (options.semicolons || requireSemicolonChars[ch]) { | |
| OUTPUT += ";"; | |
| current_col++; | |
| current_pos++; | |
| } else { | |
| fix_line(); | |
| OUTPUT += "\n"; | |
| current_pos++; | |
| current_line++; | |
| current_col = 0; | |
| if (/^\s+$/.test(str)) { | |
| // reset the semicolon flag, since we didn't print one | |
| // now and might still have to later | |
| might_need_semicolon = true; | |
| } | |
| } | |
| if (!options.beautify) | |
| might_need_space = false; | |
| } | |
| } | |
| if (might_need_space) { | |
| if ((is_identifier_char(prev) | |
| && (is_identifier_char(ch) || ch == "\\")) | |
| || (ch == "/" && ch == prev) | |
| || ((ch == "+" || ch == "-") && ch == last)) | |
| { | |
| OUTPUT += " "; | |
| current_col++; | |
| current_pos++; | |
| } | |
| might_need_space = false; | |
| } | |
| if (mapping_token) { | |
| mappings.push({ | |
| token: mapping_token, | |
| name: mapping_name, | |
| line: current_line, | |
| col: current_col | |
| }); | |
| mapping_token = false; | |
| if (line_fixed) flush_mappings(); | |
| } | |
| OUTPUT += str; | |
| has_parens = str[str.length - 1] == "("; | |
| current_pos += str.length; | |
| var a = str.split(/\r?\n/), n = a.length - 1; | |
| current_line += n; | |
| current_col += a[0].length; | |
| if (n > 0) { | |
| fix_line(); | |
| current_col = a[n].length; | |
| } | |
| last = 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 may_add_newline = options.max_line_len || options.preserve_line ? function() { | |
| fix_line(); | |
| line_end = OUTPUT.length; | |
| line_fixed = false; | |
| } : noop; | |
| var newline = options.beautify ? function() { | |
| if (newline_insert < 0) return print("\n"); | |
| if (OUTPUT[newline_insert] != "\n") { | |
| OUTPUT = OUTPUT.slice(0, newline_insert) + "\n" + OUTPUT.slice(newline_insert); | |
| current_pos++; | |
| current_line++; | |
| } | |
| newline_insert++; | |
| } : may_add_newline; | |
| 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("("); | |
| may_add_newline(); | |
| //XXX: still nice to have that for argument lists | |
| //var ret = with_indent(current_col, cont); | |
| var ret = cont(); | |
| may_add_newline(); | |
| print(")"); | |
| return ret; | |
| } | |
| function with_square(cont) { | |
| print("["); | |
| may_add_newline(); | |
| //var ret = with_indent(current_col, cont); | |
| var ret = cont(); | |
| may_add_newline(); | |
| print("]"); | |
| return ret; | |
| } | |
| function comma() { | |
| may_add_newline(); | |
| print(","); | |
| may_add_newline(); | |
| space(); | |
| } | |
| function colon() { | |
| print(":"); | |
| space(); | |
| } | |
| var add_mapping = mappings ? function(token, name) { | |
| mapping_token = token; | |
| mapping_name = name; | |
| } : noop; | |
| function get() { | |
| if (!line_fixed) fix_line(); | |
| return OUTPUT; | |
| } | |
| function has_nlb() { | |
| var index = OUTPUT.lastIndexOf("\n"); | |
| return /^ *$/.test(OUTPUT.slice(index + 1)); | |
| } | |
| function prepend_comments(node) { | |
| var self = this; | |
| var scan = node instanceof AST_Exit && node.value; | |
| var comments = dump(node); | |
| if (!comments) comments = []; | |
| if (scan) { | |
| var tw = new TreeWalker(function(node) { | |
| var parent = tw.parent(); | |
| if (parent instanceof AST_Exit | |
| || parent instanceof AST_Binary && parent.left === node | |
| || parent.TYPE == "Call" && parent.expression === node | |
| || parent instanceof AST_Conditional && parent.condition === node | |
| || parent instanceof AST_Dot && parent.expression === node | |
| || parent instanceof AST_Sequence && parent.expressions[0] === node | |
| || parent instanceof AST_Sub && parent.expression === node | |
| || parent instanceof AST_UnaryPostfix) { | |
| var before = dump(node); | |
| if (before) comments = comments.concat(before); | |
| } else { | |
| return true; | |
| } | |
| }); | |
| tw.push(node); | |
| node.value.walk(tw); | |
| } | |
| if (current_pos == 0) { | |
| if (comments.length > 0 && options.shebang && comments[0].type == "comment5") { | |
| print("#!" + comments.shift().value + "\n"); | |
| indent(); | |
| } | |
| var preamble = options.preamble; | |
| if (preamble) { | |
| print(preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); | |
| } | |
| } | |
| comments = comments.filter(comment_filter, node); | |
| if (comments.length == 0) return; | |
| var last_nlb = has_nlb(); | |
| comments.forEach(function(c, i) { | |
| if (!last_nlb) { | |
| if (c.nlb) { | |
| print("\n"); | |
| indent(); | |
| last_nlb = true; | |
| } else if (i > 0) { | |
| space(); | |
| } | |
| } | |
| if (/comment[134]/.test(c.type)) { | |
| print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n"); | |
| indent(); | |
| last_nlb = true; | |
| } else if (c.type == "comment2") { | |
| print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); | |
| last_nlb = false; | |
| } | |
| }); | |
| if (!last_nlb) { | |
| if (node.start.nlb) { | |
| print("\n"); | |
| indent(); | |
| } else { | |
| space(); | |
| } | |
| } | |
| function dump(node) { | |
| var token = node.start; | |
| if (!token) { | |
| if (!scan) return; | |
| node.start = token = new AST_Token(); | |
| } | |
| var comments = token.comments_before; | |
| if (!comments) { | |
| if (!scan) return; | |
| token.comments_before = comments = []; | |
| } | |
| if (comments._dumped === self) return; | |
| comments._dumped = self; | |
| return comments; | |
| } | |
| } | |
| function append_comments(node, tail) { | |
| var self = this; | |
| var token = node.end; | |
| if (!token) return; | |
| var comments = token[tail ? "comments_before" : "comments_after"]; | |
| if (!comments || comments._dumped === self) return; | |
| if (!(node instanceof AST_Statement || all(comments, function(c) { | |
| return !/comment[134]/.test(c.type); | |
| }))) return; | |
| comments._dumped = self; | |
| var insert = OUTPUT.length; | |
| comments.filter(comment_filter, node).forEach(function(c, i) { | |
| need_space = false; | |
| if (need_newline_indented) { | |
| print("\n"); | |
| indent(); | |
| need_newline_indented = false; | |
| } else if (c.nlb && (i > 0 || !has_nlb())) { | |
| print("\n"); | |
| indent(); | |
| } else if (i > 0 || !tail) { | |
| space(); | |
| } | |
| if (/comment[134]/.test(c.type)) { | |
| print("//" + c.value.replace(/[@#]__PURE__/g, ' ')); | |
| need_newline_indented = true; | |
| } else if (c.type == "comment2") { | |
| print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/"); | |
| need_space = true; | |
| } | |
| }); | |
| if (OUTPUT.length > insert) newline_insert = insert; | |
| } | |
| var stack = []; | |
| return { | |
| get : get, | |
| toString : get, | |
| indent : indent, | |
| indentation : function() { return indentation }, | |
| current_width : function() { return current_col - indentation }, | |
| should_break : function() { return options.width && this.current_width() >= options.width }, | |
| has_parens : function() { return has_parens }, | |
| newline : newline, | |
| print : print, | |
| space : space, | |
| comma : comma, | |
| colon : colon, | |
| last : function() { return last }, | |
| semicolon : semicolon, | |
| force_semicolon : force_semicolon, | |
| to_utf8 : to_utf8, | |
| print_name : function(name) { print(make_name(name)) }, | |
| print_string : function(str, quote, escape_directive) { | |
| var encoded = encode_string(str, quote); | |
| if (escape_directive === true && encoded.indexOf("\\") === -1) { | |
| // Insert semicolons to break directive prologue | |
| if (!EXPECT_DIRECTIVE.test(OUTPUT)) { | |
| force_semicolon(); | |
| } | |
| force_semicolon(); | |
| } | |
| print(encoded); | |
| }, | |
| next_indent : next_indent, | |
| 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] }, | |
| prepend_comments: readonly ? noop : prepend_comments, | |
| append_comments : readonly || comment_filter === return_false ? noop : append_comments, | |
| line : function() { return current_line }, | |
| col : function() { return current_col }, | |
| pos : function() { return current_pos }, | |
| push_node : function(node) { stack.push(node) }, | |
| pop_node : options.preserve_line ? function() { | |
| var node = stack.pop(); | |
| if (node.start && node.start.line > current_line) { | |
| insert_newlines(node.start.line - current_line); | |
| } | |
| } : function() { | |
| stack.pop(); | |
| }, | |
| parent : function(n) { | |
| return stack[stack.length - 2 - (n || 0)]; | |
| } | |
| }; | |
| } | |
| /* -----[ code generators ]----- */ | |
| (function() { | |
| /* -----[ utils ]----- */ | |
| function DEFPRINT(nodetype, generator) { | |
| nodetype.DEFMETHOD("_codegen", generator); | |
| } | |
| var in_directive = false; | |
| var active_scope = null; | |
| var use_asm = null; | |
| AST_Node.DEFMETHOD("print", function(stream, force_parens) { | |
| var self = this, generator = self._codegen; | |
| if (self instanceof AST_Scope) { | |
| active_scope = self; | |
| } else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") { | |
| use_asm = active_scope; | |
| } | |
| function doit() { | |
| stream.prepend_comments(self); | |
| self.add_source_map(stream); | |
| generator(self, stream); | |
| stream.append_comments(self); | |
| } | |
| stream.push_node(self); | |
| if (force_parens || self.needs_parens(stream)) { | |
| stream.with_parens(doit); | |
| } else { | |
| doit(); | |
| } | |
| stream.pop_node(); | |
| if (self === use_asm) { | |
| use_asm = null; | |
| } | |
| }); | |
| AST_Node.DEFMETHOD("_print", AST_Node.prototype.print); | |
| AST_Node.DEFMETHOD("print_to_string", function(options) { | |
| var s = OutputStream(options); | |
| this.print(s); | |
| return s.get(); | |
| }); | |
| /* -----[ PARENTHESES ]----- */ | |
| function PARENS(nodetype, func) { | |
| if (Array.isArray(nodetype)) { | |
| nodetype.forEach(function(nodetype) { | |
| PARENS(nodetype, func); | |
| }); | |
| } else { | |
| nodetype.DEFMETHOD("needs_parens", func); | |
| } | |
| } | |
| PARENS(AST_Node, 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) { | |
| if (!output.has_parens() && first_in_statement(output)) return true; | |
| if (output.option('webkit')) { | |
| var p = output.parent(); | |
| if (p instanceof AST_PropAccess && p.expression === this) return true; | |
| } | |
| if (output.option('wrap_iife')) { | |
| var p = output.parent(); | |
| if (p instanceof AST_Call && p.expression === this) return true; | |
| } | |
| }); | |
| // same goes for an object literal, because otherwise it would be | |
| // interpreted as a block of code. | |
| PARENS(AST_Object, function(output) { | |
| return !output.has_parens() && first_in_statement(output); | |
| }); | |
| PARENS(AST_Unary, function(output) { | |
| var p = output.parent(); | |
| return p instanceof AST_PropAccess && p.expression === this | |
| || p instanceof AST_Call && p.expression === this; | |
| }); | |
| PARENS(AST_Sequence, function(output) { | |
| var p = output.parent(); | |
| // (foo, bar)() or foo(1, (2, 3), 4) | |
| return p instanceof AST_Call | |
| // !(foo, bar, baz) | |
| || p instanceof AST_Unary | |
| // 1 + (2, 3) + 4 ==> 8 | |
| || p instanceof AST_Binary | |
| // var a = (1, 2), b = a + a; ==> b == 4 | |
| || p instanceof AST_VarDef | |
| // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 | |
| || p instanceof AST_PropAccess && p.expression === this | |
| // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] | |
| || p instanceof AST_Array | |
| // { foo: (1, 2) }.foo ==> 2 | |
| || p instanceof AST_ObjectProperty | |
| // (false, true) ? (a = 10, b = 20) : (c = 30) | |
| // ==> 20 (side effect, set a := 10 and b := 20) | |
| || p instanceof AST_Conditional; | |
| }); | |
| 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)) { | |
| return true; | |
| } | |
| } | |
| }); | |
| PARENS(AST_PropAccess, function(output) { | |
| var p = output.parent(); | |
| if (p instanceof AST_New && p.expression === this) { | |
| // i.e. new (foo.bar().baz) | |
| // | |
| // if there's one call into this subtree, then we need | |
| // parens around it too, otherwise the call will be | |
| // interpreted as passing the arguments to the upper New | |
| // expression. | |
| var parens = false; | |
| this.walk(new TreeWalker(function(node) { | |
| if (parens || node instanceof AST_Scope) return true; | |
| if (node instanceof AST_Call) { | |
| parens = true; | |
| return true; | |
| } | |
| })); | |
| return parens; | |
| } | |
| }); | |
| PARENS(AST_Call, function(output) { | |
| var p = output.parent(); | |
| if (p instanceof AST_New && p.expression === this) return true; | |
| // https://bugs.webkit.org/show_bug.cgi?id=123506 | |
| if (output.option('webkit')) { | |
| var g = output.parent(1); | |
| return this.expression instanceof AST_Function | |
| && p instanceof AST_PropAccess | |
| && p.expression === this | |
| && g instanceof AST_Assign | |
| && g.left === p; | |
| } | |
| }); | |
| PARENS(AST_New, function(output) { | |
| var p = output.parent(); | |
| if (!need_constructor_parens(this, output) | |
| && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() | |
| || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) | |
| return true; | |
| }); | |
| PARENS(AST_Number, function(output) { | |
| var p = output.parent(); | |
| if (p instanceof AST_PropAccess && p.expression === this) { | |
| var value = this.getValue(); | |
| if (value < 0 || /^0/.test(make_num(value))) { | |
| return true; | |
| } | |
| } | |
| }); | |
| PARENS([ AST_Assign, AST_Conditional ], function(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; | |
| }); | |
| /* -----[ PRINTERS ]----- */ | |
| DEFPRINT(AST_Directive, function(self, output) { | |
| output.print_string(self.value, self.quote); | |
| output.semicolon(); | |
| }); | |
| DEFPRINT(AST_Debugger, function(self, output) { | |
| output.print("debugger"); | |
| output.semicolon(); | |
| }); | |
| /* -----[ statements ]----- */ | |
| function display_body(body, is_toplevel, output, allow_directives) { | |
| var last = body.length - 1; | |
| in_directive = allow_directives; | |
| body.forEach(function(stmt, i) { | |
| if (in_directive === true && !(stmt instanceof AST_Directive || | |
| stmt instanceof AST_EmptyStatement || | |
| (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) | |
| )) { | |
| in_directive = false; | |
| } | |
| if (!(stmt instanceof AST_EmptyStatement)) { | |
| output.indent(); | |
| stmt.print(output); | |
| if (!(i == last && is_toplevel)) { | |
| output.newline(); | |
| if (is_toplevel) output.newline(); | |
| } | |
| } | |
| if (in_directive === true && | |
| stmt instanceof AST_SimpleStatement && | |
| stmt.body instanceof AST_String | |
| ) { | |
| in_directive = false; | |
| } | |
| }); | |
| in_directive = false; | |
| } | |
| 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, true); | |
| output.print(""); | |
| }); | |
| 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_braced_empty(self, output) { | |
| output.print("{"); | |
| output.with_indent(output.next_indent(), function() { | |
| output.append_comments(self, true); | |
| }); | |
| output.print("}"); | |
| } | |
| function print_braced(self, output, allow_directives) { | |
| if (self.body.length > 0) { | |
| output.with_block(function() { | |
| display_body(self.body, false, output, allow_directives); | |
| }); | |
| } else print_braced_empty(self, output); | |
| } | |
| DEFPRINT(AST_BlockStatement, function(self, output) { | |
| print_braced(self, output); | |
| }); | |
| DEFPRINT(AST_EmptyStatement, function(self, output) { | |
| output.semicolon(); | |
| }); | |
| DEFPRINT(AST_Do, function(self, output) { | |
| output.print("do"); | |
| output.space(); | |
| make_block(self.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) { | |
| if (self.init instanceof AST_Definitions) { | |
| self.init.print(output); | |
| } else { | |
| parenthesize_for_noin(self.init, output, true); | |
| } | |
| 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_braced(self, output, true); | |
| }); | |
| DEFPRINT(AST_Lambda, function(self, output) { | |
| self._do_print(output); | |
| }); | |
| /* -----[ jumps ]----- */ | |
| function print_jump(output, kind, target) { | |
| output.print(kind); | |
| if (target) { | |
| output.space(); | |
| target.print(output); | |
| } | |
| output.semicolon(); | |
| } | |
| DEFPRINT(AST_Return, function(self, output) { | |
| print_jump(output, "return", self.value); | |
| }); | |
| DEFPRINT(AST_Throw, function(self, output) { | |
| print_jump(output, "throw", self.value); | |
| }); | |
| DEFPRINT(AST_Break, function(self, output) { | |
| print_jump(output, "break", self.label); | |
| }); | |
| DEFPRINT(AST_Continue, function(self, output) { | |
| print_jump(output, "continue", self.label); | |
| }); | |
| /* -----[ if ]----- */ | |
| function make_then(self, output) { | |
| var b = self.body; | |
| if (output.option("braces") | |
| || output.option("ie8") && b instanceof AST_Do) | |
| return make_block(b, 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 braces if needed. | |
| if (!b) return output.force_semicolon(); | |
| 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; | |
| } | |
| force_statement(self.body, 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(); | |
| if (self.alternative instanceof AST_If) | |
| self.alternative.print(output); | |
| else | |
| 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(); | |
| var last = self.body.length - 1; | |
| if (last < 0) print_braced_empty(self, output); | |
| else output.with_block(function() { | |
| self.body.forEach(function(branch, i) { | |
| output.indent(true); | |
| branch.print(output); | |
| if (i < last && branch.body.length > 0) | |
| output.newline(); | |
| }); | |
| }); | |
| }); | |
| AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) { | |
| 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_braced(self, 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_braced(self, output); | |
| }); | |
| DEFPRINT(AST_Finally, function(self, output) { | |
| output.print("finally"); | |
| output.space(); | |
| print_braced(self, output); | |
| }); | |
| DEFPRINT(AST_Var, function(self, output) { | |
| output.print("var"); | |
| output.space(); | |
| self.definitions.forEach(function(def, i) { | |
| if (i) output.comma(); | |
| def.print(output); | |
| }); | |
| var p = output.parent(); | |
| if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon(); | |
| }); | |
| function parenthesize_for_noin(node, output, noin) { | |
| var parens = false; | |
| // need to take some precautions here: | |
| // https://github.com/mishoo/UglifyJS2/issues/60 | |
| if (noin) node.walk(new TreeWalker(function(node) { | |
| if (parens || node instanceof AST_Scope) return true; | |
| if (node instanceof AST_Binary && node.operator == "in") { | |
| parens = true; | |
| return true; | |
| } | |
| })); | |
| node.print(output, parens); | |
| } | |
| DEFPRINT(AST_VarDef, function(self, output) { | |
| self.name.print(output); | |
| if (self.value) { | |
| output.space(); | |
| output.print("="); | |
| output.space(); | |
| var p = output.parent(1); | |
| var noin = p instanceof AST_For || p instanceof AST_ForIn; | |
| parenthesize_for_noin(self.value, output, noin); | |
| } | |
| }); | |
| /* -----[ other expressions ]----- */ | |
| DEFPRINT(AST_Call, function(self, output) { | |
| self.expression.print(output); | |
| if (self instanceof AST_New && !need_constructor_parens(self, output)) | |
| return; | |
| if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) { | |
| output.add_mapping(self.start); | |
| } | |
| 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._codegen(self, output); | |
| }); | |
| DEFPRINT(AST_Sequence, function(self, output) { | |
| self.expressions.forEach(function(node, index) { | |
| if (index > 0) { | |
| output.comma(); | |
| if (output.should_break()) { | |
| output.newline(); | |
| output.indent(); | |
| } | |
| } | |
| node.print(output); | |
| }); | |
| }); | |
| DEFPRINT(AST_Dot, function(self, output) { | |
| var expr = self.expression; | |
| expr.print(output); | |
| var prop = self.property; | |
| if (output.option("ie8") && RESERVED_WORDS[prop]) { | |
| output.print("["); | |
| output.add_mapping(self.end); | |
| output.print_string(prop); | |
| output.print("]"); | |
| } else { | |
| if (expr instanceof AST_Number && expr.getValue() >= 0) { | |
| if (!/[xa-f.)]/i.test(output.last())) { | |
| output.print("."); | |
| } | |
| } | |
| output.print("."); | |
| // the name after dot would be mapped about here. | |
| output.add_mapping(self.end); | |
| output.print_name(prop); | |
| } | |
| }); | |
| 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) | |
| || (/[+-]$/.test(op) | |
| && self.expression instanceof AST_UnaryPrefix | |
| && /^[+-]/.test(self.expression.operator))) { | |
| 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) { | |
| var op = self.operator; | |
| self.left.print(output); | |
| if (op[0] == ">" /* ">>" ">>>" ">" ">=" */ | |
| && self.left instanceof AST_UnaryPostfix | |
| && self.left.operator == "--") { | |
| // space is mandatory to avoid outputting --> | |
| output.print(" "); | |
| } else { | |
| // the space is optional depending on "beautify" | |
| output.space(); | |
| } | |
| output.print(op); | |
| if ((op == "<" || op == "<<") | |
| && self.right instanceof AST_UnaryPrefix | |
| && self.right.operator == "!" | |
| && self.right.expression instanceof AST_UnaryPrefix | |
| && self.right.expression.operator == "--") { | |
| // space is mandatory to avoid outputting <!-- | |
| output.print(" "); | |
| } else { | |
| // the space is optional depending on "beautify" | |
| 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(); | |
| exp.print(output); | |
| // If the final element is a hole, we need to make sure it | |
| // doesn't look like a trailing comma, by inserting an actual | |
| // trailing comma. | |
| if (i === len - 1 && exp instanceof AST_Hole) | |
| output.comma(); | |
| }); | |
| 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 print_braced_empty(self, output); | |
| }); | |
| function print_property_name(key, quote, output) { | |
| if (output.option("quote_keys")) { | |
| output.print_string(key); | |
| } else if ("" + +key == key && key >= 0) { | |
| output.print(make_num(key)); | |
| } else if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) { | |
| if (quote && output.option("keep_quoted_props")) { | |
| output.print_string(key, quote); | |
| } else { | |
| output.print_name(key); | |
| } | |
| } else { | |
| output.print_string(key, quote); | |
| } | |
| } | |
| DEFPRINT(AST_ObjectKeyVal, function(self, output) { | |
| print_property_name(self.key, self.quote, output); | |
| output.colon(); | |
| self.value.print(output); | |
| }); | |
| AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) { | |
| output.print(type); | |
| output.space(); | |
| print_property_name(this.key.name, this.quote, output); | |
| this.value._do_print(output, true); | |
| }); | |
| DEFPRINT(AST_ObjectSetter, function(self, output) { | |
| self._print_getter_setter("set", output); | |
| }); | |
| DEFPRINT(AST_ObjectGetter, function(self, output) { | |
| self._print_getter_setter("get", output); | |
| }); | |
| DEFPRINT(AST_Symbol, function(self, output) { | |
| var def = self.definition(); | |
| output.print_name(def && def.mangled_name || self.name); | |
| }); | |
| DEFPRINT(AST_Hole, noop); | |
| 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(), self.quote, in_directive); | |
| }); | |
| DEFPRINT(AST_Number, function(self, output) { | |
| if (use_asm && self.start && self.start.raw != null) { | |
| output.print(self.start.raw); | |
| } else { | |
| output.print(make_num(self.getValue())); | |
| } | |
| }); | |
| DEFPRINT(AST_RegExp, function(self, output) { | |
| var regexp = self.getValue(); | |
| var str = regexp.toString(); | |
| if (regexp.raw_source) { | |
| str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/")); | |
| } | |
| output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(seq) { | |
| switch (seq[1]) { | |
| case "\n": return "\\n"; | |
| case "\r": return "\\r"; | |
| case "\t": return "\t"; | |
| case "\b": return "\b"; | |
| case "\f": return "\f"; | |
| case "\0": return "\0"; | |
| case "\x0B": return "\v"; | |
| case "\u2028": return "\\u2028"; | |
| case "\u2029": return "\\u2029"; | |
| default: return seq; | |
| } | |
| }).replace(/[\n\r\u2028\u2029]/g, function(c) { | |
| switch (c) { | |
| case "\n": return "\\n"; | |
| case "\r": return "\\r"; | |
| case "\u2028": return "\\u2028"; | |
| case "\u2029": return "\\u2029"; | |
| } | |
| })); | |
| var p = output.parent(); | |
| if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self) | |
| output.print(" "); | |
| }); | |
| function force_statement(stat, output) { | |
| if (output.option("braces")) { | |
| make_block(stat, output); | |
| } else { | |
| if (!stat || stat instanceof AST_EmptyStatement) | |
| output.force_semicolon(); | |
| else | |
| stat.print(output); | |
| } | |
| } | |
| // self should be AST_New. decide if we want to show parens or not. | |
| function need_constructor_parens(self, output) { | |
| // Always print parentheses with arguments | |
| if (self.args.length > 0) return true; | |
| return 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).replace(/^0\./, ".").replace("e+", "e"); | |
| var candidates = [ str ]; | |
| if (Math.floor(num) === num) { | |
| if (num < 0) { | |
| candidates.push("-0x" + (-num).toString(16).toLowerCase()); | |
| } else { | |
| candidates.push("0x" + num.toString(16).toLowerCase()); | |
| } | |
| } | |
| var match, len, digits; | |
| if (match = /^\.0+/.exec(str)) { | |
| len = match[0].length; | |
| digits = str.slice(len); | |
| candidates.push(digits + "e-" + (digits.length + len - 1)); | |
| } else if (match = /0+$/.exec(str)) { | |
| len = match[0].length; | |
| candidates.push(str.slice(0, -len) + "e" + len); | |
| } else if (match = /^(\d)\.(\d+)e(-?\d+)$/.exec(str)) { | |
| candidates.push(match[1] + match[2] + "e" + (match[3] - match[2].length)); | |
| } | |
| return best_of(candidates); | |
| } | |
| function make_block(stmt, output) { | |
| if (!stmt || stmt instanceof AST_EmptyStatement) | |
| output.print("{}"); | |
| else if (stmt instanceof AST_BlockStatement) | |
| stmt.print(output); | |
| else output.with_block(function() { | |
| output.indent(); | |
| stmt.print(output); | |
| output.newline(); | |
| }); | |
| } | |
| /* -----[ source map generators ]----- */ | |
| function DEFMAP(nodetype, generator) { | |
| nodetype.forEach(function(nodetype) { | |
| nodetype.DEFMETHOD("add_source_map", generator); | |
| }); | |
| } | |
| DEFMAP([ | |
| // 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. | |
| AST_Node, | |
| // since the label symbol will mark it | |
| AST_LabeledStatement, | |
| AST_Toplevel, | |
| ], noop); | |
| // XXX: I'm not exactly sure if we need it for all of these nodes, | |
| // or if we should add even more. | |
| DEFMAP([ | |
| AST_Array, | |
| AST_BlockStatement, | |
| AST_Catch, | |
| AST_Constant, | |
| AST_Debugger, | |
| AST_Definitions, | |
| AST_Directive, | |
| AST_Finally, | |
| AST_Jump, | |
| AST_Lambda, | |
| AST_New, | |
| AST_Object, | |
| AST_StatementWithBody, | |
| AST_Symbol, | |
| AST_Switch, | |
| AST_SwitchBranch, | |
| AST_Try, | |
| ], function(output) { | |
| output.add_mapping(this.start); | |
| }); | |
| DEFMAP([ | |
| AST_ObjectGetter, | |
| AST_ObjectSetter, | |
| ], function(output) { | |
| output.add_mapping(this.start, this.key.name); | |
| }); | |
| DEFMAP([ AST_ObjectProperty ], function(output) { | |
| output.add_mapping(this.start, this.key); | |
| }); | |
| })(); |