diff options
Diffstat (limited to 'data/extensions/jid1-KtlZuoiikVfFew@jetpack/data/chrome_worker/parser')
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; +}; |