Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 // Acorn: Loose parser
2 //
3 // This module provides an alternative parser (`parse_dammit`) that
4 // exposes that same interface as `parse`, but will try to parse
5 // anything as JavaScript, repairing syntax error the best it can.
6 // There are circumstances in which it will raise an error and give
7 // up, but they are very rare. The resulting AST will be a mostly
8 // valid JavaScript AST (as per the [Mozilla parser API][api], except
9 // that:
10 //
11 // - Return outside functions is allowed
12 //
13 // - Label consistency (no conflicts, break only to existing labels)
14 // is not enforced.
15 //
16 // - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
17 // the parser got too confused to return anything meaningful.
18 //
19 // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
20 //
21 // The expected use for this is to *first* try `acorn.parse`, and only
22 // if that fails switch to `parse_dammit`. The loose parser might
23 // parse badly indented code incorrectly, so **don't** use it as
24 // your default parser.
25 //
26 // Quite a lot of acorn.js is duplicated here. The alternative was to
27 // add a *lot* of extra cruft to that file, making it less readable
28 // and slower. Copying and editing the code allowed me to make
29 // invasive changes and simplifications without creating a complicated
30 // tangle.
32 (function(root, mod) {
33 if (typeof exports == "object" && typeof module == "object") return mod(exports, require("./acorn")); // CommonJS
34 if (typeof define == "function" && define.amd) return define(["exports", "./acorn"], mod); // AMD
35 mod(root.acorn || (root.acorn = {}), root.acorn); // Plain browser env
36 })(this, function(exports, acorn) {
37 "use strict";
39 var tt = acorn.tokTypes;
41 var options, input, fetchToken, context;
43 exports.parse_dammit = function(inpt, opts) {
44 if (!opts) opts = {};
45 input = String(inpt);
46 options = opts;
47 if (!opts.tabSize) opts.tabSize = 4;
48 fetchToken = acorn.tokenize(inpt, opts);
49 sourceFile = options.sourceFile || null;
50 context = [];
51 nextLineStart = 0;
52 ahead.length = 0;
53 next();
54 return parseTopLevel();
55 };
57 var lastEnd, token = {start: 0, end: 0}, ahead = [];
58 var curLineStart, nextLineStart, curIndent, lastEndLoc, sourceFile;
60 function next() {
61 lastEnd = token.end;
62 if (options.locations)
63 lastEndLoc = token.endLoc;
65 if (ahead.length)
66 token = ahead.shift();
67 else
68 token = readToken();
70 if (token.start >= nextLineStart) {
71 while (token.start >= nextLineStart) {
72 curLineStart = nextLineStart;
73 nextLineStart = lineEnd(curLineStart) + 1;
74 }
75 curIndent = indentationAfter(curLineStart);
76 }
77 }
79 function readToken() {
80 for (;;) {
81 try {
82 return fetchToken();
83 } catch(e) {
84 if (!(e instanceof SyntaxError)) throw e;
86 // Try to skip some text, based on the error message, and then continue
87 var msg = e.message, pos = e.raisedAt, replace = true;
88 if (/unterminated/i.test(msg)) {
89 pos = lineEnd(e.pos);
90 if (/string/.test(msg)) {
91 replace = {start: e.pos, end: pos, type: tt.string, value: input.slice(e.pos + 1, pos)};
92 } else if (/regular expr/i.test(msg)) {
93 var re = input.slice(e.pos, pos);
94 try { re = new RegExp(re); } catch(e) {}
95 replace = {start: e.pos, end: pos, type: tt.regexp, value: re};
96 } else {
97 replace = false;
98 }
99 } else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) {
100 while (pos < input.length && !isSpace(input.charCodeAt(pos))) ++pos;
101 } else if (/character escape|expected hexadecimal/i.test(msg)) {
102 while (pos < input.length) {
103 var ch = input.charCodeAt(pos++);
104 if (ch === 34 || ch === 39 || isNewline(ch)) break;
105 }
106 } else if (/unexpected character/i.test(msg)) {
107 pos++;
108 replace = false;
109 } else {
110 throw e;
111 }
112 resetTo(pos);
113 if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"};
114 if (replace) {
115 if (options.locations) {
116 replace.startLoc = acorn.getLineInfo(input, replace.start);
117 replace.endLoc = acorn.getLineInfo(input, replace.end);
118 }
119 return replace;
120 }
121 }
122 }
123 }
125 function resetTo(pos) {
126 var ch = input.charAt(pos - 1);
127 var reAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) ||
128 /[enwfd]/.test(ch) && /\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(input.slice(pos - 10, pos));
129 fetchToken.jumpTo(pos, reAllowed);
130 }
132 function copyToken(token) {
133 var copy = {start: token.start, end: token.end, type: token.type, value: token.value};
134 if (options.locations) {
135 copy.startLoc = token.startLoc;
136 copy.endLoc = token.endLoc;
137 }
138 return copy;
139 }
141 function lookAhead(n) {
142 // Copy token objects, because fetchToken will overwrite the one
143 // it returns, and in this case we still need it
144 if (!ahead.length)
145 token = copyToken(token);
146 while (n > ahead.length)
147 ahead.push(copyToken(readToken()));
148 return ahead[n-1];
149 }
151 var newline = /[\n\r\u2028\u2029]/;
153 function isNewline(ch) {
154 return ch === 10 || ch === 13 || ch === 8232 || ch === 8329;
155 }
156 function isSpace(ch) {
157 return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewline(ch);
158 }
160 function pushCx() {
161 context.push(curIndent);
162 }
163 function popCx() {
164 curIndent = context.pop();
165 }
167 function lineEnd(pos) {
168 while (pos < input.length && !isNewline(input.charCodeAt(pos))) ++pos;
169 return pos;
170 }
171 function indentationAfter(pos) {
172 for (var count = 0;; ++pos) {
173 var ch = input.charCodeAt(pos);
174 if (ch === 32) ++count;
175 else if (ch === 9) count += options.tabSize;
176 else return count;
177 }
178 }
180 function closes(closeTok, indent, line, blockHeuristic) {
181 if (token.type === closeTok || token.type === tt.eof) return true;
182 if (line != curLineStart && curIndent < indent && tokenStartsLine() &&
183 (!blockHeuristic || nextLineStart >= input.length ||
184 indentationAfter(nextLineStart) < indent)) return true;
185 return false;
186 }
188 function tokenStartsLine() {
189 for (var p = token.start - 1; p >= curLineStart; --p) {
190 var ch = input.charCodeAt(p);
191 if (ch !== 9 && ch !== 32) return false;
192 }
193 return true;
194 }
196 function node_t(start) {
197 this.type = null;
198 this.start = start;
199 this.end = null;
200 }
202 function node_loc_t(start) {
203 this.start = start || token.startLoc || {line: 1, column: 0};
204 this.end = null;
205 if (sourceFile !== null) this.source = sourceFile;
206 }
208 function startNode() {
209 var node = new node_t(token.start);
210 if (options.locations)
211 node.loc = new node_loc_t();
212 if (options.directSourceFile)
213 node.sourceFile = options.directSourceFile;
214 return node;
215 }
217 function startNodeFrom(other) {
218 var node = new node_t(other.start);
219 if (options.locations)
220 node.loc = new node_loc_t(other.loc.start);
221 return node;
222 }
224 function finishNode(node, type) {
225 node.type = type;
226 node.end = lastEnd;
227 if (options.locations)
228 node.loc.end = lastEndLoc;
229 return node;
230 }
232 function getDummyLoc() {
233 if (options.locations) {
234 var loc = new node_loc_t();
235 loc.end = loc.start;
236 return loc;
237 }
238 };
240 function dummyIdent() {
241 var dummy = new node_t(token.start);
242 dummy.type = "Identifier";
243 dummy.end = token.start;
244 dummy.name = "✖";
245 dummy.loc = getDummyLoc();
246 return dummy;
247 }
248 function isDummy(node) { return node.name == "✖"; }
250 function eat(type) {
251 if (token.type === type) {
252 next();
253 return true;
254 }
255 }
257 function canInsertSemicolon() {
258 return (token.type === tt.eof || token.type === tt.braceR || newline.test(input.slice(lastEnd, token.start)));
259 }
260 function semicolon() {
261 eat(tt.semi);
262 }
264 function expect(type) {
265 if (eat(type)) return true;
266 if (lookAhead(1).type == type) {
267 next(); next();
268 return true;
269 }
270 if (lookAhead(2).type == type) {
271 next(); next(); next();
272 return true;
273 }
274 }
276 function checkLVal(expr) {
277 if (expr.type === "Identifier" || expr.type === "MemberExpression") return expr;
278 return dummyIdent();
279 }
281 function parseTopLevel() {
282 var node = startNode();
283 node.body = [];
284 while (token.type !== tt.eof) node.body.push(parseStatement());
285 return finishNode(node, "Program");
286 }
288 function parseStatement() {
289 var starttype = token.type, node = startNode();
291 switch (starttype) {
292 case tt._break: case tt._continue:
293 next();
294 var isBreak = starttype === tt._break;
295 node.label = token.type === tt.name ? parseIdent() : null;
296 semicolon();
297 return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
299 case tt._debugger:
300 next();
301 semicolon();
302 return finishNode(node, "DebuggerStatement");
304 case tt._do:
305 next();
306 node.body = parseStatement();
307 node.test = eat(tt._while) ? parseParenExpression() : dummyIdent();
308 semicolon();
309 return finishNode(node, "DoWhileStatement");
311 case tt._for:
312 next();
313 pushCx();
314 expect(tt.parenL);
315 if (token.type === tt.semi) return parseFor(node, null);
316 if (token.type === tt._var) {
317 var init = startNode();
318 next();
319 parseVar(init, true);
320 if (init.declarations.length === 1 && eat(tt._in))
321 return parseForIn(node, init);
322 return parseFor(node, init);
323 }
324 var init = parseExpression(false, true);
325 if (eat(tt._in)) {return parseForIn(node, checkLVal(init));}
326 return parseFor(node, init);
328 case tt._function:
329 next();
330 return parseFunction(node, true);
332 case tt._if:
333 next();
334 node.test = parseParenExpression();
335 node.consequent = parseStatement();
336 node.alternate = eat(tt._else) ? parseStatement() : null;
337 return finishNode(node, "IfStatement");
339 case tt._return:
340 next();
341 if (eat(tt.semi) || canInsertSemicolon()) node.argument = null;
342 else { node.argument = parseExpression(); semicolon(); }
343 return finishNode(node, "ReturnStatement");
345 case tt._switch:
346 var blockIndent = curIndent, line = curLineStart;
347 next();
348 node.discriminant = parseParenExpression();
349 node.cases = [];
350 pushCx();
351 expect(tt.braceL);
353 for (var cur; !closes(tt.braceR, blockIndent, line, true);) {
354 if (token.type === tt._case || token.type === tt._default) {
355 var isCase = token.type === tt._case;
356 if (cur) finishNode(cur, "SwitchCase");
357 node.cases.push(cur = startNode());
358 cur.consequent = [];
359 next();
360 if (isCase) cur.test = parseExpression();
361 else cur.test = null;
362 expect(tt.colon);
363 } else {
364 if (!cur) {
365 node.cases.push(cur = startNode());
366 cur.consequent = [];
367 cur.test = null;
368 }
369 cur.consequent.push(parseStatement());
370 }
371 }
372 if (cur) finishNode(cur, "SwitchCase");
373 popCx();
374 eat(tt.braceR);
375 return finishNode(node, "SwitchStatement");
377 case tt._throw:
378 next();
379 node.argument = parseExpression();
380 semicolon();
381 return finishNode(node, "ThrowStatement");
383 case tt._try:
384 next();
385 node.block = parseBlock();
386 node.handler = null;
387 if (token.type === tt._catch) {
388 var clause = startNode();
389 next();
390 expect(tt.parenL);
391 clause.param = parseIdent();
392 expect(tt.parenR);
393 clause.guard = null;
394 clause.body = parseBlock();
395 node.handler = finishNode(clause, "CatchClause");
396 }
397 node.finalizer = eat(tt._finally) ? parseBlock() : null;
398 if (!node.handler && !node.finalizer) return node.block;
399 return finishNode(node, "TryStatement");
401 case tt._var:
402 next();
403 node = parseVar(node);
404 semicolon();
405 return node;
407 case tt._while:
408 next();
409 node.test = parseParenExpression();
410 node.body = parseStatement();
411 return finishNode(node, "WhileStatement");
413 case tt._with:
414 next();
415 node.object = parseParenExpression();
416 node.body = parseStatement();
417 return finishNode(node, "WithStatement");
419 case tt.braceL:
420 return parseBlock();
422 case tt.semi:
423 next();
424 return finishNode(node, "EmptyStatement");
426 default:
427 var expr = parseExpression();
428 if (isDummy(expr)) {
429 next();
430 if (token.type === tt.eof) return finishNode(node, "EmptyStatement");
431 return parseStatement();
432 } else if (starttype === tt.name && expr.type === "Identifier" && eat(tt.colon)) {
433 node.body = parseStatement();
434 node.label = expr;
435 return finishNode(node, "LabeledStatement");
436 } else {
437 node.expression = expr;
438 semicolon();
439 return finishNode(node, "ExpressionStatement");
440 }
441 }
442 }
444 function parseBlock() {
445 var node = startNode();
446 pushCx();
447 expect(tt.braceL);
448 var blockIndent = curIndent, line = curLineStart;
449 node.body = [];
450 while (!closes(tt.braceR, blockIndent, line, true))
451 node.body.push(parseStatement());
452 popCx();
453 eat(tt.braceR);
454 return finishNode(node, "BlockStatement");
455 }
457 function parseFor(node, init) {
458 node.init = init;
459 node.test = node.update = null;
460 if (eat(tt.semi) && token.type !== tt.semi) node.test = parseExpression();
461 if (eat(tt.semi) && token.type !== tt.parenR) node.update = parseExpression();
462 popCx();
463 expect(tt.parenR);
464 node.body = parseStatement();
465 return finishNode(node, "ForStatement");
466 }
468 function parseForIn(node, init) {
469 node.left = init;
470 node.right = parseExpression();
471 popCx();
472 expect(tt.parenR);
473 node.body = parseStatement();
474 return finishNode(node, "ForInStatement");
475 }
477 function parseVar(node, noIn) {
478 node.declarations = [];
479 node.kind = "var";
480 while (token.type === tt.name) {
481 var decl = startNode();
482 decl.id = parseIdent();
483 decl.init = eat(tt.eq) ? parseExpression(true, noIn) : null;
484 node.declarations.push(finishNode(decl, "VariableDeclarator"));
485 if (!eat(tt.comma)) break;
486 }
487 if (!node.declarations.length) {
488 var decl = startNode();
489 decl.id = dummyIdent();
490 node.declarations.push(finishNode(decl, "VariableDeclarator"));
491 }
492 return finishNode(node, "VariableDeclaration");
493 }
495 function parseExpression(noComma, noIn) {
496 var expr = parseMaybeAssign(noIn);
497 if (!noComma && token.type === tt.comma) {
498 var node = startNodeFrom(expr);
499 node.expressions = [expr];
500 while (eat(tt.comma)) node.expressions.push(parseMaybeAssign(noIn));
501 return finishNode(node, "SequenceExpression");
502 }
503 return expr;
504 }
506 function parseParenExpression() {
507 pushCx();
508 expect(tt.parenL);
509 var val = parseExpression();
510 popCx();
511 expect(tt.parenR);
512 return val;
513 }
515 function parseMaybeAssign(noIn) {
516 var left = parseMaybeConditional(noIn);
517 if (token.type.isAssign) {
518 var node = startNodeFrom(left);
519 node.operator = token.value;
520 node.left = checkLVal(left);
521 next();
522 node.right = parseMaybeAssign(noIn);
523 return finishNode(node, "AssignmentExpression");
524 }
525 return left;
526 }
528 function parseMaybeConditional(noIn) {
529 var expr = parseExprOps(noIn);
530 if (eat(tt.question)) {
531 var node = startNodeFrom(expr);
532 node.test = expr;
533 node.consequent = parseExpression(true);
534 node.alternate = expect(tt.colon) ? parseExpression(true, noIn) : dummyIdent();
535 return finishNode(node, "ConditionalExpression");
536 }
537 return expr;
538 }
540 function parseExprOps(noIn) {
541 var indent = curIndent, line = curLineStart;
542 return parseExprOp(parseMaybeUnary(noIn), -1, noIn, indent, line);
543 }
545 function parseExprOp(left, minPrec, noIn, indent, line) {
546 if (curLineStart != line && curIndent < indent && tokenStartsLine()) return left;
547 var prec = token.type.binop;
548 if (prec != null && (!noIn || token.type !== tt._in)) {
549 if (prec > minPrec) {
550 var node = startNodeFrom(left);
551 node.left = left;
552 node.operator = token.value;
553 next();
554 if (curLineStart != line && curIndent < indent && tokenStartsLine())
555 node.right = dummyIdent();
556 else
557 node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn, indent, line);
558 var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
559 return parseExprOp(node, minPrec, noIn, indent, line);
560 }
561 }
562 return left;
563 }
565 function parseMaybeUnary(noIn) {
566 if (token.type.prefix) {
567 var node = startNode(), update = token.type.isUpdate;
568 node.operator = token.value;
569 node.prefix = true;
570 next();
571 node.argument = parseMaybeUnary(noIn);
572 if (update) node.argument = checkLVal(node.argument);
573 return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
574 }
575 var expr = parseExprSubscripts();
576 while (token.type.postfix && !canInsertSemicolon()) {
577 var node = startNodeFrom(expr);
578 node.operator = token.value;
579 node.prefix = false;
580 node.argument = checkLVal(expr);
581 next();
582 expr = finishNode(node, "UpdateExpression");
583 }
584 return expr;
585 }
587 function parseExprSubscripts() {
588 return parseSubscripts(parseExprAtom(), false, curIndent, curLineStart);
589 }
591 function parseSubscripts(base, noCalls, startIndent, line) {
592 for (;;) {
593 if (curLineStart != line && curIndent <= startIndent && tokenStartsLine()) {
594 if (token.type == tt.dot && curIndent == startIndent)
595 --startIndent;
596 else
597 return base;
598 }
600 if (eat(tt.dot)) {
601 var node = startNodeFrom(base);
602 node.object = base;
603 if (curLineStart != line && curIndent <= startIndent && tokenStartsLine())
604 node.property = dummyIdent();
605 else
606 node.property = parsePropertyName() || dummyIdent();
607 node.computed = false;
608 base = finishNode(node, "MemberExpression");
609 } else if (token.type == tt.bracketL) {
610 pushCx();
611 next();
612 var node = startNodeFrom(base);
613 node.object = base;
614 node.property = parseExpression();
615 node.computed = true;
616 popCx();
617 expect(tt.bracketR);
618 base = finishNode(node, "MemberExpression");
619 } else if (!noCalls && token.type == tt.parenL) {
620 pushCx();
621 var node = startNodeFrom(base);
622 node.callee = base;
623 node.arguments = parseExprList(tt.parenR);
624 base = finishNode(node, "CallExpression");
625 } else {
626 return base;
627 }
628 }
629 }
631 function parseExprAtom() {
632 switch (token.type) {
633 case tt._this:
634 var node = startNode();
635 next();
636 return finishNode(node, "ThisExpression");
637 case tt.name:
638 return parseIdent();
639 case tt.num: case tt.string: case tt.regexp:
640 var node = startNode();
641 node.value = token.value;
642 node.raw = input.slice(token.start, token.end);
643 next();
644 return finishNode(node, "Literal");
646 case tt._null: case tt._true: case tt._false:
647 var node = startNode();
648 node.value = token.type.atomValue;
649 node.raw = token.type.keyword;
650 next();
651 return finishNode(node, "Literal");
653 case tt.parenL:
654 var tokStart1 = token.start;
655 next();
656 var val = parseExpression();
657 val.start = tokStart1;
658 val.end = token.end;
659 expect(tt.parenR);
660 return val;
662 case tt.bracketL:
663 var node = startNode();
664 pushCx();
665 node.elements = parseExprList(tt.bracketR);
666 return finishNode(node, "ArrayExpression");
668 case tt.braceL:
669 return parseObj();
671 case tt._function:
672 var node = startNode();
673 next();
674 return parseFunction(node, false);
676 case tt._new:
677 return parseNew();
679 default:
680 return dummyIdent();
681 }
682 }
684 function parseNew() {
685 var node = startNode(), startIndent = curIndent, line = curLineStart;
686 next();
687 node.callee = parseSubscripts(parseExprAtom(), true, startIndent, line);
688 if (token.type == tt.parenL) {
689 pushCx();
690 node.arguments = parseExprList(tt.parenR);
691 } else {
692 node.arguments = [];
693 }
694 return finishNode(node, "NewExpression");
695 }
697 function parseObj() {
698 var node = startNode();
699 node.properties = [];
700 pushCx();
701 next();
702 var propIndent = curIndent, line = curLineStart;
703 while (!closes(tt.braceR, propIndent, line)) {
704 var name = parsePropertyName();
705 if (!name) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; }
706 var prop = {key: name}, isGetSet = false, kind;
707 if (eat(tt.colon)) {
708 prop.value = parseExpression(true);
709 kind = prop.kind = "init";
710 } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
711 (prop.key.name === "get" || prop.key.name === "set")) {
712 isGetSet = true;
713 kind = prop.kind = prop.key.name;
714 prop.key = parsePropertyName() || dummyIdent();
715 prop.value = parseFunction(startNode(), false);
716 } else {
717 next();
718 eat(tt.comma);
719 continue;
720 }
722 node.properties.push(prop);
723 eat(tt.comma);
724 }
725 popCx();
726 eat(tt.braceR);
727 return finishNode(node, "ObjectExpression");
728 }
730 function parsePropertyName() {
731 if (token.type === tt.num || token.type === tt.string) return parseExprAtom();
732 if (token.type === tt.name || token.type.keyword) return parseIdent();
733 }
735 function parseIdent() {
736 var node = startNode();
737 node.name = token.type === tt.name ? token.value : token.type.keyword;
738 next();
739 return finishNode(node, "Identifier");
740 }
742 function parseFunction(node, isStatement) {
743 if (token.type === tt.name) node.id = parseIdent();
744 else if (isStatement) node.id = dummyIdent();
745 else node.id = null;
746 node.params = [];
747 pushCx();
748 expect(tt.parenL);
749 while (token.type == tt.name) {
750 node.params.push(parseIdent());
751 eat(tt.comma);
752 }
753 popCx();
754 eat(tt.parenR);
755 node.body = parseBlock();
756 return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
757 }
759 function parseExprList(close) {
760 var indent = curIndent, line = curLineStart, elts = [], continuedLine = nextLineStart;
761 next(); // Opening bracket
762 if (curLineStart > continuedLine) continuedLine = curLineStart;
763 while (!closes(close, indent + (curLineStart <= continuedLine ? 1 : 0), line)) {
764 var elt = parseExpression(true);
765 if (isDummy(elt)) {
766 if (closes(close, indent, line)) break;
767 next();
768 } else {
769 elts.push(elt);
770 }
771 while (eat(tt.comma)) {}
772 }
773 popCx();
774 eat(close);
775 return elts;
776 }
777 });