summaryrefslogtreecommitdiff
path: root/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser')
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsdefs.js751
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jslex.js719
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsparse.js2042
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/parse.js57
4 files changed, 3569 insertions, 0 deletions
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsdefs.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsdefs.js
new file mode 100644
index 0000000..aff5c08
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsdefs.js
@@ -0,0 +1,751 @@
+/* vim: set sw=4 ts=4 et tw=78: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan@mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Tom Austin <taustin@ucsc.edu>
+ * Brendan Eich <brendan@mozilla.org>
+ * Shu-Yu Guo <shu@rfrn.org>
+ * Dave Herman <dherman@mozilla.com>
+ * Dimitris Vardoulakis <dimvar@ccs.neu.edu>
+ * Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Well-known constants and lookup tables. Many consts are generated from the
+ * tokens table via eval to minimize redundancy, so consumers must be compiled
+ * separately to take advantage of the simple switch-case constant propagation
+ * done by SpiderMonkey.
+ */
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+//"use scrict";
+
+var Narcissus = {};
+(function() {
+
+ var narcissus = {
+ options: {
+ version: 185,
+ // Global variables to hide from the interpreter
+ hiddenHostGlobals: { Narcissus: true },
+ // Desugar SpiderMonkey language extensions?
+ desugarExtensions: false
+ },
+ hostSupportsEvalConst: (function() {
+ try {
+ return eval("(function(s) { eval(s); return x })('const x = true;')");
+ } catch (e) {
+ return false;
+ }
+ })(),
+ hostGlobal: this
+ };
+ Narcissus = narcissus;
+})();
+
+Narcissus.definitions = (function() {
+
+ var tokens = [
+ // End of source.
+ "END",
+
+ // Operators and punctuators. Some pair-wise order matters, e.g. (+, -)
+ // and (UNARY_PLUS, UNARY_MINUS).
+ "\n", ";",
+ ",",
+ "=",
+ "?", ":", "CONDITIONAL",
+ "||",
+ "&&",
+ "|",
+ "^",
+ "&",
+ "==", "!=", "===", "!==",
+ "<", "<=", ">=", ">",
+ "<<", ">>", ">>>",
+ "+", "-",
+ "*", "/", "%",
+ "!", "~", "UNARY_PLUS", "UNARY_MINUS",
+ "++", "--",
+ ".",
+ "[", "]",
+ "{", "}",
+ "(", ")",
+
+ // Nonterminal tree node type codes.
+ "SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
+ "ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
+ "GROUP", "LIST", "LET_BLOCK", "ARRAY_COMP", "GENERATOR", "COMP_TAIL",
+
+ // Terminals.
+ "IDENTIFIER", "NUMBER", "STRING", "REGEXP",
+
+ // Keywords.
+ "break",
+ "case", "catch", "const", "continue",
+ "debugger", "default", "delete", "do",
+ "else", "export",
+ "false", "finally", "for", "function",
+ "if", "import", "in", "instanceof",
+ "let", "module",
+ "new", "null",
+ "return",
+ "switch",
+ "this", "throw", "true", "try", "typeof",
+ "var", "void",
+ "yield",
+ "while", "with",
+ ];
+
+ var statementStartTokens = [
+ "break",
+ "const", "continue",
+ "debugger", "do",
+ "for",
+ "if",
+ "return",
+ "switch",
+ "throw", "try",
+ "var",
+ "yield",
+ "while", "with",
+ ];
+
+ // Whitespace characters (see ECMA-262 7.2)
+ var whitespaceChars = [
+ // normal whitespace:
+ "\u0009", "\u000B", "\u000C", "\u0020", "\u00A0", "\uFEFF",
+
+ // high-Unicode whitespace:
+ "\u1680", "\u180E",
+ "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", "\u2005", "\u2006",
+ "\u2007", "\u2008", "\u2009", "\u200A",
+ "\u202F", "\u205F", "\u3000"
+ ];
+
+ var whitespace = {};
+ for (var i = 0; i < whitespaceChars.length; i++) {
+ whitespace[whitespaceChars[i]] = true;
+ }
+
+ // Operator and punctuator mapping from token to tree node type name.
+ // NB: because the lexer doesn't backtrack, all token prefixes must themselves
+ // be valid tokens (e.g. !== is acceptable because its prefixes are the valid
+ // tokens != and !).
+ var opTypeNames = {
+ '\n': "NEWLINE",
+ ';': "SEMICOLON",
+ ',': "COMMA",
+ '?': "HOOK",
+ ':': "COLON",
+ '||': "OR",
+ '&&': "AND",
+ '|': "BITWISE_OR",
+ '^': "BITWISE_XOR",
+ '&': "BITWISE_AND",
+ '===': "STRICT_EQ",
+ '==': "EQ",
+ '=': "ASSIGN",
+ '!==': "STRICT_NE",
+ '!=': "NE",
+ '<<': "LSH",
+ '<=': "LE",
+ '<': "LT",
+ '>>>': "URSH",
+ '>>': "RSH",
+ '>=': "GE",
+ '>': "GT",
+ '++': "INCREMENT",
+ '--': "DECREMENT",
+ '+': "PLUS",
+ '-': "MINUS",
+ '*': "MUL",
+ '/': "DIV",
+ '%': "MOD",
+ '!': "NOT",
+ '~': "BITWISE_NOT",
+ '.': "DOT",
+ '[': "LEFT_BRACKET",
+ ']': "RIGHT_BRACKET",
+ '{': "LEFT_CURLY",
+ '}': "RIGHT_CURLY",
+ '(': "LEFT_PAREN",
+ ')': "RIGHT_PAREN"
+ };
+
+ // Hash of keyword identifier to tokens index. NB: we must null __proto__ to
+ // avoid toString, etc. namespace pollution.
+ var keywords = {__proto__: null};
+
+ // Define const END, etc., based on the token names. Also map name to index.
+ var tokenIds = {};
+
+ // Building up a string to be eval'd in different contexts.
+ var consts = Narcissus.hostSupportsEvalConst ? "const " : "var ";
+ for (var i = 0, j = tokens.length; i < j; i++) {
+ if (i > 0)
+ consts += ", ";
+ var t = tokens[i];
+ var name;
+ if (/^[a-z]/.test(t)) {
+ name = t.toUpperCase();
+ keywords[t] = i;
+ } else {
+ name = (/^\W/.test(t) ? opTypeNames[t] : t);
+ }
+ consts += name + " = " + i;
+ tokenIds[name] = i;
+ tokens[t] = i;
+ }
+ consts += ";";
+
+ var isStatementStartCode = {__proto__: null};
+ for (i = 0, j = statementStartTokens.length; i < j; i++)
+ isStatementStartCode[keywords[statementStartTokens[i]]] = true;
+
+ // Map assignment operators to their indexes in the tokens array.
+ var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
+
+ for (i = 0, j = assignOps.length; i < j; i++) {
+ t = assignOps[i];
+ assignOps[t] = tokens[t];
+ }
+
+ function defineGetter(obj, prop, fn, dontDelete, dontEnum) {
+ Object.defineProperty(obj, prop,
+ { get: fn, configurable: !dontDelete, enumerable: !dontEnum });
+ }
+
+ function defineGetterSetter(obj, prop, getter, setter, dontDelete, dontEnum) {
+ Object.defineProperty(obj, prop, {
+ get: getter,
+ set: setter,
+ configurable: !dontDelete,
+ enumerable: !dontEnum
+ });
+ }
+
+ function defineMemoGetter(obj, prop, fn, dontDelete, dontEnum) {
+ Object.defineProperty(obj, prop, {
+ get: function() {
+ var val = fn();
+ defineProperty(obj, prop, val, dontDelete, true, dontEnum);
+ return val;
+ },
+ configurable: true,
+ enumerable: !dontEnum
+ });
+ }
+
+ function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
+ Object.defineProperty(obj, prop,
+ { value: val, writable: !readOnly, configurable: !dontDelete,
+ enumerable: !dontEnum });
+ }
+
+ // Returns true if fn is a native function. (Note: SpiderMonkey specific.)
+ function isNativeCode(fn) {
+ // Relies on the toString method to identify native code.
+ return ((typeof fn) === "function") && fn.toString().match(/\[native code\]/);
+ }
+
+ var Fpapply = Function.prototype.apply;
+
+ function apply(f, o, a) {
+ return Fpapply.call(f, [o].concat(a));
+ }
+
+ var applyNew;
+
+ // ES5's bind is a simpler way to implement applyNew
+ if (Function.prototype.bind) {
+ applyNew = function applyNew(f, a) {
+ return new (f.bind.apply(f, [,].concat(a)))();
+ };
+ } else {
+ applyNew = function applyNew(f, a) {
+ switch (a.length) {
+ case 0:
+ return new f();
+ case 1:
+ return new f(a[0]);
+ case 2:
+ return new f(a[0], a[1]);
+ case 3:
+ return new f(a[0], a[1], a[2]);
+ default:
+ var argStr = "a[0]";
+ for (var i = 1, n = a.length; i < n; i++)
+ argStr += ",a[" + i + "]";
+ return eval("new f(" + argStr + ")");
+ }
+ };
+ }
+
+ function getPropertyDescriptor(obj, name) {
+ while (obj) {
+ if (({}).hasOwnProperty.call(obj, name))
+ return Object.getOwnPropertyDescriptor(obj, name);
+ obj = Object.getPrototypeOf(obj);
+ }
+ return undefined;
+ }
+
+ function getPropertyNames(obj) {
+ var table = Object.create(null, {});
+ while (obj) {
+ var names = Object.getOwnPropertyNames(obj);
+ for (var i = 0, n = names.length; i < n; i++)
+ table[names[i]] = true;
+ obj = Object.getPrototypeOf(obj);
+ }
+ return Object.keys(table);
+ }
+
+ function getOwnProperties(obj) {
+ var map = {};
+ for (var name in Object.getOwnPropertyNames(obj))
+ map[name] = Object.getOwnPropertyDescriptor(obj, name);
+ return map;
+ }
+
+ function blacklistHandler(target, blacklist) {
+ var mask = Object.create(null, {});
+ var redirect = StringMap.create(blacklist).mapObject(function(name) { return mask; });
+ return mixinHandler(redirect, target);
+ }
+
+ function whitelistHandler(target, whitelist) {
+ var catchall = Object.create(null, {});
+ var redirect = StringMap.create(whitelist).mapObject(function(name) { return target; });
+ return mixinHandler(redirect, catchall);
+ }
+
+ function mirrorHandler(target, writable) {
+ var handler = makePassthruHandler(target);
+
+ var defineProperty = handler.defineProperty;
+ handler.defineProperty = function(name, desc) {
+ if (!desc.enumerable)
+ throw new Error("mirror property must be enumerable");
+ if (!desc.configurable)
+ throw new Error("mirror property must be configurable");
+ if (desc.writable !== writable)
+ throw new Error("mirror property must " + (writable ? "" : "not ") + "be writable");
+ defineProperty(name, desc);
+ };
+
+ handler.fix = function() { };
+ handler.getOwnPropertyDescriptor = handler.getPropertyDescriptor;
+ handler.getOwnPropertyNames = getPropertyNames.bind(handler, target);
+ handler.keys = handler.enumerate;
+ handler["delete"] = function() { return false; };
+ handler.hasOwn = handler.has;
+ return handler;
+ }
+
+ /*
+ * Mixin proxies break the single-inheritance model of prototypes, so
+ * the handler treats all properties as own-properties:
+ *
+ * X
+ * |
+ * +------------+------------+
+ * | O |
+ * | | |
+ * | O O O |
+ * | | | | |
+ * | O O O O |
+ * | | | | | |
+ * | O O O O O |
+ * | | | | | | |
+ * +-(*)--(w)--(x)--(y)--(z)-+
+ */
+
+ function mixinHandler(redirect, catchall) {
+ function targetFor(name) {
+ return hasOwn(redirect, name) ? redirect[name] : catchall;
+ }
+
+ function getMuxPropertyDescriptor(name) {
+ var desc = getPropertyDescriptor(targetFor(name), name);
+ if (desc)
+ desc.configurable = true;
+ return desc;
+ }
+
+ function getMuxPropertyNames() {
+ var names1 = Object.getOwnPropertyNames(redirect).filter(function(name) {
+ return name in redirect[name];
+ });
+ var names2 = getPropertyNames(catchall).filter(function(name) {
+ return !hasOwn(redirect, name);
+ });
+ return names1.concat(names2);
+ }
+
+ function enumerateMux() {
+ var result = Object.getOwnPropertyNames(redirect).filter(function(name) {
+ return name in redirect[name];
+ });
+ for (name in catchall) {
+ if (!hasOwn(redirect, name))
+ result.push(name);
+ };
+ return result;
+ }
+
+ function hasMux(name) {
+ return name in targetFor(name);
+ }
+
+ return {
+ getOwnPropertyDescriptor: getMuxPropertyDescriptor,
+ getPropertyDescriptor: getMuxPropertyDescriptor,
+ getOwnPropertyNames: getMuxPropertyNames,
+ defineProperty: function(name, desc) {
+ Object.defineProperty(targetFor(name), name, desc);
+ },
+ "delete": function(name) {
+ var target = targetFor(name);
+ return delete target[name];
+ },
+ // FIXME: ha ha ha
+ fix: function() { },
+ has: hasMux,
+ hasOwn: hasMux,
+ get: function(receiver, name) {
+ var target = targetFor(name);
+ return target[name];
+ },
+ set: function(receiver, name, val) {
+ var target = targetFor(name);
+ target[name] = val;
+ return true;
+ },
+ enumerate: enumerateMux,
+ keys: enumerateMux
+ };
+ }
+
+ function makePassthruHandler(obj) {
+ // Handler copied from
+ // http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
+ return {
+ getOwnPropertyDescriptor: function(name) {
+ var desc = Object.getOwnPropertyDescriptor(obj, name);
+
+ // a trapping proxy's properties must always be configurable
+ desc.configurable = true;
+ return desc;
+ },
+ getPropertyDescriptor: function(name) {
+ var desc = getPropertyDescriptor(obj, name);
+
+ // a trapping proxy's properties must always be configurable
+ desc.configurable = true;
+ return desc;
+ },
+ getOwnPropertyNames: function() {
+ return Object.getOwnPropertyNames(obj);
+ },
+ defineProperty: function(name, desc) {
+ Object.defineProperty(obj, name, desc);
+ },
+ "delete": function(name) { return delete obj[name]; },
+ fix: function() {
+ if (Object.isFrozen(obj)) {
+ return getOwnProperties(obj);
+ }
+
+ // As long as obj is not frozen, the proxy won't allow itself to be fixed.
+ return undefined; // will cause a TypeError to be thrown
+ },
+
+ has: function(name) { return name in obj; },
+ hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
+ get: function(receiver, name) { return obj[name]; },
+
+ // bad behavior when set fails in non-strict mode
+ set: function(receiver, name, val) { obj[name] = val; return true; },
+ enumerate: function() {
+ var result = [];
+ for (name in obj) { result.push(name); };
+ return result;
+ },
+ keys: function() { return Object.keys(obj); }
+ };
+ }
+
+ var hasOwnProperty = ({}).hasOwnProperty;
+
+ function hasOwn(obj, name) {
+ return hasOwnProperty.call(obj, name);
+ }
+
+ function StringMap(table, size) {
+ this.table = table || Object.create(null, {});
+ this.size = size || 0;
+ }
+
+ StringMap.create = function(table) {
+ var init = Object.create(null, {});
+ var size = 0;
+ var names = Object.getOwnPropertyNames(table);
+ for (var i = 0, n = names.length; i < n; i++) {
+ var name = names[i];
+ init[name] = table[name];
+ size++;
+ }
+ return new StringMap(init, size);
+ };
+
+ StringMap.prototype = {
+ has: function(x) { return hasOwnProperty.call(this.table, x); },
+ set: function(x, v) {
+ if (!hasOwnProperty.call(this.table, x))
+ this.size++;
+ this.table[x] = v;
+ },
+ get: function(x) { return this.table[x]; },
+ getDef: function(x, thunk) {
+ if (!hasOwnProperty.call(this.table, x)) {
+ this.size++;
+ this.table[x] = thunk();
+ }
+ return this.table[x];
+ },
+ forEach: function(f) {
+ var table = this.table;
+ for (var key in table)
+ f.call(this, key, table[key]);
+ },
+ map: function(f) {
+ var table1 = this.table;
+ var table2 = Object.create(null, {});
+ this.forEach(function(key, val) {
+ table2[key] = f.call(this, val, key);
+ });
+ return new StringMap(table2, this.size);
+ },
+ mapObject: function(f) {
+ var table1 = this.table;
+ var table2 = Object.create(null, {});
+ this.forEach(function(key, val) {
+ table2[key] = f.call(this, val, key);
+ });
+ return table2;
+ },
+ toObject: function() {
+ return this.mapObject(function(val) { return val; });
+ },
+ choose: function() {
+ return Object.getOwnPropertyNames(this.table)[0];
+ },
+ remove: function(x) {
+ if (hasOwnProperty.call(this.table, x)) {
+ this.size--;
+ delete this.table[x];
+ }
+ },
+ copy: function() {
+ var table = Object.create(null, {});
+ for (var key in this.table)
+ table[key] = this.table[key];
+ return new StringMap(table, this.size);
+ },
+ keys: function() {
+ return Object.keys(this.table);
+ },
+ toString: function() { return "[object StringMap]" }
+ };
+
+ // an object-key table with poor asymptotics (replace with WeakMap when possible)
+ function ObjectMap(array) {
+ this.array = array || [];
+ }
+
+ function searchMap(map, key, found, notFound) {
+ var a = map.array;
+ for (var i = 0, n = a.length; i < n; i++) {
+ var pair = a[i];
+ if (pair.key === key)
+ return found(pair, i);
+ }
+ return notFound();
+ }
+
+ ObjectMap.prototype = {
+ has: function(x) {
+ return searchMap(this, x, function() { return true }, function() { return false });
+ },
+ set: function(x, v) {
+ var a = this.array;
+ searchMap(this, x,
+ function(pair) { pair.value = v },
+ function() { a.push({ key: x, value: v }) });
+ },
+ get: function(x) {
+ return searchMap(this, x,
+ function(pair) { return pair.value },
+ function() { return null });
+ },
+ getDef: function(x, thunk) {
+ var a = this.array;
+ return searchMap(this, x,
+ function(pair) { return pair.value },
+ function() {
+ var v = thunk();
+ a.push({ key: x, value: v });
+ return v;
+ });
+ },
+ forEach: function(f) {
+ var a = this.array;
+ for (var i = 0, n = a.length; i < n; i++) {
+ var pair = a[i];
+ f.call(this, pair.key, pair.value);
+ }
+ },
+ choose: function() {
+ return this.array[0].key;
+ },
+ get size() {
+ return this.array.length;
+ },
+ remove: function(x) {
+ var a = this.array;
+ searchMap(this, x,
+ function(pair, i) { a.splice(i, 1) },
+ function() { });
+ },
+ copy: function() {
+ return new ObjectMap(this.array.map(function(pair) {
+ return { key: pair.key, value: pair.value }
+ }));
+ },
+ clear: function() {
+ this.array = [];
+ },
+ toString: function() { return "[object ObjectMap]" }
+ };
+
+ // non-destructive stack
+ function Stack(elts) {
+ this.elts = elts || null;
+ }
+
+ Stack.prototype = {
+ push: function(x) {
+ return new Stack({ top: x, rest: this.elts });
+ },
+ top: function() {
+ if (!this.elts)
+ throw new Error("empty stack");
+ return this.elts.top;
+ },
+ isEmpty: function() {
+ return this.top === null;
+ },
+ find: function(test) {
+ for (var elts = this.elts; elts; elts = elts.rest) {
+ if (test(elts.top))
+ return elts.top;
+ }
+ return null;
+ },
+ has: function(x) {
+ return Boolean(this.find(function(elt) { return elt === x }));
+ },
+ forEach: function(f) {
+ for (var elts = this.elts; elts; elts = elts.rest) {
+ f(elts.top);
+ }
+ }
+ };
+
+ if (!Array.prototype.copy) {
+ Array.prototype.copy = function() {
+ var result = [];
+ for (var i = 0, n = this.length; i < n; i++)
+ result[i] = this[i];
+ return result;
+ };
+ }
+
+ return {
+ tokens: tokens,
+ whitespace: whitespace,
+ opTypeNames: opTypeNames,
+ keywords: keywords,
+ isStatementStartCode: isStatementStartCode,
+ tokenIds: tokenIds,
+ consts: consts,
+ assignOps: assignOps,
+ defineGetter: defineGetter,
+ defineGetterSetter: defineGetterSetter,
+ defineMemoGetter: defineMemoGetter,
+ defineProperty: defineProperty,
+ isNativeCode: isNativeCode,
+ apply: apply,
+ applyNew: applyNew,
+ mirrorHandler: mirrorHandler,
+ mixinHandler: mixinHandler,
+ whitelistHandler: whitelistHandler,
+ blacklistHandler: blacklistHandler,
+ makePassthruHandler: makePassthruHandler,
+ StringMap: StringMap,
+ ObjectMap: ObjectMap,
+ Stack: Stack
+ };
+}());
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jslex.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jslex.js
new file mode 100644
index 0000000..c5c2673
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jslex.js
@@ -0,0 +1,719 @@
+/* vim: set sw=4 ts=4 et tw=78: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan@mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Tom Austin <taustin@ucsc.edu>
+ * Brendan Eich <brendan@mozilla.org>
+ * Shu-Yu Guo <shu@rfrn.org>
+ * Stephan Herhut <stephan.a.herhut@intel.com>
+ * Dave Herman <dherman@mozilla.com>
+ * Dimitris Vardoulakis <dimvar@ccs.neu.edu>
+ * Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Lexical scanner.
+ */
+
+"use strict";
+
+Narcissus.lexer = (function() {
+
+ var definitions = Narcissus.definitions;
+
+ //throw Error (definitions.consts);
+
+ // Set constants in the local scope.
+ //eval(definitions.consts);
+ const END = 0,
+ NEWLINE = 1,
+ SEMICOLON = 2,
+ COMMA = 3,
+ ASSIGN = 4,
+ HOOK = 5,
+ COLON = 6,
+ CONDITIONAL = 7,
+ OR = 8,
+ AND = 9,
+ BITWISE_OR = 10,
+ BITWISE_XOR = 11,
+ BITWISE_AND = 12,
+ EQ = 13,
+ NE = 14,
+ STRICT_EQ = 15,
+ STRICT_NE = 16,
+ LT = 17,
+ LE = 18,
+ GE = 19,
+ GT = 20,
+ LSH = 21,
+ RSH = 22,
+ URSH = 23,
+ PLUS = 24,
+ MINUS = 25,
+ MUL = 26,
+ DIV = 27,
+ MOD = 28,
+ NOT = 29,
+ BITWISE_NOT = 30,
+ UNARY_PLUS = 31,
+ UNARY_MINUS = 32,
+ INCREMENT = 33,
+ DECREMENT = 34,
+ DOT = 35,
+ LEFT_BRACKET = 36,
+ RIGHT_BRACKET = 37,
+ LEFT_CURLY = 38,
+ RIGHT_CURLY = 39,
+ LEFT_PAREN = 40,
+ RIGHT_PAREN = 41,
+ SCRIPT = 42,
+ BLOCK = 43,
+ LABEL = 44,
+ FOR_IN = 45,
+ CALL = 46,
+ NEW_WITH_ARGS = 47,
+ INDEX = 48,
+ ARRAY_INIT = 49,
+ OBJECT_INIT = 50,
+ PROPERTY_INIT = 51,
+ GETTER = 52,
+ SETTER = 53,
+ GROUP = 54,
+ LIST = 55,
+ LET_BLOCK = 56,
+ ARRAY_COMP = 57,
+ GENERATOR = 58,
+ COMP_TAIL = 59,
+ IDENTIFIER = 60,
+ NUMBER = 61,
+ STRING = 62,
+ REGEXP = 63,
+ BREAK = 64,
+ CASE = 65,
+ CATCH = 66,
+ CONST = 67,
+ CONTINUE = 68,
+ DEBUGGER = 69,
+ DEFAULT = 70,
+ DELETE = 71,
+ DO = 72,
+ ELSE = 73,
+ EXPORT = 74,
+ FALSE = 75,
+ FINALLY = 76,
+ FOR = 77,
+ FUNCTION = 78,
+ IF = 79,
+ IMPORT = 80,
+ IN = 81,
+ INSTANCEOF = 82,
+ LET = 83,
+ MODULE = 84,
+ NEW = 85,
+ NULL = 86,
+ RETURN = 87,
+ SWITCH = 88,
+ THIS = 89,
+ THROW = 90,
+ TRUE = 91,
+ TRY = 92,
+ TYPEOF = 93,
+ VAR = 94,
+ VOID = 95,
+ YIELD = 96,
+ WHILE = 97,
+ WITH = 98;
+
+ // Banned keywords by language version
+ const blackLists = { 160: {}, 185: {}, harmony: {} };
+/* blackLists[160][LET] = true;
+ blackLists[160][MODULE] = true;
+ blackLists[160][YIELD] = true;
+ blackLists[185][MODULE] = true;
+*/
+ // Build up a trie of operator tokens.
+ var opTokens = {};
+ for (var op in definitions.opTypeNames) {
+ if (op === '\n' || op === '.')
+ continue;
+
+ var node = opTokens;
+ for (var i = 0; i < op.length; i++) {
+ var ch = op[i];
+ if (!(ch in node))
+ node[ch] = {};
+ node = node[ch];
+ node.op = op;
+ }
+ }
+
+ /*
+ * Since JavaScript provides no convenient way to determine if a
+ * character is in a particular Unicode category, we use
+ * metacircularity to accomplish this (oh yeaaaah!)
+ */
+ function isValidIdentifierChar(ch, first) {
+ // check directly for ASCII
+ if (ch <= "\u007F") {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '$' || ch === '_' ||
+ (!first && (ch >= '0' && ch <= '9'))) {
+ return true;
+ }
+ return false;
+ }
+
+ // create an object to test this in
+ var x = {};
+ x["x"+ch] = true;
+ x[ch] = true;
+
+ // then use eval to determine if it's a valid character
+ var valid = false;
+ try {
+ valid = (Function("x", "return (x." + (first?"":"x") + ch + ");")(x) === true);
+ } catch (ex) {}
+
+ return valid;
+ }
+
+ function isIdentifier(str) {
+ if (typeof str !== "string")
+ return false;
+
+ if (str.length === 0)
+ return false;
+
+ if (!isValidIdentifierChar(str[0], true))
+ return false;
+
+ for (var i = 1; i < str.length; i++) {
+ if (!isValidIdentifierChar(str[i], false))
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * Tokenizer :: (source, filename, line number) -> Tokenizer
+ */
+ function Tokenizer(s, f, l) {
+ this.cursor = 0;
+ this.source = String(s);
+ this.tokens = [];
+ this.tokenIndex = 0;
+ this.lookahead = 0;
+ this.scanNewlines = false;
+ this.unexpectedEOF = false;
+ this.filename = f || "";
+ this.lineno = l || 1;
+ this.blackList = blackLists[Narcissus.options.version];
+ this.blockComments = null;
+ }
+
+ Tokenizer.prototype = {
+ get done() {
+ // We need to set scanOperand to true here because the first thing
+ // might be a regexp.
+ return this.peek(true) === END;
+ },
+
+ get token() {
+ return this.tokens[this.tokenIndex];
+ },
+
+ match: function (tt, scanOperand) {
+ return this.get(scanOperand) === tt || this.unget();
+ },
+
+ mustMatch: function (tt) {
+ if (!this.match(tt)) {
+ throw this.newSyntaxError("Missing " +
+ definitions.tokens[tt].toLowerCase());
+ }
+ return this.token;
+ },
+
+ peek: function (scanOperand) {
+ var tt, next;
+ if (this.lookahead) {
+ next = this.tokens[(this.tokenIndex + this.lookahead) & 3];
+ tt = (this.scanNewlines && next.lineno !== this.lineno)
+ ? NEWLINE
+ : next.type;
+ } else {
+ tt = this.get(scanOperand);
+ this.unget();
+ }
+ return tt;
+ },
+
+ peekOnSameLine: function (scanOperand) {
+ this.scanNewlines = true;
+ var tt = this.peek(scanOperand);
+ this.scanNewlines = false;
+ return tt;
+ },
+
+ lastBlockComment: function() {
+ var length = this.blockComments.length;
+ return length ? this.blockComments[length - 1] : null;
+ },
+
+ // Eat comments and whitespace.
+ skip: function () {
+ var input = this.source;
+ this.blockComments = [];
+ for (;;) {
+ var ch = input[this.cursor++];
+ var next = input[this.cursor];
+ // handle \r, \r\n and (always preferable) \n
+ if (ch === '\r') {
+ // if the next character is \n, we don't care about this at all
+ if (next === '\n') continue;
+
+ // otherwise, we want to consider this as a newline
+ ch = '\n';
+ }
+
+ if (ch === '\n' && !this.scanNewlines) {
+ this.lineno++;
+ } else if (ch === '/' && next === '*') {
+ var commentStart = ++this.cursor;
+ for (;;) {
+ ch = input[this.cursor++];
+ if (ch === undefined)
+ throw this.newSyntaxError("Unterminated comment");
+
+ if (ch === '*') {
+ next = input[this.cursor];
+ if (next === '/') {
+ var commentEnd = this.cursor - 1;
+ this.cursor++;
+ break;
+ }
+ } else if (ch === '\n') {
+ this.lineno++;
+ }
+ }
+ this.blockComments.push(input.substring(commentStart, commentEnd));
+ }
+ else if (ch === '-' && next === '-' &&
+ input[this.cursor + 1] === '>') {
+ this.cursor += 2;
+ }
+ else if ((ch === '/' && next === '/') ||
+ (ch === '<' && next === '!' &&
+ input[this.cursor + 1] === '-' &&
+ input[this.cursor + 2] === '-' &&
+ (this.cursor += 2))) {
+
+ // capture single line comments starts.
+ var commentStart = ++this.cursor;
+ for (;;) {
+ ch = input[this.cursor++];
+ if (ch === undefined) {
+ //this.lineno++;
+ break;
+ //throw this.newSyntaxError("Unterminated comment");
+ }
+ if (ch === '\r') {
+ // check for \r\n
+ if (next !== '\n') ch = '\n';
+ var commentEnd = this.cursor - 1;
+ }
+
+ if (ch === '\n') {
+ if (this.scanNewlines) {
+ this.cursor--;
+ } else {
+ this.lineno++;
+ }
+ var commentEnd = this.cursor - 1;
+ break;
+ }
+ }
+ this.blockComments.push(input.substring(commentStart, commentEnd));
+ // capture single line comments ends.
+ } else if (!(ch in definitions.whitespace)) {
+ this.cursor--;
+ return;
+ }
+ }
+ },
+
+ // Lex the exponential part of a number, if present. Return true iff an
+ // exponential part was found.
+ lexExponent: function() {
+ var input = this.source;
+ var next = input[this.cursor];
+ if (next === 'e' || next === 'E') {
+ this.cursor++;
+ ch = input[this.cursor++];
+ if (ch === '+' || ch === '-')
+ ch = input[this.cursor++];
+
+ if (ch < '0' || ch > '9')
+ throw this.newSyntaxError("Missing exponent");
+
+ do {
+ ch = input[this.cursor++];
+ } while (ch >= '0' && ch <= '9');
+ this.cursor--;
+
+ return true;
+ }
+
+ return false;
+ },
+
+ lexZeroNumber: function (ch) {
+ var token = this.token, input = this.source;
+ token.type = NUMBER;
+
+ ch = input[this.cursor++];
+ if (ch === '.') {
+ do {
+ ch = input[this.cursor++];
+ } while (ch >= '0' && ch <= '9');
+ this.cursor--;
+
+ this.lexExponent();
+ token.value = parseFloat(
+ input.substring(token.start, this.cursor));
+ } else if (ch === 'x' || ch === 'X') {
+ do {
+ ch = input[this.cursor++];
+ } while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F'));
+ this.cursor--;
+
+ token.value = parseInt(input.substring(token.start, this.cursor));
+ } else if (ch >= '0' && ch <= '7') {
+ do {
+ ch = input[this.cursor++];
+ } while (ch >= '0' && ch <= '7');
+ this.cursor--;
+
+ token.value = parseInt(input.substring(token.start, this.cursor));
+ } else {
+ this.cursor--;
+ this.lexExponent(); // 0E1, &c.
+ token.value = 0;
+ }
+ },
+
+ lexNumber: function (ch) {
+ var token = this.token, input = this.source;
+ token.type = NUMBER;
+
+ var floating = false;
+ do {
+ ch = input[this.cursor++];
+ if (ch === '.' && !floating) {
+ floating = true;
+ ch = input[this.cursor++];
+ }
+ } while (ch >= '0' && ch <= '9');
+
+ this.cursor--;
+
+ var exponent = this.lexExponent();
+ floating = floating || exponent;
+
+ var str = input.substring(token.start, this.cursor);
+ token.value = floating ? parseFloat(str) : parseInt(str);
+ },
+
+ lexDot: function (ch) {
+ var token = this.token, input = this.source;
+ var next = input[this.cursor];
+ if (next >= '0' && next <= '9') {
+ do {
+ ch = input[this.cursor++];
+ } while (ch >= '0' && ch <= '9');
+ this.cursor--;
+
+ this.lexExponent();
+
+ token.type = NUMBER;
+ token.value = parseFloat(
+ input.substring(token.start, this.cursor));
+ } else {
+ token.type = DOT;
+ token.assignOp = null;
+ token.value = '.';
+ }
+ },
+
+ lexString: function (ch) {
+ var token = this.token, input = this.source;
+ token.type = STRING;
+
+ var hasEscapes = false;
+ var delim = ch;
+ if (input.length <= this.cursor)
+ throw this.newSyntaxError("Unterminated string literal");
+ while ((ch = input[this.cursor++]) !== delim) {
+ if (this.cursor == input.length)
+ throw this.newSyntaxError("Unterminated string literal");
+ if (ch === '\\') {
+ hasEscapes = true;
+ if (++this.cursor == input.length)
+ throw this.newSyntaxError("Unterminated string literal");
+ }
+ }
+
+ token.value = hasEscapes
+ ? eval(input.substring(token.start, this.cursor))
+ : input.substring(token.start + 1, this.cursor - 1);
+ },
+
+ lexRegExp: function (ch) {
+ var token = this.token, input = this.source;
+ token.type = REGEXP;
+
+ do {
+ ch = input[this.cursor++];
+ if (ch === '\\') {
+ this.cursor++;
+ } else if (ch === '[') {
+ do {
+ if (ch === undefined)
+ throw this.newSyntaxError("Unterminated character class");
+
+ if (ch === '\\')
+ this.cursor++;
+
+ ch = input[this.cursor++];
+ } while (ch !== ']');
+ } else if (ch === undefined) {
+ throw this.newSyntaxError("Unterminated regex");
+ }
+ } while (ch !== '/');
+
+ do {
+ ch = input[this.cursor++];
+ } while (ch >= 'a' && ch <= 'z');
+
+ this.cursor--;
+
+ token.value = eval(input.substring(token.start, this.cursor));
+ },
+
+ lexOp: function (ch) {
+ var token = this.token, input = this.source;
+
+ // A bit ugly, but it seems wasteful to write a trie lookup routine
+ // for only 3 characters...
+ var node = opTokens[ch];
+ var next = input[this.cursor];
+ if (next in node) {
+ node = node[next];
+ this.cursor++;
+ next = input[this.cursor];
+ if (next in node) {
+ node = node[next];
+ this.cursor++;
+ next = input[this.cursor];
+ }
+ }
+
+ var op = node.op;
+ if (definitions.assignOps[op] && input[this.cursor] === '=') {
+ this.cursor++;
+ token.type = ASSIGN;
+ token.assignOp = definitions.tokenIds[definitions.opTypeNames[op]];
+ op += '=';
+ } else {
+ token.type = definitions.tokenIds[definitions.opTypeNames[op]];
+ token.assignOp = null;
+ }
+
+ token.value = op;
+ },
+
+ // FIXME: Unicode escape sequences
+ lexIdent: function (ch) {
+ var token = this.token;
+ var id = ch;
+
+ while ((ch = this.getValidIdentifierChar(false)) !== null) {
+ id += ch;
+ }
+
+ token.type = definitions.keywords[id] || IDENTIFIER;
+ if (token.type in this.blackList) {
+ // banned keyword, this is an identifier
+ token.type = IDENTIFIER;
+ }
+ token.value = id;
+ },
+
+ /*
+ * Tokenizer.get :: void -> token type
+ *
+ * Consume input *only* if there is no lookahead.
+ * Dispatch to the appropriate lexing function depending on the input.
+ */
+ get: function (scanOperand) {
+ var token;
+ while (this.lookahead) {
+ --this.lookahead;
+ this.tokenIndex = (this.tokenIndex + 1) & 3;
+ token = this.tokens[this.tokenIndex];
+ if (token.type !== NEWLINE || this.scanNewlines)
+ return token.type;
+ }
+
+ this.skip();
+
+ this.tokenIndex = (this.tokenIndex + 1) & 3;
+ token = this.tokens[this.tokenIndex];
+ if (!token)
+ this.tokens[this.tokenIndex] = token = {};
+
+ var input = this.source;
+ if (this.cursor >= input.length)
+ return token.type = END;
+
+ token.start = this.cursor;
+ token.lineno = this.lineno;
+
+ var ich = this.getValidIdentifierChar(true);
+ var ch = (ich === null) ? input[this.cursor++] : null;
+ if (ich !== null) {
+ this.lexIdent(ich);
+ } else if (scanOperand && ch === '/') {
+ this.lexRegExp(ch);
+ } else if (ch in opTokens) {
+ this.lexOp(ch);
+ } else if (ch === '.') {
+ this.lexDot(ch);
+ } else if (ch >= '1' && ch <= '9') {
+ this.lexNumber(ch);
+ } else if (ch === '0') {
+ this.lexZeroNumber(ch);
+ } else if (ch === '"' || ch === "'") {
+ this.lexString(ch);
+ } else if (this.scanNewlines && (ch === '\n' || ch === '\r')) {
+ // if this was a \r, look for \r\n
+ if (ch === '\r' && input[this.cursor] === '\n') this.cursor++;
+ token.type = NEWLINE;
+ token.value = '\n';
+ this.lineno++;
+ } else {
+ throw this.newSyntaxError("Illegal token");
+ }
+
+ token.end = this.cursor;
+ return token.type;
+ },
+
+ /*
+ * Tokenizer.unget :: void -> undefined
+ *
+ * Match depends on unget returning undefined.
+ */
+ unget: function () {
+ if (++this.lookahead === 4) throw "PANIC: too much lookahead!";
+ this.tokenIndex = (this.tokenIndex - 1) & 3;
+ },
+
+ newSyntaxError: function (m) {
+ m = (this.filename ? this.filename + ":" : "") + this.lineno + ": " + m;
+ var e = new SyntaxError(m, this.filename, this.lineno);
+ e.source = this.source;
+ e.cursor = this.lookahead
+ ? this.tokens[(this.tokenIndex + this.lookahead) & 3].start
+ : this.cursor;
+ return e;
+ },
+
+
+ /* Gets a single valid identifier char from the input stream, or null
+ * if there is none.
+ */
+ getValidIdentifierChar: function(first) {
+ var input = this.source;
+ if (this.cursor >= input.length) return null;
+ var ch = input[this.cursor];
+
+ // first check for \u escapes
+ if (ch === '\\' && input[this.cursor+1] === 'u') {
+ // get the character value
+ try {
+ ch = String.fromCharCode(parseInt(
+ input.substring(this.cursor + 2, this.cursor + 6),
+ 16));
+ } catch (ex) {
+ return null;
+ }
+ this.cursor += 5;
+ }
+
+ var valid = isValidIdentifierChar(ch, first);
+ if (valid) this.cursor++;
+ return (valid ? ch : null);
+ },
+ };
+
+
+ return {
+ isIdentifier: isIdentifier,
+ Tokenizer: Tokenizer
+ };
+
+}());
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsparse.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsparse.js
new file mode 100644
index 0000000..b78078c
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/jsparse.js
@@ -0,0 +1,2042 @@
+/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*-
+ * vim: set sw=4 ts=4 et tw=78:
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Narcissus JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Brendan Eich <brendan@mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Tom Austin <taustin@ucsc.edu>
+ * Brendan Eich <brendan@mozilla.org>
+ * Shu-Yu Guo <shu@rfrn.org>
+ * Dave Herman <dherman@mozilla.com>
+ * Dimitris Vardoulakis <dimvar@ccs.neu.edu>
+ * Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+/*
+ * Narcissus - JS implemented in JS.
+ *
+ * Parser.
+ */
+
+"use strict";
+
+Narcissus.parser = (function() {
+
+ var lexer = Narcissus.lexer;
+ var definitions = Narcissus.definitions;
+
+ const StringMap = definitions.StringMap;
+ const Stack = definitions.Stack;
+
+ var comment;
+
+ // Set constants in the local scope.
+ //eval(definitions.consts);
+ const END = 0,
+ NEWLINE = 1,
+ SEMICOLON = 2,
+ COMMA = 3,
+ ASSIGN = 4,
+ HOOK = 5,
+ COLON = 6,
+ CONDITIONAL = 7,
+ OR = 8,
+ AND = 9,
+ BITWISE_OR = 10,
+ BITWISE_XOR = 11,
+ BITWISE_AND = 12,
+ EQ = 13,
+ NE = 14,
+ STRICT_EQ = 15,
+ STRICT_NE = 16,
+ LT = 17,
+ LE = 18,
+ GE = 19,
+ GT = 20,
+ LSH = 21,
+ RSH = 22,
+ URSH = 23,
+ PLUS = 24,
+ MINUS = 25,
+ MUL = 26,
+ DIV = 27,
+ MOD = 28,
+ NOT = 29,
+ BITWISE_NOT = 30,
+ UNARY_PLUS = 31,
+ UNARY_MINUS = 32,
+ INCREMENT = 33,
+ DECREMENT = 34,
+ DOT = 35,
+ LEFT_BRACKET = 36,
+ RIGHT_BRACKET = 37,
+ LEFT_CURLY = 38,
+ RIGHT_CURLY = 39,
+ LEFT_PAREN = 40,
+ RIGHT_PAREN = 41,
+ SCRIPT = 42,
+ BLOCK = 43,
+ LABEL = 44,
+ FOR_IN = 45,
+ CALL = 46,
+ NEW_WITH_ARGS = 47,
+ INDEX = 48,
+ ARRAY_INIT = 49,
+ OBJECT_INIT = 50,
+ PROPERTY_INIT = 51,
+ GETTER = 52,
+ SETTER = 53,
+ GROUP = 54,
+ LIST = 55,
+ LET_BLOCK = 56,
+ ARRAY_COMP = 57,
+ GENERATOR = 58,
+ COMP_TAIL = 59,
+ IDENTIFIER = 60,
+ NUMBER = 61,
+ STRING = 62,
+ REGEXP = 63,
+ BREAK = 64,
+ CASE = 65,
+ CATCH = 66,
+ CONST = 67,
+ CONTINUE = 68,
+ DEBUGGER = 69,
+ DEFAULT = 70,
+ DELETE = 71,
+ DO = 72,
+ ELSE = 73,
+ EXPORT = 74,
+ FALSE = 75,
+ FINALLY = 76,
+ FOR = 77,
+ FUNCTION = 78,
+ IF = 79,
+ IMPORT = 80,
+ IN = 81,
+ INSTANCEOF = 82,
+ LET = 83,
+ MODULE = 84,
+ NEW = 85,
+ NULL = 86,
+ RETURN = 87,
+ SWITCH = 88,
+ THIS = 89,
+ THROW = 90,
+ TRUE = 91,
+ TRY = 92,
+ TYPEOF = 93,
+ VAR = 94,
+ VOID = 95,
+ YIELD = 96,
+ WHILE = 97,
+ WITH = 98;
+
+ // Banned statement types by language version.
+ const blackLists = { 160: {}, 185: {}, harmony: {} };
+ /* blackLists[160][IMPORT] = true;
+ blackLists[160][EXPORT] = true;
+ blackLists[160][LET] = true;
+ blackLists[160][MODULE] = true;
+ blackLists[160][YIELD] = true;
+ blackLists[185][IMPORT] = true;
+ blackLists[185][EXPORT] = true;
+ blackLists[185][MODULE] = true;
+ blackLists.harmony[WITH] = true;
+*/
+ /*
+ * pushDestructuringVarDecls :: (node, hoisting node) -> void
+ *
+ * Recursively add all destructured declarations to varDecls.
+ */
+ function pushDestructuringVarDecls(n, s) {
+ for (var i in n) {
+ var sub = n[i];
+ if (sub.type === IDENTIFIER) {
+ s.varDecls.push(sub);
+ } else {
+ pushDestructuringVarDecls(sub, s);
+ }
+ }
+ }
+
+ function StaticContext(parentScript, parentBlock, inModule, inFunction) {
+ this.parentScript = parentScript;
+ this.parentBlock = parentBlock || parentScript;
+ this.inModule = inModule || false;
+ this.inFunction = inFunction || false;
+ this.inForLoopInit = false;
+ this.topLevel = true;
+ this.allLabels = new Stack();
+ this.currentLabels = new Stack();
+ this.labeledTargets = new Stack();
+ this.defaultLoopTarget = null;
+ this.defaultTarget = null;
+ this.blackList = blackLists[Narcissus.options.version];
+ Narcissus.options.ecma3OnlyMode && (this.ecma3OnlyMode = true);
+ Narcissus.options.parenFreeMode && (this.parenFreeMode = true);
+ }
+
+ StaticContext.prototype = {
+ ecma3OnlyMode: false,
+ parenFreeMode: false,
+ // non-destructive update via prototype extension
+ update: function(ext) {
+ var desc = {};
+ for (var key in ext) {
+ desc[key] = {
+ value: ext[key],
+ writable: true,
+ enumerable: true,
+ configurable: true
+ }
+ }
+ return Object.create(this, desc);
+ },
+ pushLabel: function(label) {
+ return this.update({ currentLabels: this.currentLabels.push(label),
+ allLabels: this.allLabels.push(label) });
+ },
+ pushTarget: function(target) {
+ var isDefaultLoopTarget = target.isLoop;
+ var isDefaultTarget = isDefaultLoopTarget || target.type === SWITCH;
+
+ if (this.currentLabels.isEmpty()) {
+ if (isDefaultLoopTarget) this.update({ defaultLoopTarget: target });
+ if (isDefaultTarget) this.update({ defaultTarget: target });
+ return this;
+ }
+
+ target.labels = new StringMap();
+ this.currentLabels.forEach(function(label) {
+ target.labels.set(label, true);
+ });
+ return this.update({ currentLabels: new Stack(),
+ labeledTargets: this.labeledTargets.push(target),
+ defaultLoopTarget: isDefaultLoopTarget
+ ? target
+ : this.defaultLoopTarget,
+ defaultTarget: isDefaultTarget
+ ? target
+ : this.defaultTarget });
+ },
+ nest: function() {
+ return this.topLevel ? this.update({ topLevel: false }) : this;
+ },
+ allow: function(type) {
+ switch (type) {
+ case EXPORT:
+ if (!this.inModule || this.inFunction || !this.topLevel)
+ return false;
+ // FALL THROUGH
+
+ case IMPORT:
+ return !this.inFunction && this.topLevel;
+
+ case MODULE:
+ return !this.inFunction && this.topLevel;
+
+ default:
+ return true;
+ }
+ }
+ };
+
+ /*
+ * Script :: (tokenizer, boolean, boolean) -> node
+ *
+ * Parses the toplevel and module/function bodies.
+ */
+ function Script(t, inModule, inFunction) {
+ var n = new Node(t, scriptInit());
+ Statements(t, new StaticContext(n, n, inModule, inFunction), n);
+ return n;
+ }
+
+ // We extend Array slightly with a top-of-stack method.
+ definitions.defineProperty(Array.prototype, "top",
+ function() {
+ return this.length && this[this.length-1];
+ }, false, false, true);
+
+ /*
+ * Node :: (tokenizer, optional init object) -> node
+ */
+ function Node(t, init) {
+ var token = t.token;
+ if (token) {
+ // If init.type exists it will override token.type.
+ this.type = token.type;
+ this.value = token.value;
+ this.lineno = token.lineno;
+
+ // Start and end are file positions for error handling.
+ this.start = token.start;
+ this.end = token.end;
+ } else {
+ this.lineno = t.lineno;
+ }
+
+ // Node uses a tokenizer for debugging (getSource, filename getter).
+ this.tokenizer = t;
+ this.children = [];
+
+ for (var prop in init)
+ this[prop] = init[prop];
+ }
+
+ /*
+ * SyntheticNode :: (tokenizer, optional init object) -> node
+ */
+ function SyntheticNode(t, init) {
+ // print("SYNTHETIC NODE");
+ // if (init.type === COMMA) {
+ // print("SYNTHETIC COMMA");
+ // print(init);
+ // }
+ this.tokenizer = t;
+ this.children = [];
+ for (var prop in init)
+ this[prop] = init[prop];
+ this.synthetic = true;
+ }
+
+ var Np = Node.prototype = SyntheticNode.prototype = {};
+ Np.constructor = Node;
+
+ const TO_SOURCE_SKIP = {
+ type: true,
+ value: true,
+ lineno: true,
+ start: true,
+ end: true,
+ tokenizer: true,
+ assignOp: true
+ };
+ function unevalableConst(code) {
+ var token = definitions.tokens[code];
+ var constName = definitions.opTypeNames.hasOwnProperty(token)
+ ? definitions.opTypeNames[token]
+ : token in definitions.keywords
+ ? token.toUpperCase()
+ : token;
+ return { toSource: function() { return constName } };
+ }
+ Np.toSource = function toSource() {
+ var mock = {};
+ var self = this;
+ mock.type = unevalableConst(this.type);
+ if ("value" in this)
+ mock.value = this.value;
+ if ("lineno" in this)
+ mock.lineno = this.lineno;
+ if ("start" in this)
+ mock.start = this.start;
+ if ("end" in this)
+ mock.end = this.end;
+ if (this.assignOp)
+ mock.assignOp = unevalableConst(this.assignOp);
+ for (var key in this) {
+ if (this.hasOwnProperty(key) && !(key in TO_SOURCE_SKIP))
+ mock[key] = this[key];
+ }
+ return mock.toSource();
+ };
+
+ // Always use push to add operands to an expression, to update start and end.
+ Np.push = function (kid) {
+ // kid can be null e.g. [1, , 2].
+ if (kid !== null) {
+ if (kid.start < this.start)
+ this.start = kid.start;
+ if (this.end < kid.end)
+ this.end = kid.end;
+ }
+ return this.children.push(kid);
+ }
+
+ Node.indentLevel = 0;
+
+ function tokenString(tt) {
+ var t = definitions.tokens[tt];
+ return /^\W/.test(t) ? definitions.opTypeNames[t] : t.toUpperCase();
+ }
+
+ Np.toString = function () {
+ var a = [];
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && i !== 'type' && i !== 'target')
+ a.push({id: i, value: this[i]});
+ }
+ a.sort(function (a,b) { return (a.id < b.id) ? -1 : 1; });
+ const INDENTATION = " ";
+ var n = ++Node.indentLevel;
+ var s = "{\n" + INDENTATION.repeat(n) + "type: " + tokenString(this.type);
+ for (i = 0; i < a.length; i++)
+ s += ",\n" + INDENTATION.repeat(n) + a[i].id + ": " + a[i].value;
+ n = --Node.indentLevel;
+ s += "\n" + INDENTATION.repeat(n) + "}";
+ return s;
+ }
+
+ Np.getSource = function () {
+ return this.tokenizer.source.slice(this.start, this.end);
+ };
+
+ /*
+ * Helper init objects for common nodes.
+ */
+
+ const LOOP_INIT = { isLoop: true };
+
+ function blockInit() {
+ return { type: BLOCK, varDecls: [] };
+ }
+
+ function scriptInit() {
+ return { type: SCRIPT,
+ funDecls: [],
+ varDecls: [],
+ modDefns: new StringMap(),
+ modAssns: new StringMap(),
+ modDecls: new StringMap(),
+ modLoads: new StringMap(),
+ impDecls: [],
+ expDecls: [],
+ exports: new StringMap(),
+ hasEmptyReturn: false,
+ hasReturnWithValue: false,
+ isGenerator: false };
+ }
+
+ definitions.defineGetter(Np, "filename",
+ function() {
+ return this.tokenizer.filename;
+ });
+
+ definitions.defineGetter(Np, "length",
+ function() {
+ throw new Error("Node.prototype.length is gone; " +
+ "use n.children.length instead");
+ });
+
+ definitions.defineProperty(String.prototype, "repeat",
+ function(n) {
+ var s = "", t = this + s;
+ while (--n >= 0)
+ s += t;
+ return s;
+ }, false, false, true);
+
+ function MaybeLeftParen(t, x) {
+ if (x.parenFreeMode)
+ return t.match(LEFT_PAREN) ? LEFT_PAREN : END;
+ return t.mustMatch(LEFT_PAREN).type;
+ }
+
+ function MaybeRightParen(t, p) {
+ if (p === LEFT_PAREN)
+ t.mustMatch(RIGHT_PAREN);
+ }
+
+ /*
+ * Statements :: (tokenizer, compiler context, node) -> void
+ *
+ * Parses a sequence of Statements.
+ */
+ function Statements(t, x, n) {
+ try {
+ while (!t.done && t.peek(true) !== RIGHT_CURLY)
+ n.push(Statement(t, x));
+ } catch (e) {
+ if (t.done)
+ t.unexpectedEOF = true;
+ throw e;
+ }
+ }
+
+ function Block(t, x) {
+ t.mustMatch(LEFT_CURLY);
+ var n = new Node(t, blockInit());
+ Statements(t, x.update({ parentBlock: n }).pushTarget(n), n);
+ t.mustMatch(RIGHT_CURLY);
+ return n;
+ }
+
+ const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
+
+ /*
+ * Export :: (binding node, boolean) -> Export
+ *
+ * Static semantic representation of a module export.
+ */
+ function Export(node, isDefinition) {
+ this.node = node; // the AST node declaring this individual export
+ this.isDefinition = isDefinition; // is the node an 'export'-annotated definition?
+ this.resolved = null; // resolved pointer to the target of this export
+ }
+
+ /*
+ * registerExport :: (StringMap, EXPORT node) -> void
+ */
+ function registerExport(exports, decl) {
+ function register(name, exp) {
+ if (exports.has(name))
+ throw new SyntaxError("multiple exports of " + name);
+ exports.set(name, exp);
+ }
+
+ switch (decl.type) {
+ case MODULE:
+ case FUNCTION:
+ register(decl.name, new Export(decl, true));
+ break;
+
+ case VAR:
+ for (var i = 0; i < decl.children.length; i++)
+ register(decl.children[i].name, new Export(decl.children[i], true));
+ break;
+
+ case LET:
+ case CONST:
+ throw new Error("NYI: " + definitions.tokens[decl.type]);
+
+ case EXPORT:
+ for (var i = 0; i < decl.pathList.length; i++) {
+ var path = decl.pathList[i];
+ switch (path.type) {
+ case OBJECT_INIT:
+ for (var j = 0; j < path.children.length; j++) {
+ // init :: IDENTIFIER | PROPERTY_INIT
+ var init = path.children[j];
+ if (init.type === IDENTIFIER)
+ register(init.value, new Export(init, false));
+ else
+ register(init.children[0].value, new Export(init.children[1], false));
+ }
+ break;
+
+ case DOT:
+ register(path.children[1].value, new Export(path, false));
+ break;
+
+ case IDENTIFIER:
+ register(path.value, new Export(path, false));
+ break;
+
+ default:
+ throw new Error("unexpected export path: " + definitions.tokens[path.type]);
+ }
+ }
+ break;
+
+ default:
+ throw new Error("unexpected export decl: " + definitions.tokens[exp.type]);
+ }
+ }
+
+ /*
+ * Module :: (node) -> Module
+ *
+ * Static semantic representation of a module.
+ */
+ function Module(node) {
+ var exports = node.body.exports;
+ var modDefns = node.body.modDefns;
+
+ var exportedModules = new StringMap();
+
+ exports.forEach(function(name, exp) {
+ var node = exp.node;
+ if (node.type === MODULE) {
+ exportedModules.set(name, node);
+ } else if (!exp.isDefinition && node.type === IDENTIFIER && modDefns.has(node.value)) {
+ var mod = modDefns.get(node.value);
+ exportedModules.set(name, mod);
+ }
+ });
+
+ this.node = node;
+ this.exports = exports;
+ this.exportedModules = exportedModules;
+ }
+
+ /*
+ * Statement :: (tokenizer, compiler context) -> node
+ *
+ * Parses a Statement.
+ */
+ function Statement(t, x) {
+ var i, label, n, n2, p, c, ss, tt = t.get(true), tt2, x2, x3;
+
+ var comments = t.blockComments;
+
+ if (x.blackList[tt])
+ throw t.newSyntaxError(definitions.tokens[tt] + " statements only allowed in Harmony");
+ if (!x.allow(tt))
+ throw t.newSyntaxError(definitions.tokens[tt] + " statement in illegal context");
+
+ // Cases for statements ending in a right curly return early, avoiding the
+ // common semicolon insertion magic after this switch.
+ switch (tt) {
+ case IMPORT:
+ n = new Node(t);
+ n.pathList = ImportPathList(t, x);
+ x.parentScript.impDecls.push(n);
+ n.blockComments = comments;
+ break;
+
+ case EXPORT:
+ switch (t.peek()) {
+ case MODULE:
+ case FUNCTION:
+ case LET:
+ case VAR:
+ case CONST:
+ n = Statement(t, x);
+ n.blockComments = comments;
+ n.exported = true;
+ x.parentScript.expDecls.push(n);
+ registerExport(x.parentScript.exports, n);
+ return n;
+
+ default:
+ n = new Node(t);
+ n.blockComments = comments;
+ n.pathList = ExportPathList(t, x);
+ break;
+ }
+ x.parentScript.expDecls.push(n);
+ registerExport(x.parentScript.exports, n);
+ break;
+
+ case MODULE:
+ n = new Node(t);
+ n.blockComments = comments;
+ t.mustMatch(IDENTIFIER);
+ label = t.token.value;
+
+ if (t.match(LEFT_CURLY)) {
+ n.name = label;
+ n.body = Script(t, true, false);
+ n.module = new Module(n);
+ t.mustMatch(RIGHT_CURLY);
+ x.parentScript.modDefns.set(n.name, n);
+ return n;
+ }
+
+ t.unget();
+ ModuleVariables(t, x, n);
+ return n;
+
+ case FUNCTION:
+ // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't.
+ return FunctionDefinition(t, x, true, x.topLevel ? DECLARED_FORM : STATEMENT_FORM, comments);
+
+ case LEFT_CURLY:
+ n = new Node(t, blockInit());
+ n.blockComments = comments;
+ Statements(t, x.update({ parentBlock: n }).pushTarget(n).nest(), n);
+ t.mustMatch(RIGHT_CURLY);
+ return n;
+
+ case IF:
+ n = new Node(t);
+ n.blockComments = comments;
+ n.condition = HeadExpression(t, x);
+ x2 = x.pushTarget(n).nest();
+ n.thenPart = Statement(t, x2);
+ n.elsePart = t.match(ELSE, true) ? Statement(t, x2) : null;
+ return n;
+
+ case SWITCH:
+ // This allows CASEs after a DEFAULT, which is in the standard.
+ n = new Node(t, { cases: [], defaultIndex: -1 });
+ n.blockComments = comments;
+ n.discriminant = HeadExpression(t, x);
+ x2 = x.pushTarget(n).nest();
+ t.mustMatch(LEFT_CURLY);
+ while ((tt = t.get()) !== RIGHT_CURLY) {
+ switch (tt) {
+ case DEFAULT:
+ if (n.defaultIndex >= 0)
+ throw t.newSyntaxError("More than one switch default");
+ // FALL THROUGH
+ case CASE:
+ n2 = new Node(t);
+ if (tt === DEFAULT)
+ n.defaultIndex = n.cases.length;
+ else
+ n2.caseLabel = Expression(t, x2, COLON);
+ break;
+
+ default:
+ throw t.newSyntaxError("Invalid switch case");
+ }
+ t.mustMatch(COLON);
+ n2.statements = new Node(t, blockInit());
+ while ((tt=t.peek(true)) !== CASE && tt !== DEFAULT &&
+ tt !== RIGHT_CURLY)
+ n2.statements.push(Statement(t, x2));
+ n.cases.push(n2);
+ }
+ return n;
+
+ case FOR:
+ n = new Node(t, LOOP_INIT);
+ n.blockComments = comments;
+ if (t.match(IDENTIFIER)) {
+ if (t.token.value === "each")
+ n.isEach = true;
+ else
+ t.unget();
+ }
+ if (!x.parenFreeMode)
+ t.mustMatch(LEFT_PAREN);
+ x2 = x.pushTarget(n).nest();
+ x3 = x.update({ inForLoopInit: true });
+ n2 = null;
+ if ((tt = t.peek(true)) !== SEMICOLON) {
+ if (tt === VAR || tt === CONST) {
+ t.get();
+ n2 = Variables(t, x3);
+ } else if (tt === LET) {
+ t.get();
+ if (t.peek() === LEFT_PAREN) {
+ n2 = LetBlock(t, x3, false);
+ } else {
+ // Let in for head, we need to add an implicit block
+ // around the rest of the for.
+ x3.parentBlock = n;
+ n.varDecls = [];
+ n2 = Variables(t, x3);
+ }
+ } else {
+ n2 = Expression(t, x3);
+ }
+ }
+ if (n2 && t.match(IN)) {
+ n.type = FOR_IN;
+ n.object = Expression(t, x3);
+ if (n2.type === VAR || n2.type === LET) {
+ c = n2.children;
+
+ // Destructuring turns one decl into multiples, so either
+ // there must be only one destructuring or only one
+ // decl.
+ if (c.length !== 1 && n2.destructurings.length !== 1) {
+ throw new SyntaxError("Invalid for..in left-hand side",
+ t.filename, n2.lineno);
+ }
+ if (n2.destructurings.length > 0) {
+ n.iterator = n2.destructurings[0];
+ } else {
+ n.iterator = c[0];
+ }
+ n.varDecl = n2;
+ } else {
+ if (n2.type === ARRAY_INIT || n2.type === OBJECT_INIT) {
+ n2.destructuredNames = checkDestructuring(t, x3, n2);
+ }
+ n.iterator = n2;
+ }
+ } else {
+ x3.inForLoopInit = false;
+ n.setup = n2;
+ t.mustMatch(SEMICOLON);
+ if (n.isEach)
+ throw t.newSyntaxError("Invalid for each..in loop");
+ n.condition = (t.peek(true) === SEMICOLON)
+ ? null
+ : Expression(t, x3);
+ t.mustMatch(SEMICOLON);
+ tt2 = t.peek(true);
+ n.update = (x.parenFreeMode
+ ? tt2 === LEFT_CURLY || definitions.isStatementStartCode[tt2]
+ : tt2 === RIGHT_PAREN)
+ ? null
+ : Expression(t, x3);
+ }
+ if (!x.parenFreeMode)
+ t.mustMatch(RIGHT_PAREN);
+ n.body = Statement(t, x2);
+ return n;
+
+ case WHILE:
+ n = new Node(t, { isLoop: true });
+ n.blockComments = comments;
+ n.condition = HeadExpression(t, x);
+ n.body = Statement(t, x.pushTarget(n).nest());
+ return n;
+
+ case DO:
+ n = new Node(t, { isLoop: true });
+ n.blockComments = comments;
+ n.body = Statement(t, x.pushTarget(n).nest());
+ t.mustMatch(WHILE);
+ n.condition = HeadExpression(t, x);
+ if (!x.ecmaStrictMode) {
+ // <script language="JavaScript"> (without version hints) may need
+ // automatic semicolon insertion without a newline after do-while.
+ // See http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
+ t.match(SEMICOLON);
+ return n;
+ }
+ break;
+
+ case BREAK:
+ case CONTINUE:
+ n = new Node(t);
+ n.blockComments = comments;
+
+ // handle the |foo: break foo;| corner case
+ x2 = x.pushTarget(n);
+
+ if (t.peekOnSameLine() === IDENTIFIER) {
+ t.get();
+ n.label = t.token.value;
+ }
+
+ if (n.label) {
+ n.target = x2.labeledTargets.find(function(target) { return target.labels.has(n.label) });
+ } else if (tt === CONTINUE) {
+ n.target = x2.defaultLoopTarget;
+ } else {
+ n.target = x2.defaultTarget;
+ }
+
+ if (!n.target)
+ throw t.newSyntaxError("Invalid " + ((tt === BREAK) ? "break" : "continue"));
+ if (!n.target.isLoop && tt === CONTINUE)
+ throw t.newSyntaxError("Invalid continue");
+
+ break;
+
+ case TRY:
+ n = new Node(t, { catchClauses: [] });
+ n.blockComments = comments;
+ n.tryBlock = Block(t, x);
+ while (t.match(CATCH)) {
+ n2 = new Node(t);
+ p = MaybeLeftParen(t, x);
+ switch (t.get()) {
+ case LEFT_BRACKET:
+ case LEFT_CURLY:
+ // Destructured catch identifiers.
+ t.unget();
+ n2.varName = DestructuringExpression(t, x, true);
+ break;
+ case IDENTIFIER:
+ n2.varName = t.token.value;
+ break;
+ default:
+ throw t.newSyntaxError("missing identifier in catch");
+ break;
+ }
+ if (t.match(IF)) {
+ if (x.ecma3OnlyMode)
+ throw t.newSyntaxError("Illegal catch guard");
+ if (n.catchClauses.length && !n.catchClauses.top().guard)
+ throw t.newSyntaxError("Guarded catch after unguarded");
+ n2.guard = Expression(t, x);
+ }
+ MaybeRightParen(t, p);
+ n2.block = Block(t, x);
+ n.catchClauses.push(n2);
+ }
+ if (t.match(FINALLY))
+ n.finallyBlock = Block(t, x);
+ if (!n.catchClauses.length && !n.finallyBlock)
+ throw t.newSyntaxError("Invalid try statement");
+ return n;
+
+ case CATCH:
+ case FINALLY:
+ throw t.newSyntaxError(definitions.tokens[tt] + " without preceding try");
+
+ case THROW:
+ n = new Node(t);
+ n.blockComments = comments;
+ n.exception = Expression(t, x);
+ break;
+
+ case RETURN:
+ n = ReturnOrYield(t, x);
+ break;
+
+ case WITH:
+ n = new Node(t);
+ n.blockComments = comments;
+ n.object = HeadExpression(t, x);
+ n.body = Statement(t, x.pushTarget(n).nest());
+ return n;
+
+ case VAR:
+ case CONST:
+ n = Variables(t, x);
+ break;
+
+ case LET:
+ if (t.peek() === LEFT_PAREN)
+ n = LetBlock(t, x, true);
+ else
+ n = Variables(t, x);
+ break;
+
+ case DEBUGGER:
+ n = new Node(t);
+ n.blockComments = comments;
+ break;
+
+ case NEWLINE:
+ case SEMICOLON:
+ n = new Node(t, { type: SEMICOLON });
+ n.blockComments = comments;
+ n.expression = null;
+ return n;
+
+ default:
+ if (tt === IDENTIFIER) {
+ tt = t.peek();
+ // Labeled statement.
+ if (tt === COLON) {
+ label = t.token.value;
+ if (x.allLabels.has(label))
+ throw t.newSyntaxError("Duplicate label");
+ t.get();
+ n = new Node(t, { type: LABEL, label: label });
+ n.blockComments = comments;
+ n.statement = Statement(t, x.pushLabel(label).nest());
+ n.target = (n.statement.type === LABEL) ? n.statement.target : n.statement;
+ return n;
+ }
+ }
+
+ // Expression statement.
+ // We unget the current token to parse the expression as a whole.
+ n = new Node(t, { type: SEMICOLON });
+ t.unget();
+ n.blockComments = comments;
+ n.expression = Expression(t, x);
+ n.end = n.expression.end;
+ break;
+ }
+
+ n.blockComments = comments;
+ MagicalSemicolon(t);
+ return n;
+ }
+
+ /*
+ * MagicalSemicolon :: (tokenizer) -> void
+ */
+ function MagicalSemicolon(t) {
+ var tt;
+ if (t.lineno === t.token.lineno) {
+ tt = t.peekOnSameLine();
+ if (tt !== END && tt !== NEWLINE && tt !== SEMICOLON && tt !== RIGHT_CURLY)
+ throw t.newSyntaxError("missing ; before statement");
+ }
+ t.match(SEMICOLON);
+ }
+
+ /*
+ * ReturnOrYield :: (tokenizer, compiler context) -> (RETURN | YIELD) node
+ */
+ function ReturnOrYield(t, x) {
+ var n, b, tt = t.token.type, tt2;
+
+ var parentScript = x.parentScript;
+
+ if (tt === RETURN) {
+ if (!x.inFunction) {
+ // pass
+ //throw t.newSyntaxError("Return not in function");
+ }
+
+ } else /* if (tt === YIELD) */ {
+ if (!x.inFunction)
+ throw t.newSyntaxError("Yield not in function");
+ parentScript.isGenerator = true;
+ }
+ n = new Node(t, { value: undefined });
+
+ tt2 = (tt === RETURN) ? t.peekOnSameLine(true) : t.peek(true);
+ if (tt2 !== END && tt2 !== NEWLINE &&
+ tt2 !== SEMICOLON && tt2 !== RIGHT_CURLY
+ && (tt !== YIELD ||
+ (tt2 !== tt && tt2 !== RIGHT_BRACKET && tt2 !== RIGHT_PAREN &&
+ tt2 !== COLON && tt2 !== COMMA))) {
+ if (tt === RETURN) {
+ n.value = Expression(t, x);
+ parentScript.hasReturnWithValue = true;
+ } else {
+ n.value = AssignExpression(t, x);
+ }
+ } else if (tt === RETURN) {
+ parentScript.hasEmptyReturn = true;
+ }
+
+ // Disallow return v; in generator.
+ if (parentScript.hasReturnWithValue && parentScript.isGenerator)
+ throw t.newSyntaxError("Generator returns a value");
+
+ return n;
+ }
+
+ /*
+ * ModuleExpression :: (tokenizer, compiler context) -> (STRING | IDENTIFIER | DOT) node
+ */
+ function ModuleExpression(t, x) {
+ return t.match(STRING) ? new Node(t) : QualifiedPath(t, x);
+ }
+
+ /*
+ * ImportPathList :: (tokenizer, compiler context) -> Array[DOT node]
+ */
+ function ImportPathList(t, x) {
+ var a = [];
+ do {
+ a.push(ImportPath(t, x));
+ } while (t.match(COMMA));
+ return a;
+ }
+
+ /*
+ * ImportPath :: (tokenizer, compiler context) -> DOT node
+ */
+ function ImportPath(t, x) {
+ var n = QualifiedPath(t, x);
+ if (!t.match(DOT)) {
+ if (n.type === IDENTIFIER)
+ throw t.newSyntaxError("cannot import local variable");
+ return n;
+ }
+
+ var n2 = new Node(t);
+ n2.push(n);
+ n2.push(ImportSpecifierSet(t, x));
+ return n2;
+ }
+
+ /*
+ * ExplicitSpecifierSet :: (tokenizer, compiler context, (tokenizer, compiler context) -> node)
+ * -> OBJECT_INIT node
+ */
+ function ExplicitSpecifierSet(t, x, SpecifierRHS) {
+ var n, n2, id, tt;
+
+ n = new Node(t, { type: OBJECT_INIT });
+ t.mustMatch(LEFT_CURLY);
+
+ if (!t.match(RIGHT_CURLY)) {
+ do {
+ id = Identifier(t, x);
+ if (t.match(COLON)) {
+ n2 = new Node(t, { type: PROPERTY_INIT });
+ n2.push(id);
+ n2.push(SpecifierRHS(t, x));
+ n.push(n2);
+ } else {
+ n.push(id);
+ }
+ } while (!t.match(RIGHT_CURLY) && t.mustMatch(COMMA));
+ }
+
+ return n;
+ }
+
+ /*
+ * ImportSpecifierSet :: (tokenizer, compiler context) -> (IDENTIFIER | OBJECT_INIT) node
+ */
+ function ImportSpecifierSet(t, x) {
+ return t.match(MUL)
+ ? new Node(t, { type: IDENTIFIER, name: "*" })
+ : ExplicitSpecifierSet(t, x, Identifier);
+ }
+
+ /*
+ * Identifier :: (tokenizer, compiler context) -> IDENTIFIER node
+ */
+ function Identifier(t, x) {
+ t.mustMatch(IDENTIFIER);
+ return new Node(t, { type: IDENTIFIER });
+ }
+
+ /*
+ * IdentifierName :: (tokenizer) -> IDENTIFIER node
+ */
+ function IdentifierName(t) {
+ if (t.match(IDENTIFIER))
+ return new Node(t, { type: IDENTIFIER });
+ t.get();
+ if (t.token.value in definitions.keywords)
+ return new Node(t, { type: IDENTIFIER });
+ throw t.newSyntaxError("missing IdentifierName");
+ }
+
+ /*
+ * QualifiedPath :: (tokenizer, compiler context) -> (IDENTIFIER | DOT) node
+ */
+ function QualifiedPath(t, x) {
+ var n, n2;
+
+ n = Identifier(t, x);
+
+ while (t.match(DOT)) {
+ if (t.peek() !== IDENTIFIER) {
+ // Unget the '.' token, which isn't part of the QualifiedPath.
+ t.unget();
+ break;
+ }
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(Identifier(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ /*
+ * ExportPath :: (tokenizer, compiler context) -> (IDENTIFIER | DOT | OBJECT_INIT) node
+ */
+ function ExportPath(t, x) {
+ if (t.peek() === LEFT_CURLY)
+ return ExplicitSpecifierSet(t, x, QualifiedPath);
+ return QualifiedPath(t, x);
+ }
+
+ /*
+ * ExportPathList :: (tokenizer, compiler context)
+ * -> Array[(IDENTIFIER | DOT | OBJECT_INIT) node]
+ */
+ function ExportPathList(t, x) {
+ var a = [];
+ do {
+ a.push(ExportPath(t, x));
+ } while (t.match(COMMA));
+ return a;
+ }
+
+ /*
+ * FunctionDefinition :: (tokenizer, compiler context, boolean,
+ * DECLARED_FORM or EXPRESSED_FORM or STATEMENT_FORM,
+ * [string] or null or undefined)
+ * -> node
+ */
+ function FunctionDefinition(t, x, requireName, functionForm, comments) {
+ var tt;
+ var f = new Node(t, { params: [], paramComments: [] });
+ if (typeof comment === "undefined")
+ comment = null;
+ f.blockComments = comments;
+ if (f.type !== FUNCTION)
+ f.type = (f.value === "get") ? GETTER : SETTER;
+ if (t.match(IDENTIFIER))
+ f.name = t.token.value;
+ else if (requireName)
+ throw t.newSyntaxError("missing function identifier");
+
+ var inModule = x ? x.inModule : false;
+ var x2 = new StaticContext(null, null, inModule, true);
+
+ t.mustMatch(LEFT_PAREN);
+ if (!t.match(RIGHT_PAREN)) {
+ do {
+ tt = t.get();
+ f.paramComments.push(t.lastBlockComment());
+ switch (tt) {
+ case LEFT_BRACKET:
+ case LEFT_CURLY:
+ // Destructured formal parameters.
+ t.unget();
+ f.params.push(DestructuringExpression(t, x2));
+ break;
+ case IDENTIFIER:
+ f.params.push(t.token.value);
+ break;
+ default:
+ throw t.newSyntaxError("missing formal parameter");
+ break;
+ }
+ } while (t.match(COMMA));
+ t.mustMatch(RIGHT_PAREN);
+ }
+
+ // Do we have an expression closure or a normal body?
+ tt = t.get();
+ if (tt !== LEFT_CURLY)
+ t.unget();
+
+ if (tt !== LEFT_CURLY) {
+ f.body = AssignExpression(t, x2);
+ if (f.body.isGenerator)
+ throw t.newSyntaxError("Generator returns a value");
+ } else {
+ f.body = Script(t, inModule, true);
+ }
+
+ if (tt === LEFT_CURLY)
+ t.mustMatch(RIGHT_CURLY);
+
+ f.end = t.token.end;
+ f.functionForm = functionForm;
+ if (functionForm === DECLARED_FORM)
+ x.parentScript.funDecls.push(f);
+ return f;
+ }
+
+ /*
+ * ModuleVariables :: (tokenizer, compiler context, MODULE node) -> void
+ *
+ * Parses a comma-separated list of module declarations (and maybe
+ * initializations).
+ */
+ function ModuleVariables(t, x, n) {
+ var n1, n2;
+ do {
+ n1 = Identifier(t, x);
+ if (t.match(ASSIGN)) {
+ n2 = ModuleExpression(t, x);
+ n1.initializer = n2;
+ if (n2.type === STRING)
+ x.parentScript.modLoads.set(n1.value, n2.value);
+ else
+ x.parentScript.modAssns.set(n1.value, n1);
+ }
+ n.push(n1);
+ } while (t.match(COMMA));
+ }
+
+ /*
+ * Variables :: (tokenizer, compiler context) -> node
+ *
+ * Parses a comma-separated list of var declarations (and maybe
+ * initializations).
+ */
+ function Variables(t, x, letBlock) {
+ var n, n2, ss, i, s, tt;
+
+ tt = t.token.type;
+ switch (tt) {
+ case VAR:
+ case CONST:
+ s = x.parentScript;
+ break;
+ case LET:
+ s = x.parentBlock;
+ break;
+ case LEFT_PAREN:
+ tt = LET;
+ s = letBlock;
+ break;
+ }
+
+ n = new Node(t, { type: tt, destructurings: [] });
+
+ do {
+ tt = t.get();
+ if (tt === LEFT_BRACKET || tt === LEFT_CURLY) {
+ // Need to unget to parse the full destructured expression.
+ t.unget();
+
+ var dexp = DestructuringExpression(t, x, true);
+
+ n2 = new Node(t, { type: IDENTIFIER,
+ name: dexp,
+ readOnly: n.type === CONST });
+ n.push(n2);
+ pushDestructuringVarDecls(n2.name.destructuredNames, s);
+ n.destructurings.push({ exp: dexp, decl: n2 });
+
+ if (x.inForLoopInit && t.peek() === IN) {
+ continue;
+ }
+
+ t.mustMatch(ASSIGN);
+ if (t.token.assignOp)
+ throw t.newSyntaxError("Invalid variable initialization");
+
+ n2.blockComment = t.lastBlockComment();
+ n2.initializer = AssignExpression(t, x);
+
+ continue;
+ }
+
+ if (tt !== IDENTIFIER)
+ throw t.newSyntaxError("missing variable name");
+
+ n2 = new Node(t, { type: IDENTIFIER,
+ name: t.token.value,
+ readOnly: n.type === CONST });
+ n.push(n2);
+ s.varDecls.push(n2);
+
+ if (t.match(ASSIGN)) {
+ var comment = t.lastBlockComment();
+ if (t.token.assignOp)
+ throw t.newSyntaxError("Invalid variable initialization");
+
+ n2.initializer = AssignExpression(t, x);
+ } else {
+ var comment = t.lastBlockComment();
+ }
+ n2.blockComment = comment;
+ } while (t.match(COMMA));
+
+ return n;
+ }
+
+ /*
+ * LetBlock :: (tokenizer, compiler context, boolean) -> node
+ *
+ * Does not handle let inside of for loop init.
+ */
+ function LetBlock(t, x, isStatement) {
+ var n, n2;
+
+ // t.token.type must be LET
+ n = new Node(t, { type: LET_BLOCK, varDecls: [] });
+ t.mustMatch(LEFT_PAREN);
+ n.variables = Variables(t, x, n);
+ t.mustMatch(RIGHT_PAREN);
+
+ if (isStatement && t.peek() !== LEFT_CURLY) {
+ /*
+ * If this is really an expression in let statement guise, then we
+ * need to wrap the LET_BLOCK node in a SEMICOLON node so that we pop
+ * the return value of the expression.
+ */
+ n2 = new Node(t, { type: SEMICOLON,
+ expression: n });
+ isStatement = false;
+ }
+
+ if (isStatement)
+ n.block = Block(t, x);
+ else
+ n.expression = AssignExpression(t, x);
+
+ return n;
+ }
+
+ function checkDestructuring(t, x, n, simpleNamesOnly) {
+ if (n.type === ARRAY_COMP)
+ throw t.newSyntaxError("Invalid array comprehension left-hand side");
+ if (n.type !== ARRAY_INIT && n.type !== OBJECT_INIT)
+ return undefined;
+
+ var lhss = {};
+ var nn, n2, idx, sub, cc, c = n.children;
+ for (var i = 0, j = c.length; i < j; i++) {
+ if (!(nn = c[i]))
+ continue;
+ if (nn.type === PROPERTY_INIT) {
+ cc = nn.children;
+ sub = cc[1];
+ idx = cc[0].value;
+ } else if (n.type === OBJECT_INIT) {
+ // Do we have destructuring shorthand {foo, bar}?
+ sub = nn;
+ idx = nn.value;
+ } else {
+ sub = nn;
+ idx = i;
+ }
+
+ if (sub.type === ARRAY_INIT || sub.type === OBJECT_INIT) {
+ lhss[idx] = checkDestructuring(t, x, sub, simpleNamesOnly);
+ } else {
+ if (simpleNamesOnly && sub.type !== IDENTIFIER) {
+ // In declarations, lhs must be simple names
+ throw t.newSyntaxError("missing name in pattern");
+ }
+
+ lhss[idx] = sub;
+ }
+ }
+
+ return lhss;
+ }
+
+ function DestructuringExpression(t, x, simpleNamesOnly) {
+ var n = PrimaryExpression(t, x);
+ // Keep the list of lefthand sides for varDecls
+ n.destructuredNames = checkDestructuring(t, x, n, simpleNamesOnly);
+ return n;
+ }
+
+ function GeneratorExpression(t, x, e) {
+ return new Node(t, { type: GENERATOR,
+ expression: e,
+ tail: ComprehensionTail(t, x) });
+ }
+
+ function ComprehensionTail(t, x) {
+ var body, n, n2, n3, p;
+
+ // t.token.type must be FOR
+ body = new Node(t, { type: COMP_TAIL });
+
+ do {
+ // Comprehension tails are always for..in loops.
+ n = new Node(t, { type: FOR_IN, isLoop: true });
+ if (t.match(IDENTIFIER)) {
+ // But sometimes they're for each..in.
+ if (t.token.value === "each")
+ n.isEach = true;
+ else
+ t.unget();
+ }
+ p = MaybeLeftParen(t, x);
+ switch(t.get()) {
+ case LEFT_BRACKET:
+ case LEFT_CURLY:
+ t.unget();
+ // Destructured left side of for in comprehension tails.
+ n.iterator = DestructuringExpression(t, x);
+ break;
+
+ case IDENTIFIER:
+ n.iterator = n3 = new Node(t, { type: IDENTIFIER });
+ n3.name = n3.value;
+ n.varDecl = n2 = new Node(t, { type: VAR });
+ n2.push(n3);
+ x.parentScript.varDecls.push(n3);
+ // Don't add to varDecls since the semantics of comprehensions is
+ // such that the variables are in their own function when
+ // desugared.
+ break;
+
+ default:
+ throw t.newSyntaxError("missing identifier");
+ }
+ t.mustMatch(IN);
+ n.object = Expression(t, x);
+ MaybeRightParen(t, p);
+ body.push(n);
+ } while (t.match(FOR));
+
+ // Optional guard.
+ if (t.match(IF))
+ body.guard = HeadExpression(t, x);
+
+ return body;
+ }
+
+ function HeadExpression(t, x) {
+ var p = MaybeLeftParen(t, x);
+ var n = ParenExpression(t, x);
+ MaybeRightParen(t, p);
+ if (p === END && !n.parenthesized) {
+ var tt = t.peek();
+ if (tt !== LEFT_CURLY && !definitions.isStatementStartCode[tt])
+ throw t.newSyntaxError("Unparenthesized head followed by unbraced body");
+ }
+ return n;
+ }
+
+ function ParenExpression(t, x) {
+ // Always accept the 'in' operator in a parenthesized expression,
+ // where it's unambiguous, even if we might be parsing the init of a
+ // for statement.
+ var n = Expression(t, x.update({ inForLoopInit: x.inForLoopInit &&
+ (t.token.type === LEFT_PAREN) }));
+
+ if (t.match(FOR)) {
+ if (n.type === YIELD && !n.parenthesized)
+ throw t.newSyntaxError("Yield expression must be parenthesized");
+ if (n.type === COMMA && !n.parenthesized)
+ throw t.newSyntaxError("Generator expression must be parenthesized");
+ n = GeneratorExpression(t, x, n);
+ }
+
+ return n;
+ }
+
+ /*
+ * Expression :: (tokenizer, compiler context) -> node
+ *
+ * Top-down expression parser matched against SpiderMonkey.
+ */
+ function Expression(t, x) {
+ var n, n2;
+
+ n = AssignExpression(t, x);
+ if (t.match(COMMA)) {
+ n2 = new Node(t, { type: COMMA });
+ n2.push(n);
+ n = n2;
+ do {
+ n2 = n.children[n.children.length-1];
+ if (n2.type === YIELD && !n2.parenthesized)
+ throw t.newSyntaxError("Yield expression must be parenthesized");
+ n.push(AssignExpression(t, x));
+ } while (t.match(COMMA));
+ }
+
+ return n;
+ }
+
+ function AssignExpression(t, x) {
+ var n, lhs;
+
+ // Have to treat yield like an operand because it could be the leftmost
+ // operand of the expression.
+ if (t.match(YIELD, true))
+ return ReturnOrYield(t, x);
+
+ n = new Node(t, { type: ASSIGN });
+ lhs = ConditionalExpression(t, x);
+
+ if (!t.match(ASSIGN)) {
+ return lhs;
+ }
+
+ n.blockComment = t.lastBlockComment();
+
+ switch (lhs.type) {
+ case OBJECT_INIT:
+ case ARRAY_INIT:
+ lhs.destructuredNames = checkDestructuring(t, x, lhs);
+ // FALL THROUGH
+ case IDENTIFIER: case DOT: case INDEX: case CALL:
+ break;
+ default:
+ throw t.newSyntaxError("Bad left-hand side of assignment");
+ break;
+ }
+
+ n.assignOp = lhs.assignOp = t.token.assignOp;
+ n.push(lhs);
+ n.push(AssignExpression(t, x));
+
+ return n;
+ }
+
+ function ConditionalExpression(t, x) {
+ var n, n2;
+
+ n = OrExpression(t, x);
+ if (t.match(HOOK)) {
+ n2 = n;
+ n = new Node(t, { type: HOOK });
+ n.push(n2);
+ /*
+ * Always accept the 'in' operator in the middle clause of a ternary,
+ * where it's unambiguous, even if we might be parsing the init of a
+ * for statement.
+ */
+ n.push(AssignExpression(t, x.update({ inForLoopInit: false })));
+ if (!t.match(COLON))
+ throw t.newSyntaxError("missing : after ?");
+ n.push(AssignExpression(t, x));
+ }
+
+ return n;
+ }
+
+ function OrExpression(t, x) {
+ var n, n2;
+
+ n = AndExpression(t, x);
+ while (t.match(OR)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(AndExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function AndExpression(t, x) {
+ var n, n2;
+
+ n = BitwiseOrExpression(t, x);
+ while (t.match(AND)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(BitwiseOrExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function BitwiseOrExpression(t, x) {
+ var n, n2;
+
+ n = BitwiseXorExpression(t, x);
+ while (t.match(BITWISE_OR)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(BitwiseXorExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function BitwiseXorExpression(t, x) {
+ var n, n2;
+
+ n = BitwiseAndExpression(t, x);
+ while (t.match(BITWISE_XOR)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(BitwiseAndExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function BitwiseAndExpression(t, x) {
+ var n, n2;
+
+ n = EqualityExpression(t, x);
+ while (t.match(BITWISE_AND)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(EqualityExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function EqualityExpression(t, x) {
+ var n, n2;
+
+ n = RelationalExpression(t, x);
+ while (t.match(EQ) || t.match(NE) ||
+ t.match(STRICT_EQ) || t.match(STRICT_NE)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(RelationalExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function RelationalExpression(t, x) {
+ var n, n2;
+
+ /*
+ * Uses of the in operator in shiftExprs are always unambiguous,
+ * so unset the flag that prohibits recognizing it.
+ */
+ var x2 = x.update({ inForLoopInit: false });
+ n = ShiftExpression(t, x2);
+ while ((t.match(LT) || t.match(LE) || t.match(GE) || t.match(GT) ||
+ (!x.inForLoopInit && t.match(IN)) ||
+ t.match(INSTANCEOF))) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(ShiftExpression(t, x2));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function ShiftExpression(t, x) {
+ var n, n2;
+
+ n = AddExpression(t, x);
+ while (t.match(LSH) || t.match(RSH) || t.match(URSH)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(AddExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function AddExpression(t, x) {
+ var n, n2;
+
+ n = MultiplyExpression(t, x);
+ while (t.match(PLUS) || t.match(MINUS)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(MultiplyExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function MultiplyExpression(t, x) {
+ var n, n2;
+
+ n = UnaryExpression(t, x);
+ while (t.match(MUL) || t.match(DIV) || t.match(MOD)) {
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(UnaryExpression(t, x));
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function UnaryExpression(t, x) {
+ var n, n2, tt;
+
+ switch (tt = t.get(true)) {
+ case DELETE: case VOID: case TYPEOF:
+ case NOT: case BITWISE_NOT: case PLUS: case MINUS:
+ if (tt === PLUS)
+ n = new Node(t, { type: UNARY_PLUS });
+ else if (tt === MINUS)
+ n = new Node(t, { type: UNARY_MINUS });
+ else
+ n = new Node(t);
+ n.push(UnaryExpression(t, x));
+ break;
+
+ case INCREMENT:
+ case DECREMENT:
+ // Prefix increment/decrement.
+ n = new Node(t);
+ n.push(MemberExpression(t, x, true));
+ break;
+
+ default:
+ t.unget();
+ n = MemberExpression(t, x, true);
+
+ // Don't look across a newline boundary for a postfix {in,de}crement.
+ if (t.tokens[(t.tokenIndex + t.lookahead - 1) & 3].lineno ===
+ t.lineno) {
+ if (t.match(INCREMENT) || t.match(DECREMENT)) {
+ n2 = new Node(t, { postfix: true });
+ n2.push(n);
+ n = n2;
+ }
+ }
+ break;
+ }
+
+ return n;
+ }
+
+ function MemberExpression(t, x, allowCallSyntax) {
+ var n, n2, name, tt;
+
+ if (t.match(NEW)) {
+ n = new Node(t);
+ n.push(MemberExpression(t, x, false));
+ if (t.match(LEFT_PAREN)) {
+ n.type = NEW_WITH_ARGS;
+ n.push(ArgumentList(t, x));
+ }
+ } else {
+ n = PrimaryExpression(t, x);
+ }
+
+ while ((tt = t.get()) !== END) {
+ switch (tt) {
+ case DOT:
+ n2 = new Node(t);
+ n2.push(n);
+ n2.push(IdentifierName(t));
+ break;
+
+ case LEFT_BRACKET:
+ n2 = new Node(t, { type: INDEX });
+ n2.push(n);
+ n2.push(Expression(t, x));
+ t.mustMatch(RIGHT_BRACKET);
+ break;
+
+ case LEFT_PAREN:
+ if (allowCallSyntax) {
+ n2 = new Node(t, { type: CALL });
+ n2.push(n);
+ n2.push(ArgumentList(t, x));
+ break;
+ }
+
+ // FALL THROUGH
+ default:
+ t.unget();
+ return n;
+ }
+
+ n = n2;
+ }
+
+ return n;
+ }
+
+ function ArgumentList(t, x) {
+ var n, n2;
+
+ n = new Node(t, { type: LIST });
+ if (t.match(RIGHT_PAREN, true))
+ return n;
+ do {
+ n2 = AssignExpression(t, x);
+ if (n2.type === YIELD && !n2.parenthesized && t.peek() === COMMA)
+ throw t.newSyntaxError("Yield expression must be parenthesized");
+ if (t.match(FOR)) {
+ n2 = GeneratorExpression(t, x, n2);
+ if (n.children.length > 1 || t.peek(true) === COMMA)
+ throw t.newSyntaxError("Generator expression must be parenthesized");
+ }
+ n.push(n2);
+ } while (t.match(COMMA));
+ t.mustMatch(RIGHT_PAREN);
+
+ return n;
+ }
+
+ function PrimaryExpression(t, x) {
+ var n, n2, tt = t.get(true);
+
+ switch (tt) {
+ case FUNCTION:
+ n = FunctionDefinition(t, x, false, EXPRESSED_FORM);
+ break;
+
+ case LEFT_BRACKET:
+ n = new Node(t, { type: ARRAY_INIT });
+ while ((tt = t.peek(true)) !== RIGHT_BRACKET) {
+ if (tt === COMMA) {
+ t.get();
+ n.push(null);
+ continue;
+ }
+ n.push(AssignExpression(t, x));
+ if (tt !== COMMA && !t.match(COMMA))
+ break;
+ }
+
+ // If we matched exactly one element and got a FOR, we have an
+ // array comprehension.
+ if (n.children.length === 1 && t.match(FOR)) {
+ n2 = new Node(t, { type: ARRAY_COMP,
+ expression: n.children[0],
+ tail: ComprehensionTail(t, x) });
+ n = n2;
+ }
+ t.mustMatch(RIGHT_BRACKET);
+ break;
+
+ case LEFT_CURLY:
+ var id, fd;
+ n = new Node(t, { type: OBJECT_INIT });
+
+ object_init:
+ if (!t.match(RIGHT_CURLY)) {
+ do {
+ tt = t.get();
+ if ((t.token.value === "get" || t.token.value === "set") &&
+ t.peek() === IDENTIFIER) {
+ if (x.ecma3OnlyMode)
+ throw t.newSyntaxError("Illegal property accessor");
+ n.push(FunctionDefinition(t, x, true, EXPRESSED_FORM));
+ } else {
+ var comments = t.blockComments;
+ switch (tt) {
+ case IDENTIFIER: case NUMBER: case STRING:
+ id = new Node(t, { type: IDENTIFIER });
+ break;
+ case RIGHT_CURLY:
+ if (x.ecma3OnlyMode)
+ throw t.newSyntaxError("Illegal trailing ,");
+ break object_init;
+ default:
+ if (t.token.value in definitions.keywords) {
+ id = new Node(t, { type: IDENTIFIER });
+ break;
+ }
+ throw t.newSyntaxError("Invalid property name");
+ }
+ if (t.match(COLON)) {
+ n2 = new Node(t, { type: PROPERTY_INIT });
+ n2.push(id);
+ n2.push(AssignExpression(t, x));
+ n2.blockComments = comments;
+ n.push(n2);
+ } else {
+ // Support, e.g., |var {x, y} = o| as destructuring shorthand
+ // for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
+ if (t.peek() !== COMMA && t.peek() !== RIGHT_CURLY)
+ throw t.newSyntaxError("missing : after property");
+ n.push(id);
+ }
+ }
+ } while (t.match(COMMA));
+ t.mustMatch(RIGHT_CURLY);
+ }
+ break;
+
+ case LEFT_PAREN:
+ n = ParenExpression(t, x);
+ t.mustMatch(RIGHT_PAREN);
+ n.parenthesized = true;
+ break;
+
+ case LET:
+ n = LetBlock(t, x, false);
+ break;
+
+ case NULL: case THIS: case TRUE: case FALSE:
+ case IDENTIFIER: case NUMBER: case STRING: case REGEXP:
+ n = new Node(t);
+ break;
+
+ default:
+ n = new Node(t);
+ // don't throw an error by default. Just make it a node and forget it :-}
+ //throw t.newSyntaxError("missing operand");
+ break;
+ }
+
+ return n;
+ }
+
+ /*
+ * parse :: (source, filename, line number) -> node
+ */
+ function parse(s, f, l) {
+ var t = new lexer.Tokenizer(s, f, l);
+ var n = Script(t, false, false);
+ if (!t.done)
+ throw t.newSyntaxError("Syntax error");
+
+ return n;
+ }
+
+ /*
+ * parseStdin :: (source, {line number}, string, (string) -> boolean) -> program node
+ */
+ function parseStdin(s, ln, prefix, isCommand) {
+ // the special .begin command is only recognized at the beginning
+ if (s.match(/^[\s]*\.begin[\s]*$/)) {
+ ++ln.value;
+ return parseMultiline(ln, prefix);
+ }
+
+ // commands at the beginning are treated as the entire input
+ if (isCommand(s.trim()))
+ s = "";
+
+ for (;;) {
+ try {
+ var t = new lexer.Tokenizer(s, "stdin", ln.value);
+ var n = Script(t, false, false);
+ ln.value = t.lineno;
+ return n;
+ } catch (e) {
+ if (!t.unexpectedEOF)
+ throw e;
+
+ // commands in the middle are not treated as part of the input
+ var more;
+ do {
+ if (prefix)
+ putstr(prefix);
+ more = readline();
+ if (!more)
+ throw e;
+ } while (isCommand(more.trim()));
+
+ s += "\n" + more;
+ }
+ }
+ }
+
+ /*
+ * parseMultiline :: ({line number}, string | null) -> program node
+ */
+ function parseMultiline(ln, prefix) {
+ var s = "";
+ for (;;) {
+ if (prefix)
+ putstr(prefix);
+ var more = readline();
+ if (more === null)
+ return null;
+ // the only command recognized in multiline mode is .end
+ if (more.match(/^[\s]*\.end[\s]*$/))
+ break;
+ s += "\n" + more;
+ }
+ var t = new lexer.Tokenizer(s, "stdin", ln.value);
+ var n = Script(t, false, false);
+ ln.value = t.lineno;
+ return n;
+ }
+
+ return {
+ parse: parse,
+ parseStdin: parseStdin,
+ Node: Node,
+ SyntheticNode: SyntheticNode,
+ DECLARED_FORM: DECLARED_FORM,
+ EXPRESSED_FORM: EXPRESSED_FORM,
+ STATEMENT_FORM: STATEMENT_FORM,
+ Tokenizer: lexer.Tokenizer,
+ FunctionDefinition: FunctionDefinition,
+ Module: Module,
+ Export: Export
+ };
+
+}());
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/parse.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/parse.js
new file mode 100644
index 0000000..07c800e
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser/parse.js
@@ -0,0 +1,57 @@
+/**
+ * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
+ * *
+ * Copyright (C) 2011, 2012, 2014 Loic J. Duros
+ * Copyright (C) 2014, 2015 Nik Nyby
+ *
+ * This file is part of GNU LibreJS.
+ *
+ * GNU LibreJS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNU LibreJS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU LibreJS. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * parse.js
+ *
+ * A chrome worker that we keep working for the duration
+ * of the session.
+ *
+ * It takes about 10-12M in memory when not actively running, and it
+ * is much faster than reloading the Narcissus scripts for every
+ * script.
+ *
+ */
+
+importScripts('./jsdefs.js', './jslex.js', './jsparse.js');
+
+self.onmessage = function(event) {
+ if (event.data === 'stop') {
+ // destroy chrome worker.
+ self.close();
+ return;
+ }
+
+ var obj = JSON.parse(event.data);
+ var tree;
+
+ try {
+ tree = new Narcissus.parser.parse(obj.code);
+ self.postMessage({'tree': tree, 'hash': obj.hash});
+ } catch (x) {
+ // error with parsing. Delete all.
+ self.postMessage({'hash': obj.hash});
+ }
+
+ tree = null;
+ obj = null;
+};