1/***********************************************************************
2
3  A JavaScript tokenizer / parser / beautifier / compressor.
4  https://github.com/mishoo/UglifyJS2
5
6  -------------------------------- (C) ---------------------------------
7
8                           Author: Mihai Bazon
9                         <mihai.bazon@gmail.com>
10                       http://mihai.bazon.net/blog
11
12  Distributed under the BSD license:
13
14    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16    Redistribution and use in source and binary forms, with or without
17    modification, are permitted provided that the following conditions
18    are met:
19
20        * Redistributions of source code must retain the above
21          copyright notice, this list of conditions and the following
22          disclaimer.
23
24        * Redistributions in binary form must reproduce the above
25          copyright notice, this list of conditions and the following
26          disclaimer in the documentation and/or other materials
27          provided with the distribution.
28
29    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32    PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40    SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46(function(){
47
48    var normalize_directives = function(body) {
49        var in_directive = true;
50
51        for (var i = 0; i < body.length; i++) {
52            if (in_directive && body[i] instanceof AST_Statement && body[i].body instanceof AST_String) {
53                body[i] = new AST_Directive({
54                    start: body[i].start,
55                    end: body[i].end,
56                    value: body[i].body.value
57                });
58            } else if (in_directive && !(body[i] instanceof AST_Statement && body[i].body instanceof AST_String)) {
59                in_directive = false;
60            }
61        }
62
63        return body;
64    };
65
66    var MOZ_TO_ME = {
67        Program: function(M) {
68            return new AST_Toplevel({
69                start: my_start_token(M),
70                end: my_end_token(M),
71                body: normalize_directives(M.body.map(from_moz))
72            });
73        },
74        FunctionDeclaration: function(M) {
75            return new AST_Defun({
76                start: my_start_token(M),
77                end: my_end_token(M),
78                name: from_moz(M.id),
79                argnames: M.params.map(from_moz),
80                body: normalize_directives(from_moz(M.body).body)
81            });
82        },
83        FunctionExpression: function(M) {
84            return new AST_Function({
85                start: my_start_token(M),
86                end: my_end_token(M),
87                name: from_moz(M.id),
88                argnames: M.params.map(from_moz),
89                body: normalize_directives(from_moz(M.body).body)
90            });
91        },
92        ExpressionStatement: function(M) {
93            return new AST_SimpleStatement({
94                start: my_start_token(M),
95                end: my_end_token(M),
96                body: from_moz(M.expression)
97            });
98        },
99        TryStatement: function(M) {
100            var handlers = M.handlers || [M.handler];
101            if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
102                throw new Error("Multiple catch clauses are not supported.");
103            }
104            return new AST_Try({
105                start    : my_start_token(M),
106                end      : my_end_token(M),
107                body     : from_moz(M.block).body,
108                bcatch   : from_moz(handlers[0]),
109                bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
110            });
111        },
112        Property: function(M) {
113            var key = M.key;
114            var args = {
115                start    : my_start_token(key),
116                end      : my_end_token(M.value),
117                key      : key.type == "Identifier" ? key.name : key.value,
118                value    : from_moz(M.value)
119            };
120            if (M.kind == "init") return new AST_ObjectKeyVal(args);
121            args.key = new AST_SymbolAccessor({
122                name: args.key
123            });
124            args.value = new AST_Accessor(args.value);
125            if (M.kind == "get") return new AST_ObjectGetter(args);
126            if (M.kind == "set") return new AST_ObjectSetter(args);
127        },
128        ArrayExpression: function(M) {
129            return new AST_Array({
130                start    : my_start_token(M),
131                end      : my_end_token(M),
132                elements : M.elements.map(function(elem){
133                    return elem === null ? new AST_Hole() : from_moz(elem);
134                })
135            });
136        },
137        ObjectExpression: function(M) {
138            return new AST_Object({
139                start      : my_start_token(M),
140                end        : my_end_token(M),
141                properties : M.properties.map(function(prop){
142                    prop.type = "Property";
143                    return from_moz(prop)
144                })
145            });
146        },
147        SequenceExpression: function(M) {
148            return AST_Seq.from_array(M.expressions.map(from_moz));
149        },
150        MemberExpression: function(M) {
151            return new (M.computed ? AST_Sub : AST_Dot)({
152                start      : my_start_token(M),
153                end        : my_end_token(M),
154                property   : M.computed ? from_moz(M.property) : M.property.name,
155                expression : from_moz(M.object)
156            });
157        },
158        SwitchCase: function(M) {
159            return new (M.test ? AST_Case : AST_Default)({
160                start      : my_start_token(M),
161                end        : my_end_token(M),
162                expression : from_moz(M.test),
163                body       : M.consequent.map(from_moz)
164            });
165        },
166        VariableDeclaration: function(M) {
167            return new (M.kind === "const" ? AST_Const : AST_Var)({
168                start       : my_start_token(M),
169                end         : my_end_token(M),
170                definitions : M.declarations.map(from_moz)
171            });
172        },
173        Literal: function(M) {
174            var val = M.value, args = {
175                start  : my_start_token(M),
176                end    : my_end_token(M)
177            };
178            if (val === null) return new AST_Null(args);
179            switch (typeof val) {
180              case "string":
181                args.value = val;
182                return new AST_String(args);
183              case "number":
184                args.value = val;
185                return new AST_Number(args);
186              case "boolean":
187                return new (val ? AST_True : AST_False)(args);
188              default:
189                var rx = M.regex;
190                if (rx && rx.pattern) {
191                    // RegExpLiteral as per ESTree AST spec
192                    args.value = new RegExp(rx.pattern, rx.flags).toString();
193                } else {
194                    // support legacy RegExp
195                    args.value = M.regex && M.raw ? M.raw : val;
196                }
197                return new AST_RegExp(args);
198            }
199        },
200        Identifier: function(M) {
201            var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
202            return new (  p.type == "LabeledStatement" ? AST_Label
203                        : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
204                        : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
205                        : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
206                        : p.type == "CatchClause" ? AST_SymbolCatch
207                        : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef
208                        : AST_SymbolRef)({
209                            start : my_start_token(M),
210                            end   : my_end_token(M),
211                            name  : M.name
212                        });
213        }
214    };
215
216    MOZ_TO_ME.UpdateExpression =
217    MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
218        var prefix = "prefix" in M ? M.prefix
219            : M.type == "UnaryExpression" ? true : false;
220        return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
221            start      : my_start_token(M),
222            end        : my_end_token(M),
223            operator   : M.operator,
224            expression : from_moz(M.argument)
225        });
226    };
227
228    map("EmptyStatement", AST_EmptyStatement);
229    map("BlockStatement", AST_BlockStatement, "body@body");
230    map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
231    map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
232    map("BreakStatement", AST_Break, "label>label");
233    map("ContinueStatement", AST_Continue, "label>label");
234    map("WithStatement", AST_With, "object>expression, body>body");
235    map("SwitchStatement", AST_Switch, "discriminant>expression, cases@body");
236    map("ReturnStatement", AST_Return, "argument>value");
237    map("ThrowStatement", AST_Throw, "argument>value");
238    map("WhileStatement", AST_While, "test>condition, body>body");
239    map("DoWhileStatement", AST_Do, "test>condition, body>body");
240    map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body");
241    map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
242    map("DebuggerStatement", AST_Debugger);
243    map("VariableDeclarator", AST_VarDef, "id>name, init>value");
244    map("CatchClause", AST_Catch, "param>argname, body%body");
245
246    map("ThisExpression", AST_This);
247    map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
248    map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
249    map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
250    map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
251    map("NewExpression", AST_New, "callee>expression, arguments@args");
252    map("CallExpression", AST_Call, "callee>expression, arguments@args");
253
254    def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
255        return to_moz_scope("Program", M);
256    });
257
258    def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
259        return {
260            type: "FunctionDeclaration",
261            id: to_moz(M.name),
262            params: M.argnames.map(to_moz),
263            body: to_moz_scope("BlockStatement", M)
264        }
265    });
266
267    def_to_moz(AST_Function, function To_Moz_FunctionExpression(M) {
268        return {
269            type: "FunctionExpression",
270            id: to_moz(M.name),
271            params: M.argnames.map(to_moz),
272            body: to_moz_scope("BlockStatement", M)
273        }
274    });
275
276    def_to_moz(AST_Directive, function To_Moz_Directive(M) {
277        return {
278            type: "ExpressionStatement",
279            expression: {
280                type: "Literal",
281                value: M.value
282            }
283        };
284    });
285
286    def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
287        return {
288            type: "ExpressionStatement",
289            expression: to_moz(M.body)
290        };
291    });
292
293    def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
294        return {
295            type: "SwitchCase",
296            test: to_moz(M.expression),
297            consequent: M.body.map(to_moz)
298        };
299    });
300
301    def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
302        return {
303            type: "TryStatement",
304            block: to_moz_block(M),
305            handler: to_moz(M.bcatch),
306            guardedHandlers: [],
307            finalizer: to_moz(M.bfinally)
308        };
309    });
310
311    def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
312        return {
313            type: "CatchClause",
314            param: to_moz(M.argname),
315            guard: null,
316            body: to_moz_block(M)
317        };
318    });
319
320    def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
321        return {
322            type: "VariableDeclaration",
323            kind: M instanceof AST_Const ? "const" : "var",
324            declarations: M.definitions.map(to_moz)
325        };
326    });
327
328    def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
329        return {
330            type: "SequenceExpression",
331            expressions: M.to_array().map(to_moz)
332        };
333    });
334
335    def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
336        var isComputed = M instanceof AST_Sub;
337        return {
338            type: "MemberExpression",
339            object: to_moz(M.expression),
340            computed: isComputed,
341            property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
342        };
343    });
344
345    def_to_moz(AST_Unary, function To_Moz_Unary(M) {
346        return {
347            type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
348            operator: M.operator,
349            prefix: M instanceof AST_UnaryPrefix,
350            argument: to_moz(M.expression)
351        };
352    });
353
354    def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
355        return {
356            type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
357            left: to_moz(M.left),
358            operator: M.operator,
359            right: to_moz(M.right)
360        };
361    });
362
363    def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) {
364        return {
365            type: "ArrayExpression",
366            elements: M.elements.map(to_moz)
367        };
368    });
369
370    def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
371        return {
372            type: "ObjectExpression",
373            properties: M.properties.map(to_moz)
374        };
375    });
376
377    def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
378        var key = {
379            type: "Literal",
380            value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
381        };
382        var kind;
383        if (M instanceof AST_ObjectKeyVal) {
384            kind = "init";
385        } else
386        if (M instanceof AST_ObjectGetter) {
387            kind = "get";
388        } else
389        if (M instanceof AST_ObjectSetter) {
390            kind = "set";
391        }
392        return {
393            type: "Property",
394            kind: kind,
395            key: key,
396            value: to_moz(M.value)
397        };
398    });
399
400    def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
401        var def = M.definition();
402        return {
403            type: "Identifier",
404            name: def ? def.mangled_name || def.name : M.name
405        };
406    });
407
408    def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
409        var value = M.value;
410        return {
411            type: "Literal",
412            value: value,
413            raw: value.toString(),
414            regex: {
415                pattern: value.source,
416                flags: value.toString().match(/[gimuy]*$/)[0]
417            }
418        };
419    });
420
421    def_to_moz(AST_Constant, function To_Moz_Literal(M) {
422        var value = M.value;
423        if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
424            return {
425                type: "UnaryExpression",
426                operator: "-",
427                prefix: true,
428                argument: {
429                    type: "Literal",
430                    value: -value,
431                    raw: M.start.raw
432                }
433            };
434        }
435        return {
436            type: "Literal",
437            value: value,
438            raw: M.start.raw
439        };
440    });
441
442    def_to_moz(AST_Atom, function To_Moz_Atom(M) {
443        return {
444            type: "Identifier",
445            name: String(M.value)
446        };
447    });
448
449    AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
450    AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
451    AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
452
453    AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
454    AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
455
456    /* -----[ tools ]----- */
457
458    function raw_token(moznode) {
459        if (moznode.type == "Literal") {
460            return moznode.raw != null ? moznode.raw : moznode.value + "";
461        }
462    }
463
464    function my_start_token(moznode) {
465        var loc = moznode.loc, start = loc && loc.start;
466        var range = moznode.range;
467        return new AST_Token({
468            file    : loc && loc.source,
469            line    : start && start.line,
470            col     : start && start.column,
471            pos     : range ? range[0] : moznode.start,
472            endline : start && start.line,
473            endcol  : start && start.column,
474            endpos  : range ? range[0] : moznode.start,
475            raw     : raw_token(moznode),
476        });
477    };
478
479    function my_end_token(moznode) {
480        var loc = moznode.loc, end = loc && loc.end;
481        var range = moznode.range;
482        return new AST_Token({
483            file    : loc && loc.source,
484            line    : end && end.line,
485            col     : end && end.column,
486            pos     : range ? range[1] : moznode.end,
487            endline : end && end.line,
488            endcol  : end && end.column,
489            endpos  : range ? range[1] : moznode.end,
490            raw     : raw_token(moznode),
491        });
492    };
493
494    function map(moztype, mytype, propmap) {
495        var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
496        moz_to_me += "return new U2." + mytype.name + "({\n" +
497            "start: my_start_token(M),\n" +
498            "end: my_end_token(M)";
499
500        var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
501        me_to_moz += "return {\n" +
502            "type: " + JSON.stringify(moztype);
503
504        if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
505            var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
506            if (!m) throw new Error("Can't understand property map: " + prop);
507            var moz = m[1], how = m[2], my = m[3];
508            moz_to_me += ",\n" + my + ": ";
509            me_to_moz += ",\n" + moz + ": ";
510            switch (how) {
511                case "@":
512                    moz_to_me += "M." + moz + ".map(from_moz)";
513                    me_to_moz += "M." +  my + ".map(to_moz)";
514                    break;
515                case ">":
516                    moz_to_me += "from_moz(M." + moz + ")";
517                    me_to_moz += "to_moz(M." + my + ")";
518                    break;
519                case "=":
520                    moz_to_me += "M." + moz;
521                    me_to_moz += "M." + my;
522                    break;
523                case "%":
524                    moz_to_me += "from_moz(M." + moz + ").body";
525                    me_to_moz += "to_moz_block(M)";
526                    break;
527                default:
528                    throw new Error("Can't understand operator in propmap: " + prop);
529            }
530        });
531
532        moz_to_me += "\n})\n}";
533        me_to_moz += "\n}\n}";
534
535        //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
536        //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
537        //console.log(moz_to_me);
538
539        moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
540            exports, my_start_token, my_end_token, from_moz
541        );
542        me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
543            to_moz, to_moz_block, to_moz_scope
544        );
545        MOZ_TO_ME[moztype] = moz_to_me;
546        def_to_moz(mytype, me_to_moz);
547    };
548
549    var FROM_MOZ_STACK = null;
550
551    function from_moz(node) {
552        FROM_MOZ_STACK.push(node);
553        var ret = node != null ? MOZ_TO_ME[node.type](node) : null;
554        FROM_MOZ_STACK.pop();
555        return ret;
556    };
557
558    AST_Node.from_mozilla_ast = function(node){
559        var save_stack = FROM_MOZ_STACK;
560        FROM_MOZ_STACK = [];
561        var ast = from_moz(node);
562        FROM_MOZ_STACK = save_stack;
563        return ast;
564    };
565
566    function set_moz_loc(mynode, moznode, myparent) {
567        var start = mynode.start;
568        var end = mynode.end;
569        if (start.pos != null && end.endpos != null) {
570            moznode.range = [start.pos, end.endpos];
571        }
572        if (start.line) {
573            moznode.loc = {
574                start: {line: start.line, column: start.col},
575                end: end.endline ? {line: end.endline, column: end.endcol} : null
576            };
577            if (start.file) {
578                moznode.loc.source = start.file;
579            }
580        }
581        return moznode;
582    };
583
584    function def_to_moz(mytype, handler) {
585        mytype.DEFMETHOD("to_mozilla_ast", function() {
586            return set_moz_loc(this, handler(this));
587        });
588    };
589
590    function to_moz(node) {
591        return node != null ? node.to_mozilla_ast() : null;
592    };
593
594    function to_moz_block(node) {
595        return {
596            type: "BlockStatement",
597            body: node.body.map(to_moz)
598        };
599    };
600
601    function to_moz_scope(type, node) {
602        var body = node.body.map(to_moz);
603        if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
604            body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
605        }
606        return {
607            type: type,
608            body: body
609        };
610    };
611})();
612