/* 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 . * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Tom Austin * Brendan Eich * Shu-Yu Guo * Dave Herman * Dimitris Vardoulakis * Patrick Walton * * 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. */ (function() { var narcissus = { options: { version: 185, }, 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", "false", "finally", "for", "function", "if", "in", "instanceof", "let", "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", ]; // 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 = "const "; 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 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\]/); } function getPropertyDescriptor(obj, name) { while (obj) { if (({}).hasOwnProperty.call(obj, name)) return Object.getOwnPropertyDescriptor(obj, name); obj = Object.getPrototypeOf(obj); } } function getOwnProperties(obj) { var map = {}; for (var name in Object.getOwnPropertyNames(obj)) map[name] = Object.getOwnPropertyDescriptor(obj, name); return map; } 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); } }; } // default function used when looking for a property in the global object function noPropFound() { return undefined; } var hasOwnProperty = ({}).hasOwnProperty; function StringMap() { this.table = Object.create(null, {}); this.size = 0; } 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]); }, toString: function() { return "[object StringMap]" } }; // 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); } } }; return { tokens: tokens, opTypeNames: opTypeNames, keywords: keywords, isStatementStartCode: isStatementStartCode, tokenIds: tokenIds, consts: consts, assignOps: assignOps, defineGetter: defineGetter, defineProperty: defineProperty, isNativeCode: isNativeCode, makePassthruHandler: makePassthruHandler, noPropFound: noPropFound, StringMap: StringMap, Stack: Stack }; }());