Merge tracemonkey to mozilla-central. (CLOSED TREE)
authorRobert Sayre <sayrer@gmail.com>
Wed, 04 Aug 2010 00:47:26 -0700
changeset 48823 c761f8e85b8c890441f16d4f92c2c4bbbfb19f55
parent 48797 79aa28daf1f469a02207c26257cbe7ba4c23e184 (current diff)
parent 48822 e866db9165efd9fcad476ef3f2a2d33ed968db8b (diff)
child 48824 2f187db8f5f62b36c0ea2a9129530bf710312989
push id14825
push userrsayre@mozilla.com
push dateWed, 04 Aug 2010 07:47:43 +0000
treeherdermozilla-central@c761f8e85b8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b3pre
first release with
nightly linux32
c761f8e85b8c / 4.0b3pre / 20100804031526 / files
nightly linux64
c761f8e85b8c / 4.0b3pre / 20100804030858 / files
nightly mac
c761f8e85b8c / 4.0b3pre / 20100804031237 / files
nightly win32
c761f8e85b8c / 4.0b3pre / 20100804040859 / files
nightly win64
c761f8e85b8c / 4.0b3pre / 20100804042520 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge tracemonkey to mozilla-central. (CLOSED TREE)
js/narcissus/js.js
js/src/ctypes/CTypes.cpp
js/src/njs
deleted file mode 100644
--- a/js/narcissus/js.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- Mode: JS; tab-width: 4; indent-tabs-mode: nil; -*-
- * vim: set sw=4 ts=8 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):
- *
- * 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.
- *
- * Native objects and classes implemented metacircularly:
- *      the global object (singleton)
- *      eval
- *      function objects, Function
- *
- * SpiderMonkey extensions used:
- *      catch guards
- *      const declarations
- *      get and set functions in object initialisers
- *      Object.prototype.__proto__
- *      filename and line number arguments to *Error constructors
- *      callable regular expression objects
- *
- * SpiderMonkey extensions supported metacircularly:
- *      catch guards
- *      const declarations
- *      get and set functions in object initialisers
- */
-
-load('jsdefs.js');
-load('jslex.js');
-load('jsparse.js');
-load('jsexec.js');
-
--- a/js/narcissus/jsdefs.js
+++ b/js/narcissus/jsdefs.js
@@ -1,8 +1,9 @@
+/* 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/
  *
@@ -37,144 +38,162 @@
 /*
  * 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.
  */
-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",
-];
+Narcissus = {};
+Narcissus.jsdefs = (function() {
 
-// 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"
-};
+    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",
+    ];
+    
+    // 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 += ";";
+    
+    // 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 });
+    }
+    
+    return {
+      "tokens": tokens,
+      "opTypeNames": opTypeNames,
+      "keywords": keywords,
+      "tokenIds": tokenIds,
+      "consts": consts,
+      "assignOps": assignOps,
+      "defineGetter": defineGetter,
+      "defineProperty": defineProperty
+    };
+}());
 
-// 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 = {};
-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;
-}
-eval(consts + ";");
-
-// 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 });
-}
--- a/js/narcissus/jsexec.js
+++ b/js/narcissus/jsexec.js
@@ -43,997 +43,1012 @@
  * Execution of parse trees.
  *
  * Standard classes except for eval, Function, Array, and String are borrowed
  * from the host JS environment.  Function is metacircular.  Array and String
  * are reflected via wrapping the corresponding native constructor and adding
  * an extra level of prototype-based delegation.
  */
 
-const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2;
-
-function ExecutionContext(type) {
-    this.type = type;
-}
+Narcissus.jsexec = (function() {
 
-var global = {
-    // Value properties.
-    NaN: NaN, Infinity: Infinity, undefined: undefined,
-
-    // Function properties.
-    eval: function eval(s) {
+    var jsparse = Narcissus.jsparse;
+    var jsdefs = Narcissus.jsdefs;
+    
+    // Set constants in the local scope.
+    eval(jsdefs.consts);
+    
+    const GLOBAL_CODE = 0, EVAL_CODE = 1, FUNCTION_CODE = 2;
+    
+    function ExecutionContext(type) {
+        this.type = type;
+    }
+    
+    var global = {
+        // Value properties.
+        NaN: NaN, Infinity: Infinity, undefined: undefined,
+    
+        // Function properties.
+        eval: function eval(s) {
+            if (typeof s != "string")
+                return s;
+    
+            var x = ExecutionContext.current;
+            var x2 = new ExecutionContext(EVAL_CODE);
+            x2.thisObject = x.thisObject;
+            x2.caller = x.caller;
+            x2.callee = x.callee;
+            x2.scope = x.scope;
+            ExecutionContext.current = x2;
+            try {
+                execute(jsparse.parse(new jsparse.VanillaBuilder, s), x2);
+            } catch (e if e == THROW) {
+                x.result = x2.result;
+                throw e;
+            } catch (e if e instanceof SyntaxError) {
+                x.result = e;
+                throw THROW;
+            } catch (e if e instanceof InternalError) {
+                /*
+                 * If we get too much recursion during parsing we need to re-throw
+                 * it as a narcissus THROW.
+                 *
+                 * See bug 152646.
+                 */
+                var re = /InternalError: (script stack space quota is exhausted|too much recursion)/;
+                if (re.test(e.toString())) {
+                    x.result = e;
+                    throw THROW;
+                } else {
+                    throw e;
+                }
+            } finally {
+                ExecutionContext.current = x;
+            }
+            return x2.result;
+        },
+        parseInt: parseInt, parseFloat: parseFloat,
+        isNaN: isNaN, isFinite: isFinite,
+        decodeURI: decodeURI, encodeURI: encodeURI,
+        decodeURIComponent: decodeURIComponent,
+        encodeURIComponent: encodeURIComponent,
+    
+        // Class constructors.  Where ECMA-262 requires C.length == 1, we declare
+        // a dummy formal parameter.
+        Object: Object,
+        Function: function Function(dummy) {
+            var p = "", b = "", n = arguments.length;
+            if (n) {
+                var m = n - 1;
+                if (m) {
+                    p += arguments[0];
+                    for (var k = 1; k < m; k++)
+                        p += "," + arguments[k];
+                }
+                b += arguments[m];
+            }
+    
+            // XXX We want to pass a good file and line to the tokenizer.
+            // Note the anonymous name to maintain parity with Spidermonkey.
+            var t = new jsparse.Tokenizer("anonymous(" + p + ") {" + b + "}");
+    
+            // NB: Use the STATEMENT_FORM constant since we don't want to push this
+            // function onto the fake compilation context.
+            var x = { builder: new jsparse.VanillaBuilder };
+            var f = jsparse.FunctionDefinition(t, x, false, jsparse.STATEMENT_FORM);
+            var s = {object: global, parent: null};
+            return newFunction(f,{scope:s});
+        },
+        Array: function (dummy) {
+            // Array when called as a function acts as a constructor.
+            return Array.apply(this, arguments);
+        },
+        String: function String(s) {
+            // Called as function or constructor: convert argument to string type.
+            s = arguments.length ? "" + s : "";
+            if (this instanceof String) {
+                // Called as constructor: save the argument as the string value
+                // of this String object and return this object.
+                this.value = s;
+                return this;
+            }
+            return s;
+        },
+        Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp,
+        Error: Error, EvalError: EvalError, RangeError: RangeError,
+        ReferenceError: ReferenceError, SyntaxError: SyntaxError,
+        TypeError: TypeError, URIError: URIError,
+    
+        // Other properties.
+        Math: Math,
+    
+        // Extensions to ECMA.
+        snarf: snarf, evaluate: evaluate,
+        load: function load(s) {
+            if (typeof s != "string")
+                return s;
+    
+            evaluate(snarf(s), s, 1)
+        },
+        print: print,
+        version: function() { return 185; }
+    };
+    
+    // Helper to avoid Object.prototype.hasOwnProperty polluting scope objects.
+    function hasDirectProperty(o, p) {
+        return Object.prototype.hasOwnProperty.call(o, p);
+    }
+    
+    // Reflect a host class into the target global environment by delegation.
+    function reflectClass(name, proto) {
+        var gctor = global[name];
+        jsdefs.defineProperty(gctor, "prototype", proto, true, true, true);
+        jsdefs.defineProperty(proto, "constructor", gctor, false, false, true);
+        return proto;
+    }
+    
+    // Reflect Array -- note that all Array methods are generic.
+    reflectClass('Array', new Array);
+    
+    // Reflect String, overriding non-generic methods.
+    var gSp = reflectClass('String', new String);
+    gSp.toSource = function () { return this.value.toSource(); };
+    gSp.toString = function () { return this.value; };
+    gSp.valueOf  = function () { return this.value; };
+    global.String.fromCharCode = String.fromCharCode;
+    
+    ExecutionContext.current = null;
+    
+    ExecutionContext.prototype = {
+        caller: null,
+        callee: null,
+        scope: {object: global, parent: null},
+        thisObject: global,
+        result: undefined,
+        target: null,
+        ecma3OnlyMode: false
+    };
+    
+    function Reference(base, propertyName, node) {
+        this.base = base;
+        this.propertyName = propertyName;
+        this.node = node;
+    }
+    
+    Reference.prototype.toString = function () { return this.node.getSource(); }
+    
+    function getValue(v) {
+        if (v instanceof Reference) {
+            if (!v.base) {
+                throw new ReferenceError(v.propertyName + " is not defined",
+                                         v.node.filename, v.node.lineno);
+            }
+            return v.base[v.propertyName];
+        }
+        return v;
+    }
+    
+    function putValue(v, w, vn) {
+        if (v instanceof Reference)
+            return (v.base || global)[v.propertyName] = w;
+        throw new ReferenceError("Invalid assignment left-hand side",
+                                 vn.filename, vn.lineno);
+    }
+    
+    function isPrimitive(v) {
+        var t = typeof v;
+        return (t == "object") ? v === null : t != "function";
+    }
+    
+    function isObject(v) {
+        var t = typeof v;
+        return (t == "object") ? v !== null : t == "function";
+    }
+    
+    // If r instanceof Reference, v == getValue(r); else v === r.  If passed, rn
+    // is the node whose execute result was r.
+    function toObject(v, r, rn) {
+        switch (typeof v) {
+          case "boolean":
+            return new global.Boolean(v);
+          case "number":
+            return new global.Number(v);
+          case "string":
+            return new global.String(v);
+          case "function":
+            return v;
+          case "object":
+            if (v !== null)
+                return v;
+        }
+        var message = r + " (type " + (typeof v) + ") has no properties";
+        throw rn ? new TypeError(message, rn.filename, rn.lineno)
+                 : new TypeError(message);
+    }
+    
+    function execute(n, x) {
+        var a, f, i, j, r, s, t, u, v;
+    
+        switch (n.type) {
+          case FUNCTION:
+            if (n.functionForm != jsparse.DECLARED_FORM) {
+                if (!n.name || n.functionForm == jsparse.STATEMENT_FORM) {
+                    v = newFunction(n, x);
+                    if (n.functionForm == jsparse.STATEMENT_FORM)
+                        jsdefs.defineProperty(x.scope.object, n.name, v, true);
+                } else {
+                    t = new Object;
+                    x.scope = {object: t, parent: x.scope};
+                    try {
+                        v = newFunction(n, x);
+                        jsdefs.defineProperty(t, n.name, v, true, true);
+                    } finally {
+                        x.scope = x.scope.parent;
+                    }
+                }
+            }
+            break;
+    
+          case SCRIPT:
+            t = x.scope.object;
+            a = n.funDecls;
+            for (i = 0, j = a.length; i < j; i++) {
+                s = a[i].name;
+                f = newFunction(a[i], x);
+                jsdefs.defineProperty(t, s, f, x.type != EVAL_CODE);
+            }
+            a = n.varDecls;
+            for (i = 0, j = a.length; i < j; i++) {
+                u = a[i];
+                s = u.name;
+                if (u.readOnly && hasDirectProperty(t, s)) {
+                    throw new TypeError("Redeclaration of const " + s,
+                                        u.filename, u.lineno);
+                }
+                if (u.readOnly || !hasDirectProperty(t, s)) {
+                    jsdefs.defineProperty(t, s, undefined, x.type != EVAL_CODE, u.readOnly);
+                }
+            }
+            // FALL THROUGH
+    
+          case BLOCK:
+            for (i = 0, j = n.length; i < j; i++)
+                execute(n[i], x);
+            break;
+    
+          case IF:
+            if (getValue(execute(n.condition, x)))
+                execute(n.thenPart, x);
+            else if (n.elsePart)
+                execute(n.elsePart, x);
+            break;
+    
+          case SWITCH:
+            s = getValue(execute(n.discriminant, x));
+            a = n.cases;
+            var matchDefault = false;
+          switch_loop:
+            for (i = 0, j = a.length; ; i++) {
+                if (i == j) {
+                    if (n.defaultIndex >= 0) {
+                        i = n.defaultIndex - 1; // no case matched, do default
+                        matchDefault = true;
+                        continue;
+                    }
+                    break;                      // no default, exit switch_loop
+                }
+                t = a[i];                       // next case (might be default!)
+                if (t.type == CASE) {
+                    u = getValue(execute(t.caseLabel, x));
+                } else {
+                    if (!matchDefault)          // not defaulting, skip for now
+                        continue;
+                    u = s;                      // force match to do default
+                }
+                if (u === s) {
+                    for (;;) {                  // this loop exits switch_loop
+                        if (t.statements.length) {
+                            try {
+                                execute(t.statements, x);
+                            } catch (e if e == BREAK && x.target == n) {
+                                break switch_loop;
+                            }
+                        }
+                        if (++i == j)
+                            break switch_loop;
+                        t = a[i];
+                    }
+                    // NOT REACHED
+                }
+            }
+            break;
+    
+          case FOR:
+            n.setup && getValue(execute(n.setup, x));
+            // FALL THROUGH
+          case WHILE:
+            while (!n.condition || getValue(execute(n.condition, x))) {
+                try {
+                    execute(n.body, x);
+                } catch (e if e == BREAK && x.target == n) {
+                    break;
+                } catch (e if e == CONTINUE && x.target == n) {
+                    // Must run the update expression.
+                }
+                n.update && getValue(execute(n.update, x));
+            }
+            break;
+    
+          case FOR_IN:
+            u = n.varDecl;
+            if (u)
+                execute(u, x);
+            r = n.iterator;
+            s = execute(n.object, x);
+            v = getValue(s);
+    
+            // ECMA deviation to track extant browser JS implementation behavior.
+            t = (v == null && !x.ecma3OnlyMode) ? v : toObject(v, s, n.object);
+            a = [];
+            for (i in t)
+                a.push(i);
+            for (i = 0, j = a.length; i < j; i++) {
+                putValue(execute(r, x), a[i], r);
+                try {
+                    execute(n.body, x);
+                } catch (e if e == BREAK && x.target == n) {
+                    break;
+                } catch (e if e == CONTINUE && x.target == n) {
+                    continue;
+                }
+            }
+            break;
+    
+          case DO:
+            do {
+                try {
+                    execute(n.body, x);
+                } catch (e if e == BREAK && x.target == n) {
+                    break;
+                } catch (e if e == CONTINUE && x.target == n) {
+                    continue;
+                }
+            } while (getValue(execute(n.condition, x)));
+            break;
+    
+          case BREAK:
+          case CONTINUE:
+            x.target = n.target;
+            throw n.type;
+    
+          case TRY:
+            try {
+                execute(n.tryBlock, x);
+            } catch (e if e == THROW && (j = n.catchClauses.length)) {
+                e = x.result;
+                x.result = undefined;
+                for (i = 0; ; i++) {
+                    if (i == j) {
+                        x.result = e;
+                        throw THROW;
+                    }
+                    t = n.catchClauses[i];
+                    x.scope = {object: {}, parent: x.scope};
+                    jsdefs.defineProperty(x.scope.object, t.varName, e, true);
+                    try {
+                        if (t.guard && !getValue(execute(t.guard, x)))
+                            continue;
+                        execute(t.block, x);
+                        break;
+                    } finally {
+                        x.scope = x.scope.parent;
+                    }
+                }
+            } finally {
+                if (n.finallyBlock)
+                    execute(n.finallyBlock, x);
+            }
+            break;
+    
+          case THROW:
+            x.result = getValue(execute(n.exception, x));
+            throw THROW;
+    
+          case RETURN:
+            x.result = getValue(execute(n.value, x));
+            throw RETURN;
+    
+          case WITH:
+            r = execute(n.object, x);
+            t = toObject(getValue(r), r, n.object);
+            x.scope = {object: t, parent: x.scope};
+            try {
+                execute(n.body, x);
+            } finally {
+                x.scope = x.scope.parent;
+            }
+            break;
+    
+          case VAR:
+          case CONST:
+            for (i = 0, j = n.length; i < j; i++) {
+                u = n[i].initializer;
+                if (!u)
+                    continue;
+                t = n[i].name;
+                for (s = x.scope; s; s = s.parent) {
+                    if (hasDirectProperty(s.object, t))
+                        break;
+                }
+                u = getValue(execute(u, x));
+                if (n.type == CONST)
+                    jsdefs.defineProperty(s.object, t, u, x.type != EVAL_CODE, true);
+                else
+                    s.object[t] = u;
+            }
+            break;
+    
+          case DEBUGGER:
+            throw "NYI: " + jsdefs.tokens[n.type];
+    
+          case SEMICOLON:
+            if (n.expression)
+                x.result = getValue(execute(n.expression, x));
+            break;
+    
+          case LABEL:
+            try {
+                execute(n.statement, x);
+            } catch (e if e == BREAK && x.target == n) {
+            }
+            break;
+    
+          case COMMA:
+            for (i = 0, j = n.length; i < j; i++)
+                v = getValue(execute(n[i], x));
+            break;
+    
+          case ASSIGN:
+            r = execute(n[0], x);
+            t = n.assignOp;
+            if (t)
+                u = getValue(r);
+            v = getValue(execute(n[1], x));
+            if (t) {
+                switch (t) {
+                  case BITWISE_OR:  v = u | v; break;
+                  case BITWISE_XOR: v = u ^ v; break;
+                  case BITWISE_AND: v = u & v; break;
+                  case LSH:         v = u << v; break;
+                  case RSH:         v = u >> v; break;
+                  case URSH:        v = u >>> v; break;
+                  case PLUS:        v = u + v; break;
+                  case MINUS:       v = u - v; break;
+                  case MUL:         v = u * v; break;
+                  case DIV:         v = u / v; break;
+                  case MOD:         v = u % v; break;
+                }
+            }
+            putValue(r, v, n[0]);
+            break;
+    
+          case HOOK:
+            v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x))
+                                           : getValue(execute(n[2], x));
+            break;
+    
+          case OR:
+            v = getValue(execute(n[0], x)) || getValue(execute(n[1], x));
+            break;
+    
+          case AND:
+            v = getValue(execute(n[0], x)) && getValue(execute(n[1], x));
+            break;
+    
+          case BITWISE_OR:
+            v = getValue(execute(n[0], x)) | getValue(execute(n[1], x));
+            break;
+    
+          case BITWISE_XOR:
+            v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x));
+            break;
+    
+          case BITWISE_AND:
+            v = getValue(execute(n[0], x)) & getValue(execute(n[1], x));
+            break;
+    
+          case EQ:
+            v = getValue(execute(n[0], x)) == getValue(execute(n[1], x));
+            break;
+    
+          case NE:
+            v = getValue(execute(n[0], x)) != getValue(execute(n[1], x));
+            break;
+    
+          case STRICT_EQ:
+            v = getValue(execute(n[0], x)) === getValue(execute(n[1], x));
+            break;
+    
+          case STRICT_NE:
+            v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x));
+            break;
+    
+          case LT:
+            v = getValue(execute(n[0], x)) < getValue(execute(n[1], x));
+            break;
+    
+          case LE:
+            v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x));
+            break;
+    
+          case GE:
+            v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x));
+            break;
+    
+          case GT:
+            v = getValue(execute(n[0], x)) > getValue(execute(n[1], x));
+            break;
+    
+          case IN:
+            v = getValue(execute(n[0], x)) in getValue(execute(n[1], x));
+            break;
+    
+          case INSTANCEOF:
+            t = getValue(execute(n[0], x));
+            u = getValue(execute(n[1], x));
+            if (isObject(u) && typeof u.__hasInstance__ == "function")
+                v = u.__hasInstance__(t);
+            else
+                v = t instanceof u;
+            break;
+    
+          case LSH:
+            v = getValue(execute(n[0], x)) << getValue(execute(n[1], x));
+            break;
+    
+          case RSH:
+            v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x));
+            break;
+    
+          case URSH:
+            v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x));
+            break;
+    
+          case PLUS:
+            v = getValue(execute(n[0], x)) + getValue(execute(n[1], x));
+            break;
+    
+          case MINUS:
+            v = getValue(execute(n[0], x)) - getValue(execute(n[1], x));
+            break;
+    
+          case MUL:
+            v = getValue(execute(n[0], x)) * getValue(execute(n[1], x));
+            break;
+    
+          case DIV:
+            v = getValue(execute(n[0], x)) / getValue(execute(n[1], x));
+            break;
+    
+          case MOD:
+            v = getValue(execute(n[0], x)) % getValue(execute(n[1], x));
+            break;
+    
+          case DELETE:
+            t = execute(n[0], x);
+            v = !(t instanceof Reference) || delete t.base[t.propertyName];
+            break;
+    
+          case VOID:
+            getValue(execute(n[0], x));
+            break;
+    
+          case TYPEOF:
+            t = execute(n[0], x);
+            if (t instanceof Reference)
+                t = t.base ? t.base[t.propertyName] : undefined;
+            v = typeof t;
+            break;
+    
+          case NOT:
+            v = !getValue(execute(n[0], x));
+            break;
+    
+          case BITWISE_NOT:
+            v = ~getValue(execute(n[0], x));
+            break;
+    
+          case UNARY_PLUS:
+            v = +getValue(execute(n[0], x));
+            break;
+    
+          case UNARY_MINUS:
+            v = -getValue(execute(n[0], x));
+            break;
+    
+          case INCREMENT:
+          case DECREMENT:
+            t = execute(n[0], x);
+            u = Number(getValue(t));
+            if (n.postfix)
+                v = u;
+            putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]);
+            if (!n.postfix)
+                v = u;
+            break;
+    
+          case DOT:
+            r = execute(n[0], x);
+            t = getValue(r);
+            u = n[1].value;
+            v = new Reference(toObject(t, r, n[0]), u, n);
+            break;
+    
+          case INDEX:
+            r = execute(n[0], x);
+            t = getValue(r);
+            u = getValue(execute(n[1], x));
+            v = new Reference(toObject(t, r, n[0]), String(u), n);
+            break;
+    
+          case LIST:
+            // Curse ECMA for specifying that arguments is not an Array object!
+            v = {};
+            for (i = 0, j = n.length; i < j; i++) {
+                u = getValue(execute(n[i], x));
+                jsdefs.defineProperty(v, i, u, false, false, true);
+            }
+            jsdefs.defineProperty(v, "length", i, false, false, true);
+            break;
+    
+          case CALL:
+            r = execute(n[0], x);
+            a = execute(n[1], x);
+            f = getValue(r);
+            if (isPrimitive(f) || typeof f.__call__ != "function") {
+                throw new TypeError(r + " is not callable",
+                                    n[0].filename, n[0].lineno);
+            }
+            t = (r instanceof Reference) ? r.base : null;
+            if (t instanceof Activation)
+                t = null;
+            v = f.__call__(t, a, x);
+            break;
+    
+          case NEW:
+          case NEW_WITH_ARGS:
+            r = execute(n[0], x);
+            f = getValue(r);
+            if (n.type == NEW) {
+                a = {};
+                jsdefs.defineProperty(a, "length", 0, false, false, true);
+            } else {
+                a = execute(n[1], x);
+            }
+            if (isPrimitive(f) || typeof f.__construct__ != "function") {
+                throw new TypeError(r + " is not a constructor",
+                                    n[0].filename, n[0].lineno);
+            }
+            v = f.__construct__(a, x);
+            break;
+    
+          case ARRAY_INIT:
+            v = [];
+            for (i = 0, j = n.length; i < j; i++) {
+                if (n[i])
+                    v[i] = getValue(execute(n[i], x));
+            }
+            v.length = j;
+            break;
+    
+          case OBJECT_INIT:
+            v = {};
+            for (i = 0, j = n.length; i < j; i++) {
+                t = n[i];
+                if (t.type == PROPERTY_INIT) {
+                    v[t[0].value] = getValue(execute(t[1], x));
+                } else {
+                    f = newFunction(t, x);
+                    u = (t.type == GETTER) ? '__defineGetter__'
+                                           : '__defineSetter__';
+                    v[u](t.name, thunk(f, x));
+                }
+            }
+            break;
+    
+          case NULL:
+            v = null;
+            break;
+    
+          case THIS:
+            v = x.thisObject;
+            break;
+    
+          case TRUE:
+            v = true;
+            break;
+    
+          case FALSE:
+            v = false;
+            break;
+    
+          case IDENTIFIER:
+            for (s = x.scope; s; s = s.parent) {
+                if (n.value in s.object)
+                    break;
+            }
+            v = new Reference(s && s.object, n.value, n);
+            break;
+    
+          case NUMBER:
+          case STRING:
+          case REGEXP:
+            v = n.value;
+            break;
+    
+          case GROUP:
+            v = execute(n[0], x);
+            break;
+    
+          default:
+            throw "PANIC: unknown operation " + n.type + ": " + uneval(n);
+        }
+    
+        return v;
+    }
+    
+    function Activation(f, a) {
+        for (var i = 0, j = f.params.length; i < j; i++)
+            jsdefs.defineProperty(this, f.params[i], a[i], true);
+        jsdefs.defineProperty(this, "arguments", a, true);
+    }
+    
+    // Null Activation.prototype's proto slot so that Object.prototype.* does not
+    // pollute the scope of heavyweight functions.  Also delete its 'constructor'
+    // property so that it doesn't pollute function scopes.
+    
+    Activation.prototype.__proto__ = null;
+    delete Activation.prototype.constructor;
+    
+    function FunctionObject(node, scope) {
+        this.node = node;
+        this.scope = scope;
+        jsdefs.defineProperty(this, "length", node.params.length, true, true, true);
+        var proto = {};
+        jsdefs.defineProperty(this, "prototype", proto, true);
+        jsdefs.defineProperty(proto, "constructor", this, false, false, true);
+    }
+    
+    // Returns a new function wrapped with a Proxy.
+    function newFunction(n,x) {
+        var f = new FunctionObject(n, x.scope);
+        var p = Proxy.createFunction(
+    
+                // Handler function copied from
+                //  http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
+                function(obj) { return {
+                    getOwnPropertyDescriptor: function(name) {
+                        var desc = Object.getOwnPropertyDescriptor(obj);
+    
+                        // a trapping proxy's properties must always be configurable
+                        desc.configurable = true;
+                        return desc;
+                     },
+                    getPropertyDescriptor: function(name) {
+                        var desc = Object.getPropertyDescriptor(obj); //assumed
+    
+                        // 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 Object.getOwnProperties(obj); // assumed
+                        }
+    
+                        // 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;
+                    },
+                    enumerateOwn: function() { return Object.keys(obj); } };
+                }(f),
+                function() { return f.__call__(this, arguments, x); },
+                function() { return f.__construct__(arguments, x); });
+        return p;
+    }
+    
+    var FOp = FunctionObject.prototype = {
+    
+        // Internal methods.
+        __call__: function (t, a, x) {
+            var x2 = new ExecutionContext(FUNCTION_CODE);
+            x2.thisObject = t || global;
+            x2.caller = x;
+            x2.callee = this;
+            jsdefs.defineProperty(a, "callee", this, false, false, true);
+            var f = this.node;
+            x2.scope = {object: new Activation(f, a), parent: this.scope};
+    
+            ExecutionContext.current = x2;
+            try {
+                execute(f.body, x2);
+            } catch (e if e == RETURN) {
+                return x2.result;
+            } catch (e if e == THROW) {
+                x.result = x2.result;
+                throw THROW;
+            } finally {
+                ExecutionContext.current = x;
+            }
+            return undefined;
+        },
+    
+        __construct__: function (a, x) {
+            var o = new Object;
+            var p = this.prototype;
+            if (isObject(p))
+                o.__proto__ = p;
+            // else o.__proto__ defaulted to Object.prototype
+    
+            var v = this.__call__(o, a, x);
+            if (isObject(v))
+                return v;
+            return o;
+        },
+    
+        __hasInstance__: function (v) {
+            if (isPrimitive(v))
+                return false;
+            var p = this.prototype;
+            if (isPrimitive(p)) {
+                throw new TypeError("'prototype' property is not an object",
+                                    this.node.filename, this.node.lineno);
+            }
+            var o;
+            while ((o = v.__proto__)) {
+                if (o == p)
+                    return true;
+                v = o;
+            }
+            return false;
+        },
+    
+        // Standard methods.
+        toString: function () {
+            return this.node.getSource();
+        },
+    
+        apply: function (t, a) {
+            // Curse ECMA again!
+            if (typeof this.__call__ != "function") {
+                throw new TypeError("Function.prototype.apply called on" +
+                                    " uncallable object");
+            }
+    
+            if (t === undefined || t === null)
+                t = global;
+            else if (typeof t != "object")
+                t = toObject(t, t);
+    
+            if (a === undefined || a === null) {
+                a = {};
+                jsdefs.defineProperty(a, "length", 0, false, false, true);
+            } else if (a instanceof Array) {
+                var v = {};
+                for (var i = 0, j = a.length; i < j; i++)
+                    jsdefs.defineProperty(v, i, a[i], false, false, true);
+                jsdefs.defineProperty(v, "length", i, false, false, true);
+                a = v;
+            } else if (!(a instanceof Object)) {
+                // XXX check for a non-arguments object
+                throw new TypeError("Second argument to Function.prototype.apply" +
+                                    " must be an array or arguments object",
+                                    this.node.filename, this.node.lineno);
+            }
+    
+            return this.__call__(t, a, ExecutionContext.current);
+        },
+    
+        call: function (t) {
+            // Curse ECMA a third time!
+            var a = Array.prototype.splice.call(arguments, 1);
+            return this.apply(t, a);
+        }
+    };
+    
+    // Connect Function.prototype and Function.prototype.constructor in global.
+    reflectClass('Function', FOp);
+    
+    // Help native and host-scripted functions be like FunctionObjects.
+    var Fp = Function.prototype;
+    var REp = RegExp.prototype;
+    
+    if (!('__call__' in Fp)) {
+        jsdefs.defineProperty(Fp, "__call__",
+                       function (t, a, x) {
+                           // Curse ECMA yet again!
+                           a = Array.prototype.splice.call(a, 0, a.length);
+                           return this.apply(t, a);
+                       }, true, true, true);
+        jsdefs.defineProperty(REp, "__call__",
+                       function (t, a, x) {
+                           a = Array.prototype.splice.call(a, 0, a.length);
+                           return this.exec.apply(this, a);
+                       }, true, true, true);
+        jsdefs.defineProperty(Fp, "__construct__",
+                       function (a, x) {
+                           a = Array.prototype.splice.call(a, 0, a.length);
+                           switch (a.length) {
+                             case 0:
+                               return new this();
+                             case 1:
+                               return new this(a[0]);
+                             case 2:
+                               return new this(a[0], a[1]);
+                             case 3:
+                               return new this(a[0], a[1], a[2]);
+                             default:
+                               var argStr = "";
+                               for (var i=0; i<a.length; i++) {
+                                   argStr += 'a[' + i + '],';
+                               }
+                               return eval('new this(' + argStr.slice(0,-1) + ');');
+                           }
+                       }, true, true, true);
+    
+        // Since we use native functions such as Date along with host ones such
+        // as global.eval, we want both to be considered instances of the native
+        // Function constructor.
+        jsdefs.defineProperty(Fp, "__hasInstance__",
+                       function (v) {
+                           return v instanceof Function || v instanceof global.Function;
+                       }, true, true, true);
+    }
+    
+    function thunk(f, x) {
+        return function () { return f.__call__(this, arguments, x); };
+    }
+    
+    function evaluate(s, f, l) {
         if (typeof s != "string")
             return s;
-
+    
         var x = ExecutionContext.current;
-        var x2 = new ExecutionContext(EVAL_CODE);
-        x2.thisObject = x.thisObject;
-        x2.caller = x.caller;
-        x2.callee = x.callee;
-        x2.scope = x.scope;
+        var x2 = new ExecutionContext(GLOBAL_CODE);
         ExecutionContext.current = x2;
         try {
-            execute(parse(new VanillaBuilder, s), x2);
+            execute(jsparse.parse(new jsparse.VanillaBuilder, s, f, l), x2);
         } catch (e if e == THROW) {
-            x.result = x2.result;
-            throw e;
-        } catch (e if e instanceof SyntaxError) {
-            x.result = e;
-            throw THROW;
-        } catch (e if e instanceof InternalError) {
-            /*
-             * If we get too much recursion during parsing we need to re-throw
-             * it as a narcissus THROW.
-             *
-             * See bug 152646.
-             */
-            var re = /InternalError: (script stack space quota is exhausted|too much recursion)/;
-            if (re.test(e.toString())) {
-                x.result = e;
+            if (x) {
+                x.result = x2.result;
                 throw THROW;
-            } else {
-                throw e;
             }
+            throw x2.result;
         } finally {
             ExecutionContext.current = x;
         }
         return x2.result;
-    },
-    parseInt: parseInt, parseFloat: parseFloat,
-    isNaN: isNaN, isFinite: isFinite,
-    decodeURI: decodeURI, encodeURI: encodeURI,
-    decodeURIComponent: decodeURIComponent,
-    encodeURIComponent: encodeURIComponent,
-
-    // Class constructors.  Where ECMA-262 requires C.length == 1, we declare
-    // a dummy formal parameter.
-    Object: Object,
-    Function: function Function(dummy) {
-        var p = "", b = "", n = arguments.length;
-        if (n) {
-            var m = n - 1;
-            if (m) {
-                p += arguments[0];
-                for (var k = 1; k < m; k++)
-                    p += "," + arguments[k];
-            }
-            b += arguments[m];
-        }
-
-        // XXX We want to pass a good file and line to the tokenizer.
-        // Note the anonymous name to maintain parity with Spidermonkey.
-        var t = new Tokenizer("anonymous(" + p + ") {" + b + "}");
-
-        // NB: Use the STATEMENT_FORM constant since we don't want to push this
-        // function onto the fake compilation context.
-        var x = { builder: new VanillaBuilder };
-        var f = FunctionDefinition(t, x, false, STATEMENT_FORM);
-        var s = {object: global, parent: null};
-        return newFunction(f,{scope:s});
-    },
-    Array: function (dummy) {
-        // Array when called as a function acts as a constructor.
-        return Array.apply(this, arguments);
-    },
-    String: function String(s) {
-        // Called as function or constructor: convert argument to string type.
-        s = arguments.length ? "" + s : "";
-        if (this instanceof String) {
-            // Called as constructor: save the argument as the string value
-            // of this String object and return this object.
-            this.value = s;
-            return this;
-        }
-        return s;
-    },
-    Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp,
-    Error: Error, EvalError: EvalError, RangeError: RangeError,
-    ReferenceError: ReferenceError, SyntaxError: SyntaxError,
-    TypeError: TypeError, URIError: URIError,
-
-    // Other properties.
-    Math: Math,
-
-    // Extensions to ECMA.
-    snarf: snarf, evaluate: evaluate,
-    load: function load(s) {
-        if (typeof s != "string")
-            return s;
-
-        evaluate(snarf(s), s, 1)
-    },
-    print: print,
-    version: function() { return 185; }
-};
-
-// Helper to avoid Object.prototype.hasOwnProperty polluting scope objects.
-function hasDirectProperty(o, p) {
-    return Object.prototype.hasOwnProperty.call(o, p);
-}
-
-// Reflect a host class into the target global environment by delegation.
-function reflectClass(name, proto) {
-    var gctor = global[name];
-    defineProperty(gctor, "prototype", proto, true, true, true);
-    defineProperty(proto, "constructor", gctor, false, false, true);
-    return proto;
-}
-
-// Reflect Array -- note that all Array methods are generic.
-reflectClass('Array', new Array);
-
-// Reflect String, overriding non-generic methods.
-var gSp = reflectClass('String', new String);
-gSp.toSource = function () { return this.value.toSource(); };
-gSp.toString = function () { return this.value; };
-gSp.valueOf  = function () { return this.value; };
-global.String.fromCharCode = String.fromCharCode;
-
-ExecutionContext.current = null;
-
-ExecutionContext.prototype = {
-    caller: null,
-    callee: null,
-    scope: {object: global, parent: null},
-    thisObject: global,
-    result: undefined,
-    target: null,
-    ecma3OnlyMode: false
-};
-
-function Reference(base, propertyName, node) {
-    this.base = base;
-    this.propertyName = propertyName;
-    this.node = node;
-}
-
-Reference.prototype.toString = function () { return this.node.getSource(); }
-
-function getValue(v) {
-    if (v instanceof Reference) {
-        if (!v.base) {
-            throw new ReferenceError(v.propertyName + " is not defined",
-                                     v.node.filename, v.node.lineno);
-        }
-        return v.base[v.propertyName];
     }
-    return v;
-}
-
-function putValue(v, w, vn) {
-    if (v instanceof Reference)
-        return (v.base || global)[v.propertyName] = w;
-    throw new ReferenceError("Invalid assignment left-hand side",
-                             vn.filename, vn.lineno);
-}
-
-function isPrimitive(v) {
-    var t = typeof v;
-    return (t == "object") ? v === null : t != "function";
-}
-
-function isObject(v) {
-    var t = typeof v;
-    return (t == "object") ? v !== null : t == "function";
-}
-
-// If r instanceof Reference, v == getValue(r); else v === r.  If passed, rn
-// is the node whose execute result was r.
-function toObject(v, r, rn) {
-    switch (typeof v) {
-      case "boolean":
-        return new global.Boolean(v);
-      case "number":
-        return new global.Number(v);
-      case "string":
-        return new global.String(v);
-      case "function":
-        return v;
-      case "object":
-        if (v !== null)
-            return v;
-    }
-    var message = r + " (type " + (typeof v) + ") has no properties";
-    throw rn ? new TypeError(message, rn.filename, rn.lineno)
-             : new TypeError(message);
-}
-
-function execute(n, x) {
-    var a, f, i, j, r, s, t, u, v;
-
-    switch (n.type) {
-      case FUNCTION:
-        if (n.functionForm != DECLARED_FORM) {
-            if (!n.name || n.functionForm == STATEMENT_FORM) {
-                v = newFunction(n, x);
-                if (n.functionForm == STATEMENT_FORM)
-                    defineProperty(x.scope.object, n.name, v, true);
-            } else {
-                t = new Object;
-                x.scope = {object: t, parent: x.scope};
-                try {
-                    v = newFunction(n, x);
-                    defineProperty(t, n.name, v, true, true);
-                } finally {
-                    x.scope = x.scope.parent;
-                }
-            }
-        }
-        break;
-
-      case SCRIPT:
-        t = x.scope.object;
-        a = n.funDecls;
-        for (i = 0, j = a.length; i < j; i++) {
-            s = a[i].name;
-            f = newFunction(a[i], x);
-            defineProperty(t, s, f, x.type != EVAL_CODE);
-        }
-        a = n.varDecls;
-        for (i = 0, j = a.length; i < j; i++) {
-            u = a[i];
-            s = u.name;
-            if (u.readOnly && hasDirectProperty(t, s)) {
-                throw new TypeError("Redeclaration of const " + s,
-                                    u.filename, u.lineno);
-            }
-            if (u.readOnly || !hasDirectProperty(t, s)) {
-                defineProperty(t, s, undefined, x.type != EVAL_CODE, u.readOnly);
-            }
-        }
-        // FALL THROUGH
-
-      case BLOCK:
-        for (i = 0, j = n.length; i < j; i++)
-            execute(n[i], x);
-        break;
-
-      case IF:
-        if (getValue(execute(n.condition, x)))
-            execute(n.thenPart, x);
-        else if (n.elsePart)
-            execute(n.elsePart, x);
-        break;
-
-      case SWITCH:
-        s = getValue(execute(n.discriminant, x));
-        a = n.cases;
-        var matchDefault = false;
-      switch_loop:
-        for (i = 0, j = a.length; ; i++) {
-            if (i == j) {
-                if (n.defaultIndex >= 0) {
-                    i = n.defaultIndex - 1; // no case matched, do default
-                    matchDefault = true;
-                    continue;
-                }
-                break;                      // no default, exit switch_loop
-            }
-            t = a[i];                       // next case (might be default!)
-            if (t.type == CASE) {
-                u = getValue(execute(t.caseLabel, x));
-            } else {
-                if (!matchDefault)          // not defaulting, skip for now
-                    continue;
-                u = s;                      // force match to do default
-            }
-            if (u === s) {
-                for (;;) {                  // this loop exits switch_loop
-                    if (t.statements.length) {
-                        try {
-                            execute(t.statements, x);
-                        } catch (e if e == BREAK && x.target == n) {
-                            break switch_loop;
-                        }
-                    }
-                    if (++i == j)
-                        break switch_loop;
-                    t = a[i];
-                }
-                // NOT REACHED
-            }
-        }
-        break;
-
-      case FOR:
-        n.setup && getValue(execute(n.setup, x));
-        // FALL THROUGH
-      case WHILE:
-        while (!n.condition || getValue(execute(n.condition, x))) {
-            try {
-                execute(n.body, x);
-            } catch (e if e == BREAK && x.target == n) {
-                break;
-            } catch (e if e == CONTINUE && x.target == n) {
-                // Must run the update expression.
-            }
-            n.update && getValue(execute(n.update, x));
-        }
-        break;
-
-      case FOR_IN:
-        u = n.varDecl;
-        if (u)
-            execute(u, x);
-        r = n.iterator;
-        s = execute(n.object, x);
-        v = getValue(s);
-
-        // ECMA deviation to track extant browser JS implementation behavior.
-        t = (v == null && !x.ecma3OnlyMode) ? v : toObject(v, s, n.object);
-        a = [];
-        for (i in t)
-            a.push(i);
-        for (i = 0, j = a.length; i < j; i++) {
-            putValue(execute(r, x), a[i], r);
-            try {
-                execute(n.body, x);
-            } catch (e if e == BREAK && x.target == n) {
-                break;
-            } catch (e if e == CONTINUE && x.target == n) {
-                continue;
-            }
-        }
-        break;
-
-      case DO:
-        do {
-            try {
-                execute(n.body, x);
-            } catch (e if e == BREAK && x.target == n) {
-                break;
-            } catch (e if e == CONTINUE && x.target == n) {
-                continue;
-            }
-        } while (getValue(execute(n.condition, x)));
-        break;
-
-      case BREAK:
-      case CONTINUE:
-        x.target = n.target;
-        throw n.type;
-
-      case TRY:
-        try {
-            execute(n.tryBlock, x);
-        } catch (e if e == THROW && (j = n.catchClauses.length)) {
-            e = x.result;
-            x.result = undefined;
-            for (i = 0; ; i++) {
-                if (i == j) {
-                    x.result = e;
-                    throw THROW;
-                }
-                t = n.catchClauses[i];
-                x.scope = {object: {}, parent: x.scope};
-                defineProperty(x.scope.object, t.varName, e, true);
-                try {
-                    if (t.guard && !getValue(execute(t.guard, x)))
-                        continue;
-                    execute(t.block, x);
-                    break;
-                } finally {
-                    x.scope = x.scope.parent;
-                }
-            }
-        } finally {
-            if (n.finallyBlock)
-                execute(n.finallyBlock, x);
-        }
-        break;
-
-      case THROW:
-        x.result = getValue(execute(n.exception, x));
-        throw THROW;
-
-      case RETURN:
-        x.result = getValue(execute(n.value, x));
-        throw RETURN;
-
-      case WITH:
-        r = execute(n.object, x);
-        t = toObject(getValue(r), r, n.object);
-        x.scope = {object: t, parent: x.scope};
-        try {
-            execute(n.body, x);
-        } finally {
-            x.scope = x.scope.parent;
-        }
-        break;
-
-      case VAR:
-      case CONST:
-        for (i = 0, j = n.length; i < j; i++) {
-            u = n[i].initializer;
-            if (!u)
-                continue;
-            t = n[i].name;
-            for (s = x.scope; s; s = s.parent) {
-                if (hasDirectProperty(s.object, t))
-                    break;
-            }
-            u = getValue(execute(u, x));
-            if (n.type == CONST)
-                defineProperty(s.object, t, u, x.type != EVAL_CODE, true);
-            else
-                s.object[t] = u;
-        }
-        break;
-
-      case DEBUGGER:
-        throw "NYI: " + tokens[n.type];
-
-      case SEMICOLON:
-        if (n.expression)
-            x.result = getValue(execute(n.expression, x));
-        break;
-
-      case LABEL:
-        try {
-            execute(n.statement, x);
-        } catch (e if e == BREAK && x.target == n) {
-        }
-        break;
-
-      case COMMA:
-        for (i = 0, j = n.length; i < j; i++)
-            v = getValue(execute(n[i], x));
-        break;
-
-      case ASSIGN:
-        r = execute(n[0], x);
-        t = n.assignOp;
-        if (t)
-            u = getValue(r);
-        v = getValue(execute(n[1], x));
-        if (t) {
-            switch (t) {
-              case BITWISE_OR:  v = u | v; break;
-              case BITWISE_XOR: v = u ^ v; break;
-              case BITWISE_AND: v = u & v; break;
-              case LSH:         v = u << v; break;
-              case RSH:         v = u >> v; break;
-              case URSH:        v = u >>> v; break;
-              case PLUS:        v = u + v; break;
-              case MINUS:       v = u - v; break;
-              case MUL:         v = u * v; break;
-              case DIV:         v = u / v; break;
-              case MOD:         v = u % v; break;
-            }
-        }
-        putValue(r, v, n[0]);
-        break;
-
-      case HOOK:
-        v = getValue(execute(n[0], x)) ? getValue(execute(n[1], x))
-                                       : getValue(execute(n[2], x));
-        break;
-
-      case OR:
-        v = getValue(execute(n[0], x)) || getValue(execute(n[1], x));
-        break;
-
-      case AND:
-        v = getValue(execute(n[0], x)) && getValue(execute(n[1], x));
-        break;
-
-      case BITWISE_OR:
-        v = getValue(execute(n[0], x)) | getValue(execute(n[1], x));
-        break;
-
-      case BITWISE_XOR:
-        v = getValue(execute(n[0], x)) ^ getValue(execute(n[1], x));
-        break;
-
-      case BITWISE_AND:
-        v = getValue(execute(n[0], x)) & getValue(execute(n[1], x));
-        break;
-
-      case EQ:
-        v = getValue(execute(n[0], x)) == getValue(execute(n[1], x));
-        break;
-
-      case NE:
-        v = getValue(execute(n[0], x)) != getValue(execute(n[1], x));
-        break;
-
-      case STRICT_EQ:
-        v = getValue(execute(n[0], x)) === getValue(execute(n[1], x));
-        break;
-
-      case STRICT_NE:
-        v = getValue(execute(n[0], x)) !== getValue(execute(n[1], x));
-        break;
-
-      case LT:
-        v = getValue(execute(n[0], x)) < getValue(execute(n[1], x));
-        break;
+    
+    return {
+        "evaluate": evaluate
+    };
 
-      case LE:
-        v = getValue(execute(n[0], x)) <= getValue(execute(n[1], x));
-        break;
-
-      case GE:
-        v = getValue(execute(n[0], x)) >= getValue(execute(n[1], x));
-        break;
-
-      case GT:
-        v = getValue(execute(n[0], x)) > getValue(execute(n[1], x));
-        break;
-
-      case IN:
-        v = getValue(execute(n[0], x)) in getValue(execute(n[1], x));
-        break;
-
-      case INSTANCEOF:
-        t = getValue(execute(n[0], x));
-        u = getValue(execute(n[1], x));
-        if (isObject(u) && typeof u.__hasInstance__ == "function")
-            v = u.__hasInstance__(t);
-        else
-            v = t instanceof u;
-        break;
-
-      case LSH:
-        v = getValue(execute(n[0], x)) << getValue(execute(n[1], x));
-        break;
-
-      case RSH:
-        v = getValue(execute(n[0], x)) >> getValue(execute(n[1], x));
-        break;
-
-      case URSH:
-        v = getValue(execute(n[0], x)) >>> getValue(execute(n[1], x));
-        break;
-
-      case PLUS:
-        v = getValue(execute(n[0], x)) + getValue(execute(n[1], x));
-        break;
-
-      case MINUS:
-        v = getValue(execute(n[0], x)) - getValue(execute(n[1], x));
-        break;
-
-      case MUL:
-        v = getValue(execute(n[0], x)) * getValue(execute(n[1], x));
-        break;
-
-      case DIV:
-        v = getValue(execute(n[0], x)) / getValue(execute(n[1], x));
-        break;
-
-      case MOD:
-        v = getValue(execute(n[0], x)) % getValue(execute(n[1], x));
-        break;
-
-      case DELETE:
-        t = execute(n[0], x);
-        v = !(t instanceof Reference) || delete t.base[t.propertyName];
-        break;
-
-      case VOID:
-        getValue(execute(n[0], x));
-        break;
-
-      case TYPEOF:
-        t = execute(n[0], x);
-        if (t instanceof Reference)
-            t = t.base ? t.base[t.propertyName] : undefined;
-        v = typeof t;
-        break;
-
-      case NOT:
-        v = !getValue(execute(n[0], x));
-        break;
-
-      case BITWISE_NOT:
-        v = ~getValue(execute(n[0], x));
-        break;
-
-      case UNARY_PLUS:
-        v = +getValue(execute(n[0], x));
-        break;
-
-      case UNARY_MINUS:
-        v = -getValue(execute(n[0], x));
-        break;
-
-      case INCREMENT:
-      case DECREMENT:
-        t = execute(n[0], x);
-        u = Number(getValue(t));
-        if (n.postfix)
-            v = u;
-        putValue(t, (n.type == INCREMENT) ? ++u : --u, n[0]);
-        if (!n.postfix)
-            v = u;
-        break;
-
-      case DOT:
-        r = execute(n[0], x);
-        t = getValue(r);
-        u = n[1].value;
-        v = new Reference(toObject(t, r, n[0]), u, n);
-        break;
-
-      case INDEX:
-        r = execute(n[0], x);
-        t = getValue(r);
-        u = getValue(execute(n[1], x));
-        v = new Reference(toObject(t, r, n[0]), String(u), n);
-        break;
-
-      case LIST:
-        // Curse ECMA for specifying that arguments is not an Array object!
-        v = {};
-        for (i = 0, j = n.length; i < j; i++) {
-            u = getValue(execute(n[i], x));
-            defineProperty(v, i, u, false, false, true);
-        }
-        defineProperty(v, "length", i, false, false, true);
-        break;
-
-      case CALL:
-        r = execute(n[0], x);
-        a = execute(n[1], x);
-        f = getValue(r);
-        if (isPrimitive(f) || typeof f.__call__ != "function") {
-            throw new TypeError(r + " is not callable",
-                                n[0].filename, n[0].lineno);
-        }
-        t = (r instanceof Reference) ? r.base : null;
-        if (t instanceof Activation)
-            t = null;
-        v = f.__call__(t, a, x);
-        break;
-
-      case NEW:
-      case NEW_WITH_ARGS:
-        r = execute(n[0], x);
-        f = getValue(r);
-        if (n.type == NEW) {
-            a = {};
-            defineProperty(a, "length", 0, false, false, true);
-        } else {
-            a = execute(n[1], x);
-        }
-        if (isPrimitive(f) || typeof f.__construct__ != "function") {
-            throw new TypeError(r + " is not a constructor",
-                                n[0].filename, n[0].lineno);
-        }
-        v = f.__construct__(a, x);
-        break;
-
-      case ARRAY_INIT:
-        v = [];
-        for (i = 0, j = n.length; i < j; i++) {
-            if (n[i])
-                v[i] = getValue(execute(n[i], x));
-        }
-        v.length = j;
-        break;
-
-      case OBJECT_INIT:
-        v = {};
-        for (i = 0, j = n.length; i < j; i++) {
-            t = n[i];
-            if (t.type == PROPERTY_INIT) {
-                v[t[0].value] = getValue(execute(t[1], x));
-            } else {
-                f = newFunction(t, x);
-                u = (t.type == GETTER) ? '__defineGetter__'
-                                       : '__defineSetter__';
-                v[u](t.name, thunk(f, x));
-            }
-        }
-        break;
-
-      case NULL:
-        v = null;
-        break;
-
-      case THIS:
-        v = x.thisObject;
-        break;
-
-      case TRUE:
-        v = true;
-        break;
-
-      case FALSE:
-        v = false;
-        break;
-
-      case IDENTIFIER:
-        for (s = x.scope; s; s = s.parent) {
-            if (n.value in s.object)
-                break;
-        }
-        v = new Reference(s && s.object, n.value, n);
-        break;
-
-      case NUMBER:
-      case STRING:
-      case REGEXP:
-        v = n.value;
-        break;
-
-      case GROUP:
-        v = execute(n[0], x);
-        break;
-
-      default:
-        throw "PANIC: unknown operation " + n.type + ": " + uneval(n);
-    }
-
-    return v;
-}
-
-function Activation(f, a) {
-    for (var i = 0, j = f.params.length; i < j; i++)
-        defineProperty(this, f.params[i], a[i], true);
-    defineProperty(this, "arguments", a, true);
-}
-
-// Null Activation.prototype's proto slot so that Object.prototype.* does not
-// pollute the scope of heavyweight functions.  Also delete its 'constructor'
-// property so that it doesn't pollute function scopes.
-
-Activation.prototype.__proto__ = null;
-delete Activation.prototype.constructor;
+}());
 
-function FunctionObject(node, scope) {
-    this.node = node;
-    this.scope = scope;
-    defineProperty(this, "length", node.params.length, true, true, true);
-    var proto = {};
-    defineProperty(this, "prototype", proto, true);
-    defineProperty(proto, "constructor", this, false, false, true);
-}
-
-// Returns a new function wrapped with a Proxy.
-function newFunction(n,x) {
-    var f = new FunctionObject(n, x.scope);
-    var p = Proxy.createFunction(
-
-            // Handler function copied from
-            //  http://wiki.ecmascript.org/doku.php?id=harmony:proxies&s=proxy%20object#examplea_no-op_forwarding_proxy
-            function(obj) { return {
-                getOwnPropertyDescriptor: function(name) {
-                    var desc = Object.getOwnPropertyDescriptor(obj);
-
-                    // a trapping proxy's properties must always be configurable
-                    desc.configurable = true;
-                    return desc;
-                 },
-                getPropertyDescriptor: function(name) {
-                    var desc = Object.getPropertyDescriptor(obj); //assumed
-
-                    // 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 Object.getOwnProperties(obj); // assumed
-                    }
-
-                    // 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;
-                },
-                enumerateOwn: function() { return Object.keys(obj); } };
-            }(f),
-            function() { return f.__call__(this, arguments, x); },
-            function() { return f.__construct__(arguments, x); });
-    return p;
-}
-
-var FOp = FunctionObject.prototype = {
-
-    // Internal methods.
-    __call__: function (t, a, x) {
-        var x2 = new ExecutionContext(FUNCTION_CODE);
-        x2.thisObject = t || global;
-        x2.caller = x;
-        x2.callee = this;
-        defineProperty(a, "callee", this, false, false, true);
-        var f = this.node;
-        x2.scope = {object: new Activation(f, a), parent: this.scope};
-
-        ExecutionContext.current = x2;
-        try {
-            execute(f.body, x2);
-        } catch (e if e == RETURN) {
-            return x2.result;
-        } catch (e if e == THROW) {
-            x.result = x2.result;
-            throw THROW;
-        } finally {
-            ExecutionContext.current = x;
-        }
-        return undefined;
-    },
-
-    __construct__: function (a, x) {
-        var o = new Object;
-        var p = this.prototype;
-        if (isObject(p))
-            o.__proto__ = p;
-        // else o.__proto__ defaulted to Object.prototype
-
-        var v = this.__call__(o, a, x);
-        if (isObject(v))
-            return v;
-        return o;
-    },
-
-    __hasInstance__: function (v) {
-        if (isPrimitive(v))
-            return false;
-        var p = this.prototype;
-        if (isPrimitive(p)) {
-            throw new TypeError("'prototype' property is not an object",
-                                this.node.filename, this.node.lineno);
-        }
-        var o;
-        while ((o = v.__proto__)) {
-            if (o == p)
-                return true;
-            v = o;
-        }
-        return false;
-    },
-
-    // Standard methods.
-    toString: function () {
-        return this.node.getSource();
-    },
-
-    apply: function (t, a) {
-        // Curse ECMA again!
-        if (typeof this.__call__ != "function") {
-            throw new TypeError("Function.prototype.apply called on" +
-                                " uncallable object");
-        }
-
-        if (t === undefined || t === null)
-            t = global;
-        else if (typeof t != "object")
-            t = toObject(t, t);
-
-        if (a === undefined || a === null) {
-            a = {};
-            defineProperty(a, "length", 0, false, false, true);
-        } else if (a instanceof Array) {
-            var v = {};
-            for (var i = 0, j = a.length; i < j; i++)
-                defineProperty(v, i, a[i], false, false, true);
-            defineProperty(v, "length", i, false, false, true);
-            a = v;
-        } else if (!(a instanceof Object)) {
-            // XXX check for a non-arguments object
-            throw new TypeError("Second argument to Function.prototype.apply" +
-                                " must be an array or arguments object",
-                                this.node.filename, this.node.lineno);
-        }
-
-        return this.__call__(t, a, ExecutionContext.current);
-    },
-
-    call: function (t) {
-        // Curse ECMA a third time!
-        var a = Array.prototype.splice.call(arguments, 1);
-        return this.apply(t, a);
-    }
-};
-
-// Connect Function.prototype and Function.prototype.constructor in global.
-reflectClass('Function', FOp);
-
-// Help native and host-scripted functions be like FunctionObjects.
-var Fp = Function.prototype;
-var REp = RegExp.prototype;
-
-if (!('__call__' in Fp)) {
-    defineProperty(Fp, "__call__",
-                   function (t, a, x) {
-                       // Curse ECMA yet again!
-                       a = Array.prototype.splice.call(a, 0, a.length);
-                       return this.apply(t, a);
-                   }, true, true, true);
-    defineProperty(REp, "__call__",
-                   function (t, a, x) {
-                       a = Array.prototype.splice.call(a, 0, a.length);
-                       return this.exec.apply(this, a);
-                   }, true, true, true);
-    defineProperty(Fp, "__construct__",
-                   function (a, x) {
-                       a = Array.prototype.splice.call(a, 0, a.length);
-                       switch (a.length) {
-                         case 0:
-                           return new this();
-                         case 1:
-                           return new this(a[0]);
-                         case 2:
-                           return new this(a[0], a[1]);
-                         case 3:
-                           return new this(a[0], a[1], a[2]);
-                         default:
-                           var argStr = "";
-                           for (var i=0; i<a.length; i++) {
-                               argStr += 'a[' + i + '],';
-                           }
-                           return eval('new this(' + argStr.slice(0,-1) + ');');
-                       }
-                   }, true, true, true);
-
-    // Since we use native functions such as Date along with host ones such
-    // as global.eval, we want both to be considered instances of the native
-    // Function constructor.
-    defineProperty(Fp, "__hasInstance__",
-                   function (v) {
-                       return v instanceof Function || v instanceof global.Function;
-                   }, true, true, true);
-}
-
-function thunk(f, x) {
-    return function () { return f.__call__(this, arguments, x); };
-}
-
-function evaluate(s, f, l) {
-    if (typeof s != "string")
-        return s;
-
-    var x = ExecutionContext.current;
-    var x2 = new ExecutionContext(GLOBAL_CODE);
-    ExecutionContext.current = x2;
-    try {
-        execute(parse(new VanillaBuilder, s, f, l), x2);
-    } catch (e if e == THROW) {
-        if (x) {
-            x.result = x2.result;
-            throw THROW;
-        }
-        throw x2.result;
-    } finally {
-        ExecutionContext.current = x;
-    }
-    return x2.result;
-}
--- a/js/narcissus/jslex.js
+++ b/js/narcissus/jslex.js
@@ -36,422 +36,434 @@
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * Narcissus - JS implemented in JS.
  *
  * Lexical scanner.
  */
 
-// Build up a trie of operator tokens.
-var opTokens = {};
-for (var op in 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;
-    }
-}
-
-/*
- * Tokenizer :: (file ptr, path, 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.filename = f || "";
-    this.lineno = l || 1;
-}
-
-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();
-    },
+Narcissus.jslex = (function() {
 
-    mustMatch: function (tt) {
-        if (!this.match(tt))
-            throw this.newSyntaxError("Missing " + 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();
+    var jsdefs = Narcissus.jsdefs;
+    
+    // Set constants in the local scope.
+    eval(jsdefs.consts);
+    
+    // Build up a trie of operator tokens.
+    var opTokens = {};
+    for (var op in jsdefs.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;
         }
-        return tt;
-    },
-
-    peekOnSameLine: function (scanOperand) {
-        this.scanNewlines = true;
-        var tt = this.peek(scanOperand);
+    }
+    
+    /*
+     * Tokenizer :: (file ptr, path, 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;
-        return tt;
-    },
-
-    // Eats comments and whitespace.
-    skip: function () {
-        var input = this.source;
-        for (;;) {
-            var ch = input[this.cursor++];
-            var next = input[this.cursor];
-            if (ch === '\n' && !this.scanNewlines) {
-                this.lineno++;
-            } else if (ch === '/' && next === '*') {
-                this.cursor++;
-                for (;;) {
-                    ch = input[this.cursor++];
-                    if (ch === undefined)
-                        throw this.newSyntaxError("Unterminated comment");
-
-                    if (ch === '*') {
-                        next = input[this.cursor];
-                        if (next === '/') {
-                            this.cursor++;
+        this.filename = f || "";
+        this.lineno = l || 1;
+    }
+    
+    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 " + 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;
+        },
+    
+        // Eats comments and whitespace.
+        skip: function () {
+            var input = this.source;
+            for (;;) {
+                var ch = input[this.cursor++];
+                var next = input[this.cursor];
+                if (ch === '\n' && !this.scanNewlines) {
+                    this.lineno++;
+                } else if (ch === '/' && next === '*') {
+                    this.cursor++;
+                    for (;;) {
+                        ch = input[this.cursor++];
+                        if (ch === undefined)
+                            throw this.newSyntaxError("Unterminated comment");
+    
+                        if (ch === '*') {
+                            next = input[this.cursor];
+                            if (next === '/') {
+                                this.cursor++;
+                                break;
+                            }
+                        } else if (ch === '\n') {
+                            this.lineno++;
+                        }
+                    }
+                } else if (ch === '/' && next === '/') {
+                    this.cursor++;
+                    for (;;) {
+                        ch = input[this.cursor++];
+                        if (ch === undefined)
+                            return;
+    
+                        if (ch === '\n') {
+                            this.lineno++;
                             break;
                         }
-                    } else if (ch === '\n') {
-                        this.lineno++;
                     }
+                } else if (ch !== ' ' && ch !== '\t') {
+                    this.cursor--;
+                    return;
                 }
-            } else if (ch === '/' && next === '/') {
+            }
+        },
+    
+        // Lexes the exponential part of a number, if present. Returns 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++;
-                for (;;) {
+                ch = input[this.cursor++];
+                if (ch === '+' || ch === '-')
                     ch = input[this.cursor++];
-                    if (ch === undefined)
-                        return;
-
-                    if (ch === '\n') {
-                        this.lineno++;
-                        break;
-                    }
-                }
-            } else if (ch !== ' ' && ch !== '\t') {
+    
+                if (ch < '0' || ch > '9')
+                    throw this.newSyntaxError("Missing exponent");
+    
+                do {
+                    ch = input[this.cursor++];
+                } while (ch >= '0' && ch <= '9');
                 this.cursor--;
-                return;
+    
+                return true;
             }
-        }
-    },
-
-    // Lexes the exponential part of a number, if present. Returns 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++;
+    
+            return false;
+        },
+    
+        lexZeroNumber: function (ch) {
+            var token = this.token, input = this.source;
+            token.type = NUMBER;
+    
             ch = input[this.cursor++];
-            if (ch === '+' || ch === '-')
-                ch = input[this.cursor++];
-
-            if (ch < '0' || ch > '9')
-                throw this.newSyntaxError("Missing exponent");
-
+            if (ch === '.') {
+                do {
+                    ch = input[this.cursor++];
+                } while (ch >= '0' && ch <= '9');
+                this.cursor--;
+    
+                this.lexExponent();
+                token.value = parseFloat(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--;
-
-            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(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 {
+    
+            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(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;
             ch = input[this.cursor++];
-            if (ch === '.' && !floating) {
-                floating = true;
+            while (ch !== delim) {
+                if (ch === '\\') {
+                    hasEscapes = true;
+                    this.cursor++;
+                }
                 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') {
+    
+            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++];
-            } while (ch >= '0' && ch <= '9');
+                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--;
-
-            this.lexExponent();
-
-            token.type = NUMBER;
-            token.value = parseFloat(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;
-        ch = input[this.cursor++];
-        while (ch !== delim) {
-            if (ch === '\\') {
-                hasEscapes = true;
-                this.cursor++;
-            }
-            ch = input[this.cursor++];
-        }
-
-        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];
+    
+            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 (assignOps[op] && input[this.cursor] === '=') {
-            this.cursor++;
-            token.type = ASSIGN;
-            token.assignOp = tokenIds[opTypeNames[op]];
-            op += '=';
-        } else {
-            token.type = tokenIds[opTypeNames[op]];
-            token.assignOp = null;
-        }
-
-        token.value = op;
-    },
-
-    // FIXME: Unicode escape sequences
-    // FIXME: Unicode identifiers
-    lexIdent: function (ch) {
-        var token = this.token, input = this.source;
-
-        do {
-            ch = input[this.cursor++];
-        } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
-                 (ch >= '0' && ch <= '9') || ch === '$' || ch === '_');
-
-        this.cursor--;  // Put the non-word character back.
-
-        var id = input.substring(token.start, this.cursor);
-        token.type = keywords[id] || IDENTIFIER;
-        token.value = id;
-    },
-
-    /*
-     * Tokenizer.get :: void -> token type
-     *
-     * Consumes 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;
+    
+            var op = node.op;
+            if (jsdefs.assignOps[op] && input[this.cursor] === '=') {
+                this.cursor++;
+                token.type = ASSIGN;
+                token.assignOp = jsdefs.tokenIds[jsdefs.opTypeNames[op]];
+                op += '=';
+            } else {
+                token.type = jsdefs.tokenIds[jsdefs.opTypeNames[op]];
+                token.assignOp = null;
+            }
+    
+            token.value = op;
+        },
+    
+        // FIXME: Unicode escape sequences
+        // FIXME: Unicode identifiers
+        lexIdent: function (ch) {
+            var token = this.token, input = this.source;
+    
+            do {
+                ch = input[this.cursor++];
+            } while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+                     (ch >= '0' && ch <= '9') || ch === '$' || ch === '_');
+    
+            this.cursor--;  // Put the non-word character back.
+    
+            var id = input.substring(token.start, this.cursor);
+            token.type = jsdefs.keywords[id] || IDENTIFIER;
+            token.value = id;
+        },
+    
+        /*
+         * Tokenizer.get :: void -> token type
+         *
+         * Consumes 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.type != NEWLINE || this.scanNewlines)
-                return token.type;
+            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 ch = input[this.cursor++];
+            if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+                    ch === '$' || ch === '_') {
+                this.lexIdent(ch);
+            } 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') {
+                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) {
+            var e = new SyntaxError(m, this.filename, this.lineno);
+            e.source = this.source;
+            e.cursor = this.cursor;
+            return e;
+        },
+    
+        save: function () {
+            return {
+                cursor: this.cursor,
+                tokenIndex: this.tokenIndex,
+                tokens: this.tokens.slice(),
+                lookahead: this.lookahead,
+                scanNewlines: this.scanNewlines,
+                lineno: this.lineno
+            };
+        },
+    
+        rewind: function(point) {
+            this.cursor = point.cursor;
+            this.tokenIndex = point.tokenIndex;
+            this.tokens = point.tokens.slice();
+            this.lookahead = point.lookahead;
+            this.scanNewline = point.scanNewline;
+            this.lineno = point.lineno;
         }
-
-        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 ch = input[this.cursor++];
-        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
-                ch === '$' || ch === '_') {
-            this.lexIdent(ch);
-        } 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') {
-            token.type = NEWLINE;
-            token.value = '\n';
-            this.lineno++;
-        } else {
-            throw this.newSyntaxError("Illegal token");
-        }
+    };
+    
+    return { "Tokenizer": Tokenizer };
 
-        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) {
-        var e = new SyntaxError(m, this.filename, this.lineno);
-        e.source = this.source;
-        e.cursor = this.cursor;
-        return e;
-    },
+}());
 
-    save: function () {
-        return {
-            cursor: this.cursor,
-            tokenIndex: this.tokenIndex,
-            tokens: this.tokens.slice(),
-            lookahead: this.lookahead,
-            scanNewlines: this.scanNewlines,
-            lineno: this.lineno
-        };
-    },
-
-    rewind: function(point) {
-        this.cursor = point.cursor;
-        this.tokenIndex = point.tokenIndex;
-        this.tokens = point.tokens.slice();
-        this.lookahead = point.lookahead;
-        this.scanNewline = point.scanNewline;
-        this.lineno = point.lineno;
-    }
-};
--- a/js/narcissus/jsparse.js
+++ b/js/narcissus/jsparse.js
@@ -42,2202 +42,2223 @@
  *
  * Parser.
  */
 
 /*
  * The vanilla AST builder.
  */
 
-function VanillaBuilder() {
-}
-
-VanillaBuilder.prototype = {
-    IF$build: function(t) {
-        return new Node(t, IF);
-    },
-
-    IF$setCondition: function(n, e) {
-        n.condition = e;
-    },
-
-    IF$setThenPart: function(n, s) {
-        n.thenPart = s;
-    },
-
-    IF$setElsePart: function(n, s) {
-        n.elsePart = s;
-    },
-
-    IF$finish: function(n) {
-    },
-
-    SWITCH$build: function(t) {
-        var n = new Node(t, SWITCH);
-        n.cases = [];
-        n.defaultIndex = -1;
-        return n;
-    },
-
-    SWITCH$setDiscriminant: function(n, e) {
-        n.discriminant = e;
-    },
-
-    SWITCH$setDefaultIndex: function(n, i) {
-        n.defaultIndex = i;
-    },
-
-    SWITCH$addCase: function(n, n2) {
-        n.cases.push(n2);
-    },
-
-    SWITCH$finish: function(n) {
-    },
-
-    CASE$build: function(t) {
-        return new Node(t, CASE);
-    },
-
-    CASE$setLabel: function(n, e) {
-        n.caseLabel = e;
-    },
-
-    CASE$initializeStatements: function(n, t) {
-        n.statements = new Node(t, BLOCK);
-    },
-
-    CASE$addStatement: function(n, s) {
-        n.statements.push(s);
-    },
-
-    CASE$finish: function(n) {
-    },
-
-    DEFAULT$build: function(t, p) {
-        return new Node(t, DEFAULT);
-    },
-
-    DEFAULT$initializeStatements: function(n, t) {
-        n.statements = new Node(t, BLOCK);
-    },
-
-    DEFAULT$addStatement: function(n, s) {
-        n.statements.push(s);
-    },
-
-    DEFAULT$finish: function(n) {
-    },
-
-    FOR$build: function(t) {
-        var n = new Node(t, FOR);
-        n.isLoop = true;
-        n.isEach = false;
-        return n;
-    },
-
-    FOR$rebuildForEach: function(n) {
-        n.isEach = true;
-    },
-
-    // NB. This function is called after rebuildForEach, if that's called
-    // at all.
-    FOR$rebuildForIn: function(n) {
-        n.type = FOR_IN;
-    },
-
-    FOR$setCondition: function(n, e) {
-        n.condition = e;
-    },
-
-    FOR$setSetup: function(n, e) {
-        n.setup = e || null;
-    },
-
-    FOR$setUpdate: function(n, e) {
-        n.update = e;
-    },
-
-    FOR$setObject: function(n, e) {
-        n.object = e;
-    },
-
-    FOR$setIterator: function(n, e, e2) {
-        n.iterator = e;
-        n.varDecl = e2;
-    },
-
-    FOR$setBody: function(n, s) {
-        n.body = s;
-    },
-
-    FOR$finish: function(n) {
-    },
-
-    WHILE$build: function(t) {
-        var n = new Node(t, WHILE);
-        n.isLoop = true;
-        return n;
-    },
-
-    WHILE$setCondition: function(n, e) {
-        n.condition = e;
-    },
-
-    WHILE$setBody: function(n, s) {
-        n.body = s;
-    },
-
-    WHILE$finish: function(n) {
-    },
-
-    DO$build: function(t) {
-        var n = new Node(t, DO);
-        n.isLoop = true;
-        return n;
-    },
-
-    DO$setCondition: function(n, e) {
-        n.condition = e;
-    },
-
-    DO$setBody: function(n, s) {
-        n.body = s;
-    },
-
-    DO$finish: function(n) {
-    },
-
-    BREAK$build: function(t) {
-        return new Node(t, BREAK);
-    },
-
-    BREAK$setLabel: function(n, v) {
-        n.label = v;
-    },
-
-    BREAK$setTarget: function(n, n2) {
-        n.target = n2;
-    },
-
-    BREAK$finish: function(n) {
-    },
-
-    CONTINUE$build: function(t) {
-        return new Node(t, CONTINUE);
-    },
-
-    CONTINUE$setLabel: function(n, v) {
-        n.label = v;
-    },
-
-    CONTINUE$setTarget: function(n, n2) {
-        n.target = n2;
-    },
-
-    CONTINUE$finish: function(n) {
-    },
-
-    TRY$build: function(t) {
-        var n = new Node(t, TRY);
-        n.catchClauses = [];
-        return n;
-    },
-
-    TRY$setTryBlock: function(n, s) {
-        n.tryBlock = s;
-    },
-
-    TRY$addCatch: function(n, n2) {
-        n.catchClauses.push(n2);
-    },
-
-    TRY$finishCatches: function(n) {
-    },
-
-    TRY$setFinallyBlock: function(n, s) {
-        n.finallyBlock = s;
-    },
-
-    TRY$finish: function(n) {
-    },
-
-    CATCH$build: function(t) {
-        var n = new Node(t, CATCH);
-        n.guard = null;
-        return n;
-    },
-
-    CATCH$setVarName: function(n, v) {
-        n.varName = v;
-    },
-
-    CATCH$setGuard: function(n, e) {
-        n.guard = e;
-    },
-
-    CATCH$setBlock: function(n, s) {
-        n.block = s;
-    },
-
-    CATCH$finish: function(n) {
-    },
-
-    THROW$build: function(t) {
-        return new Node(t, THROW);
-    },
-
-    THROW$setException: function(n, e) {
-        n.exception = e;
-    },
-
-    THROW$finish: function(n) {
-    },
-
-    RETURN$build: function(t) {
-        return new Node(t, RETURN);
-    },
-
-    RETURN$setValue: function(n, e) {
-        n.value = e;
-    },
-
-    RETURN$finish: function(n) {
-    },
-
-    YIELD$build: function(t) {
-        return new Node(t, YIELD);
-    },
-
-    YIELD$setValue: function(n, e) {
-        n.value = e;
-    },
-
-    YIELD$finish: function(n) {
-    },
-
-    GENERATOR$build: function(t) {
-        return new Node(t, GENERATOR);
-    },
-
-    GENERATOR$setExpression: function(n, e) {
-        n.expression = e;
-    },
-
-    GENERATOR$setTail: function(n, n2) {
-        n.tail = n2;
-    },
-
-    GENERATOR$finish: function(n) {
-    },
-
-    WITH$build: function(t) {
-        return new Node(t, WITH);
-    },
-
-    WITH$setObject: function(n, e) {
-        n.object = e;
-    },
-
-    WITH$setBody: function(n, s) {
-        n.body = s;
-    },
-
-    WITH$finish: function(n) {
-    },
-
-    DEBUGGER$build: function(t) {
-        return new Node(t, DEBUGGER);
-    },
-
-    SEMICOLON$build: function(t) {
-        return new Node(t, SEMICOLON);
-    },
-
-    SEMICOLON$setExpression: function(n, e) {
-        n.expression = e;
-    },
-
-    SEMICOLON$finish: function(n) {
-    },
-
-    LABEL$build: function(t) {
-        return new Node(t, LABEL);
-    },
-
-    LABEL$setLabel: function(n, e) {
-        n.label = e;
-    },
-
-    LABEL$setStatement: function(n, s) {
-        n.statement = s;
-    },
-
-    LABEL$finish: function(n) {
-    },
-
-    FUNCTION$build: function(t) {
-        var n = new Node(t);
-        if (n.type != FUNCTION)
-            n.type = (n.value == "get") ? GETTER : SETTER;
-        n.params = [];
-        return n;
-    },
+Narcissus.jsparse = (function() {
 
-    FUNCTION$setName: function(n, v) {
-        n.name = v;
-    },
-
-    FUNCTION$addParam: function(n, v) {
-        n.params.push(v);
-    },
-
-    FUNCTION$setBody: function(n, s) {
-        n.body = s;
-    },
-
-    FUNCTION$hoistVars: function(x) {
-    },
-
-    FUNCTION$finish: function(n, x) {
-    },
-
-    VAR$build: function(t) {
-        return new Node(t, VAR);
-    },
-
-    VAR$addDecl: function(n, n2, x) {
-        n.push(n2);
-    },
-
-    VAR$finish: function(n) {
-    },
-
-    CONST$build: function(t) {
-        return new Node(t, VAR);
-    },
-
-    CONST$addDecl: function(n, n2, x) {
-        n.push(n2);
-    },
-
-    CONST$finish: function(n) {
-    },
-
-    LET$build: function(t) {
-        return new Node(t, LET);
-    },
-
-    LET$addDecl: function(n, n2, x) {
-        n.push(n2);
-    },
-
-    LET$finish: function(n) {
-    },
-
-    DECL$build: function(t) {
-        return new Node(t, IDENTIFIER);
-    },
-
-    DECL$setName: function(n, v) {
-        n.name = v;
-    },
-
-    DECL$setInitializer: function(n, e) {
-        n.initializer = e;
-    },
-
-    DECL$setReadOnly: function(n, b) {
-        n.readOnly = b;
-    },
-
-    DECL$finish: function(n) {
-    },
-
-    LET_BLOCK$build: function(t) {
-        var n = Node(t, LET_BLOCK);
-        n.varDecls = [];
-        return n;
-    },
-
-    LET_BLOCK$setVariables: function(n, n2) {
-        n.variables = n2;
-    },
-
-    LET_BLOCK$setExpression: function(n, e) {
-        n.expression = e;
-    },
-
-    LET_BLOCK$setBlock: function(n, s) {
-        n.block = s;
-    },
-
-    LET_BLOCK$finish: function(n) {
-    },
-
-    BLOCK$build: function(t, id) {
-        var n = new Node(t, BLOCK);
-        n.varDecls = [];
-        n.id = id;
-        return n;
-    },
-
-    BLOCK$hoistLets: function(n) {
-    },
-
-    BLOCK$addStatement: function(n, n2) {
-        n.push(n2);
-    },
-
-    BLOCK$finish: function(n) {
-    },
-
-    EXPRESSION$build: function(t, tt) {
-        return new Node(t, tt);
-    },
-
-    EXPRESSION$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    EXPRESSION$finish: function(n) {
-    },
-
-    ASSIGN$build: function(t) {
-        return new Node(t, ASSIGN);
-    },
-
-    ASSIGN$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    ASSIGN$setAssignOp: function(n, o) {
-        n.assignOp = o;
-    },
-
-    ASSIGN$finish: function(n) {
-    },
-
-    HOOK$build: function(t) {
-        return new Node(t, HOOK);
-    },
-
-    HOOK$setCondition: function(n, e) {
-        n[0] = e;
-    },
-
-    HOOK$setThenPart: function(n, n2) {
-        n[1] = n2;
-    },
-
-    HOOK$setElsePart: function(n, n2) {
-        n[2] = n2;
-    },
-
-    HOOK$finish: function(n) {
-    },
-
-    OR$build: function(t) {
-        return new Node(t, OR);
-    },
-
-    OR$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    OR$finish: function(n) {
-    },
-
-    AND$build: function(t) {
-        return new Node(t, AND);
-    },
-
-    AND$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    AND$finish: function(n) {
-    },
-
-    BITWISE_OR$build: function(t) {
-        return new Node(t, BITWISE_OR);
-    },
-
-    BITWISE_OR$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    BITWISE_OR$finish: function(n) {
-    },
-
-    BITWISE_XOR$build: function(t) {
-        return new Node(t, BITWISE_XOR);
-    },
-
-    BITWISE_XOR$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    BITWISE_XOR$finish: function(n) {
-    },
-
-    BITWISE_AND$build: function(t) {
-        return new Node(t, BITWISE_AND);
-    },
-
-    BITWISE_AND$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    BITWISE_AND$finish: function(n) {
-    },
-
-    EQUALITY$build: function(t) {
-        // NB t.token.type must be EQ, NE, STRICT_EQ, or STRICT_NE.
-        return new Node(t);
-    },
-
-    EQUALITY$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    EQUALITY$finish: function(n) {
-    },
-
-    RELATIONAL$build: function(t) {
-        // NB t.token.type must be LT, LE, GE, or GT.
-        return new Node(t);
-    },
-
-    RELATIONAL$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    RELATIONAL$finish: function(n) {
-    },
-
-    SHIFT$build: function(t) {
-        // NB t.token.type must be LSH, RSH, or URSH.
-        return new Node(t);
-    },
-
-    SHIFT$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    SHIFT$finish: function(n) {
-    },
-
-    ADD$build: function(t) {
-        // NB t.token.type must be PLUS or MINUS.
-        return new Node(t);
-    },
-
-    ADD$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    ADD$finish: function(n) {
-    },
-
-    MULTIPLY$build: function(t) {
-        // NB t.token.type must be MUL, DIV, or MOD.
-        return new Node(t);
-    },
-
-    MULTIPLY$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    MULTIPLY$finish: function(n) {
-    },
-
-    UNARY$build: function(t) {
-        // NB t.token.type must be DELETE, VOID, TYPEOF, NOT, BITWISE_NOT,
-        // UNARY_PLUS, UNARY_MINUS, INCREMENT, or DECREMENT.
-        if (t.token.type == PLUS)
-            t.token.type = UNARY_PLUS;
-        else if (t.token.type == MINUS)
-            t.token.type = UNARY_MINUS;
-        return new Node(t);
-    },
-
-    UNARY$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    UNARY$setPostfix: function(n) {
-        n.postfix = true;
-    },
-
-    UNARY$finish: function(n) {
-    },
-
-    MEMBER$build: function(t, tt) {
-        // NB t.token.type must be NEW, DOT, or INDEX.
-        return new Node(t, tt);
-    },
-
-    MEMBER$rebuildNewWithArgs: function(n) {
-        n.type = NEW_WITH_ARGS;
-    },
-
-    MEMBER$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    MEMBER$finish: function(n) {
-    },
-
-    PRIMARY$build: function(t, tt) {
-        // NB t.token.type must be NULL, THIS, TRUIE, FALSE, IDENTIFIER,
-        // NUMBER, STRING, or REGEXP.
-        return new Node(t, tt);
-    },
-
-    PRIMARY$finish: function(n) {
-    },
-
-    ARRAY_INIT$build: function(t) {
-        return new Node(t, ARRAY_INIT);
-    },
-
-    ARRAY_INIT$addElement: function(n, n2) {
-        n.push(n2);
-    },
-
-    ARRAY_INIT$finish: function(n) {
-    },
-
-    ARRAY_COMP: {
-        build: function(t) {
-            return new Node(t, ARRAY_COMP);
+    var jslex = Narcissus.jslex;
+    var jsdefs = Narcissus.jsdefs;
+    
+    // Set constants in the local scope.
+    eval(jsdefs.consts);
+    
+    VanillaBuilder = function VanillaBuilder() {
+    }
+    
+    VanillaBuilder.prototype = {
+        IF$build: function(t) {
+            return new Node(t, IF);
+        },
+    
+        IF$setCondition: function(n, e) {
+            n.condition = e;
+        },
+    
+        IF$setThenPart: function(n, s) {
+            n.thenPart = s;
+        },
+    
+        IF$setElsePart: function(n, s) {
+            n.elsePart = s;
+        },
+    
+        IF$finish: function(n) {
+        },
+    
+        SWITCH$build: function(t) {
+            var n = new Node(t, SWITCH);
+            n.cases = [];
+            n.defaultIndex = -1;
+            return n;
+        },
+    
+        SWITCH$setDiscriminant: function(n, e) {
+            n.discriminant = e;
+        },
+    
+        SWITCH$setDefaultIndex: function(n, i) {
+            n.defaultIndex = i;
+        },
+    
+        SWITCH$addCase: function(n, n2) {
+            n.cases.push(n2);
+        },
+    
+        SWITCH$finish: function(n) {
+        },
+    
+        CASE$build: function(t) {
+            return new Node(t, CASE);
+        },
+    
+        CASE$setLabel: function(n, e) {
+            n.caseLabel = e;
+        },
+    
+        CASE$initializeStatements: function(n, t) {
+            n.statements = new Node(t, BLOCK);
+        },
+    
+        CASE$addStatement: function(n, s) {
+            n.statements.push(s);
+        },
+    
+        CASE$finish: function(n) {
+        },
+    
+        DEFAULT$build: function(t, p) {
+            return new Node(t, DEFAULT);
+        },
+    
+        DEFAULT$initializeStatements: function(n, t) {
+            n.statements = new Node(t, BLOCK);
+        },
+    
+        DEFAULT$addStatement: function(n, s) {
+            n.statements.push(s);
+        },
+    
+        DEFAULT$finish: function(n) {
+        },
+    
+        FOR$build: function(t) {
+            var n = new Node(t, FOR);
+            n.isLoop = true;
+            n.isEach = false;
+            return n;
+        },
+    
+        FOR$rebuildForEach: function(n) {
+            n.isEach = true;
+        },
+    
+        // NB. This function is called after rebuildForEach, if that's called
+        // at all.
+        FOR$rebuildForIn: function(n) {
+            n.type = FOR_IN;
+        },
+    
+        FOR$setCondition: function(n, e) {
+            n.condition = e;
+        },
+    
+        FOR$setSetup: function(n, e) {
+            n.setup = e || null;
+        },
+    
+        FOR$setUpdate: function(n, e) {
+            n.update = e;
+        },
+    
+        FOR$setObject: function(n, e) {
+            n.object = e;
+        },
+    
+        FOR$setIterator: function(n, e, e2) {
+            n.iterator = e;
+            n.varDecl = e2;
+        },
+    
+        FOR$setBody: function(n, s) {
+            n.body = s;
+        },
+    
+        FOR$finish: function(n) {
+        },
+    
+        WHILE$build: function(t) {
+            var n = new Node(t, WHILE);
+            n.isLoop = true;
+            return n;
+        },
+    
+        WHILE$setCondition: function(n, e) {
+            n.condition = e;
         },
-
-        setExpression: function(n, e) {
-            n.expression = e
+    
+        WHILE$setBody: function(n, s) {
+            n.body = s;
+        },
+    
+        WHILE$finish: function(n) {
+        },
+    
+        DO$build: function(t) {
+            var n = new Node(t, DO);
+            n.isLoop = true;
+            return n;
+        },
+    
+        DO$setCondition: function(n, e) {
+            n.condition = e;
+        },
+    
+        DO$setBody: function(n, s) {
+            n.body = s;
+        },
+    
+        DO$finish: function(n) {
+        },
+    
+        BREAK$build: function(t) {
+            return new Node(t, BREAK);
+        },
+    
+        BREAK$setLabel: function(n, v) {
+            n.label = v;
+        },
+    
+        BREAK$setTarget: function(n, n2) {
+            n.target = n2;
+        },
+    
+        BREAK$finish: function(n) {
+        },
+    
+        CONTINUE$build: function(t) {
+            return new Node(t, CONTINUE);
+        },
+    
+        CONTINUE$setLabel: function(n, v) {
+            n.label = v;
+        },
+    
+        CONTINUE$setTarget: function(n, n2) {
+            n.target = n2;
+        },
+    
+        CONTINUE$finish: function(n) {
+        },
+    
+        TRY$build: function(t) {
+            var n = new Node(t, TRY);
+            n.catchClauses = [];
+            return n;
+        },
+    
+        TRY$setTryBlock: function(n, s) {
+            n.tryBlock = s;
+        },
+    
+        TRY$addCatch: function(n, n2) {
+            n.catchClauses.push(n2);
+        },
+    
+        TRY$finishCatches: function(n) {
         },
-
-        setTail: function(n, n2) {
+    
+        TRY$setFinallyBlock: function(n, s) {
+            n.finallyBlock = s;
+        },
+    
+        TRY$finish: function(n) {
+        },
+    
+        CATCH$build: function(t) {
+            var n = new Node(t, CATCH);
+            n.guard = null;
+            return n;
+        },
+    
+        CATCH$setVarName: function(n, v) {
+            n.varName = v;
+        },
+    
+        CATCH$setGuard: function(n, e) {
+            n.guard = e;
+        },
+    
+        CATCH$setBlock: function(n, s) {
+            n.block = s;
+        },
+    
+        CATCH$finish: function(n) {
+        },
+    
+        THROW$build: function(t) {
+            return new Node(t, THROW);
+        },
+    
+        THROW$setException: function(n, e) {
+            n.exception = e;
+        },
+    
+        THROW$finish: function(n) {
+        },
+    
+        RETURN$build: function(t) {
+            return new Node(t, RETURN);
+        },
+    
+        RETURN$setValue: function(n, e) {
+            n.value = e;
+        },
+    
+        RETURN$finish: function(n) {
+        },
+    
+        YIELD$build: function(t) {
+            return new Node(t, YIELD);
+        },
+    
+        YIELD$setValue: function(n, e) {
+            n.value = e;
+        },
+    
+        YIELD$finish: function(n) {
+        },
+    
+        GENERATOR$build: function(t) {
+            return new Node(t, GENERATOR);
+        },
+    
+        GENERATOR$setExpression: function(n, e) {
+            n.expression = e;
+        },
+    
+        GENERATOR$setTail: function(n, n2) {
             n.tail = n2;
         },
-
-        finish: function(n) {
+    
+        GENERATOR$finish: function(n) {
+        },
+    
+        WITH$build: function(t) {
+            return new Node(t, WITH);
+        },
+    
+        WITH$setObject: function(n, e) {
+            n.object = e;
+        },
+    
+        WITH$setBody: function(n, s) {
+            n.body = s;
+        },
+    
+        WITH$finish: function(n) {
+        },
+    
+        DEBUGGER$build: function(t) {
+            return new Node(t, DEBUGGER);
+        },
+    
+        SEMICOLON$build: function(t) {
+            return new Node(t, SEMICOLON);
+        },
+    
+        SEMICOLON$setExpression: function(n, e) {
+            n.expression = e;
+        },
+    
+        SEMICOLON$finish: function(n) {
+        },
+    
+        LABEL$build: function(t) {
+            return new Node(t, LABEL);
+        },
+    
+        LABEL$setLabel: function(n, e) {
+            n.label = e;
+        },
+    
+        LABEL$setStatement: function(n, s) {
+            n.statement = s;
+        },
+    
+        LABEL$finish: function(n) {
+        },
+    
+        FUNCTION$build: function(t) {
+            var n = new Node(t);
+            if (n.type != FUNCTION)
+                n.type = (n.value == "get") ? GETTER : SETTER;
+            n.params = [];
+            return n;
+        },
+    
+        FUNCTION$setName: function(n, v) {
+            n.name = v;
+        },
+    
+        FUNCTION$addParam: function(n, v) {
+            n.params.push(v);
+        },
+    
+        FUNCTION$setBody: function(n, s) {
+            n.body = s;
+        },
+    
+        FUNCTION$hoistVars: function(x) {
+        },
+    
+        FUNCTION$finish: function(n, x) {
+        },
+    
+        VAR$build: function(t) {
+            return new Node(t, VAR);
+        },
+    
+        VAR$addDecl: function(n, n2, x) {
+            n.push(n2);
+        },
+    
+        VAR$finish: function(n) {
+        },
+    
+        CONST$build: function(t) {
+            return new Node(t, VAR);
+        },
+    
+        CONST$addDecl: function(n, n2, x) {
+            n.push(n2);
+        },
+    
+        CONST$finish: function(n) {
+        },
+    
+        LET$build: function(t) {
+            return new Node(t, LET);
+        },
+    
+        LET$addDecl: function(n, n2, x) {
+            n.push(n2);
+        },
+    
+        LET$finish: function(n) {
+        },
+    
+        DECL$build: function(t) {
+            return new Node(t, IDENTIFIER);
+        },
+    
+        DECL$setName: function(n, v) {
+            n.name = v;
+        },
+    
+        DECL$setInitializer: function(n, e) {
+            n.initializer = e;
+        },
+    
+        DECL$setReadOnly: function(n, b) {
+            n.readOnly = b;
+        },
+    
+        DECL$finish: function(n) {
+        },
+    
+        LET_BLOCK$build: function(t) {
+            var n = Node(t, LET_BLOCK);
+            n.varDecls = [];
+            return n;
+        },
+    
+        LET_BLOCK$setVariables: function(n, n2) {
+            n.variables = n2;
+        },
+    
+        LET_BLOCK$setExpression: function(n, e) {
+            n.expression = e;
+        },
+    
+        LET_BLOCK$setBlock: function(n, s) {
+            n.block = s;
+        },
+    
+        LET_BLOCK$finish: function(n) {
+        },
+    
+        BLOCK$build: function(t, id) {
+            var n = new Node(t, BLOCK);
+            n.varDecls = [];
+            n.id = id;
+            return n;
+        },
+    
+        BLOCK$hoistLets: function(n) {
+        },
+    
+        BLOCK$addStatement: function(n, n2) {
+            n.push(n2);
+        },
+    
+        BLOCK$finish: function(n) {
+        },
+    
+        EXPRESSION$build: function(t, tt) {
+            return new Node(t, tt);
+        },
+    
+        EXPRESSION$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        EXPRESSION$finish: function(n) {
+        },
+    
+        ASSIGN$build: function(t) {
+            return new Node(t, ASSIGN);
+        },
+    
+        ASSIGN$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        ASSIGN$setAssignOp: function(n, o) {
+            n.assignOp = o;
+        },
+    
+        ASSIGN$finish: function(n) {
+        },
+    
+        HOOK$build: function(t) {
+            return new Node(t, HOOK);
+        },
+    
+        HOOK$setCondition: function(n, e) {
+            n[0] = e;
+        },
+    
+        HOOK$setThenPart: function(n, n2) {
+            n[1] = n2;
+        },
+    
+        HOOK$setElsePart: function(n, n2) {
+            n[2] = n2;
+        },
+    
+        HOOK$finish: function(n) {
+        },
+    
+        OR$build: function(t) {
+            return new Node(t, OR);
+        },
+    
+        OR$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        OR$finish: function(n) {
+        },
+    
+        AND$build: function(t) {
+            return new Node(t, AND);
+        },
+    
+        AND$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        AND$finish: function(n) {
+        },
+    
+        BITWISE_OR$build: function(t) {
+            return new Node(t, BITWISE_OR);
+        },
+    
+        BITWISE_OR$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        BITWISE_OR$finish: function(n) {
+        },
+    
+        BITWISE_XOR$build: function(t) {
+            return new Node(t, BITWISE_XOR);
+        },
+    
+        BITWISE_XOR$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        BITWISE_XOR$finish: function(n) {
+        },
+    
+        BITWISE_AND$build: function(t) {
+            return new Node(t, BITWISE_AND);
+        },
+    
+        BITWISE_AND$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        BITWISE_AND$finish: function(n) {
+        },
+    
+        EQUALITY$build: function(t) {
+            // NB t.token.type must be EQ, NE, STRICT_EQ, or STRICT_NE.
+            return new Node(t);
+        },
+    
+        EQUALITY$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        EQUALITY$finish: function(n) {
+        },
+    
+        RELATIONAL$build: function(t) {
+            // NB t.token.type must be LT, LE, GE, or GT.
+            return new Node(t);
+        },
+    
+        RELATIONAL$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        RELATIONAL$finish: function(n) {
+        },
+    
+        SHIFT$build: function(t) {
+            // NB t.token.type must be LSH, RSH, or URSH.
+            return new Node(t);
+        },
+    
+        SHIFT$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        SHIFT$finish: function(n) {
+        },
+    
+        ADD$build: function(t) {
+            // NB t.token.type must be PLUS or MINUS.
+            return new Node(t);
+        },
+    
+        ADD$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        ADD$finish: function(n) {
+        },
+    
+        MULTIPLY$build: function(t) {
+            // NB t.token.type must be MUL, DIV, or MOD.
+            return new Node(t);
+        },
+    
+        MULTIPLY$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        MULTIPLY$finish: function(n) {
+        },
+    
+        UNARY$build: function(t) {
+            // NB t.token.type must be DELETE, VOID, TYPEOF, NOT, BITWISE_NOT,
+            // UNARY_PLUS, UNARY_MINUS, INCREMENT, or DECREMENT.
+            if (t.token.type == PLUS)
+                t.token.type = UNARY_PLUS;
+            else if (t.token.type == MINUS)
+                t.token.type = UNARY_MINUS;
+            return new Node(t);
+        },
+    
+        UNARY$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        UNARY$setPostfix: function(n) {
+            n.postfix = true;
+        },
+    
+        UNARY$finish: function(n) {
+        },
+    
+        MEMBER$build: function(t, tt) {
+            // NB t.token.type must be NEW, DOT, or INDEX.
+            return new Node(t, tt);
+        },
+    
+        MEMBER$rebuildNewWithArgs: function(n) {
+            n.type = NEW_WITH_ARGS;
+        },
+    
+        MEMBER$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        MEMBER$finish: function(n) {
+        },
+    
+        PRIMARY$build: function(t, tt) {
+            // NB t.token.type must be NULL, THIS, TRUIE, FALSE, IDENTIFIER,
+            // NUMBER, STRING, or REGEXP.
+            return new Node(t, tt);
+        },
+    
+        PRIMARY$finish: function(n) {
+        },
+    
+        ARRAY_INIT$build: function(t) {
+            return new Node(t, ARRAY_INIT);
+        },
+    
+        ARRAY_INIT$addElement: function(n, n2) {
+            n.push(n2);
+        },
+    
+        ARRAY_INIT$finish: function(n) {
+        },
+    
+        ARRAY_COMP: {
+            build: function(t) {
+                return new Node(t, ARRAY_COMP);
+            },
+    
+            setExpression: function(n, e) {
+                n.expression = e
+            },
+    
+            setTail: function(n, n2) {
+                n.tail = n2;
+            },
+    
+            finish: function(n) {
+            }
+        },
+    
+        COMP_TAIL$build: function(t) {
+            return new Node(t, COMP_TAIL);
+        },
+    
+        COMP_TAIL$setGuard: function(n, e) {
+            n.guard = e;
+        },
+    
+        COMP_TAIL$addFor: function(n, n2) {
+            n.push(n2);
+        },
+    
+        COMP_TAIL$finish: function(n) {
+        },
+    
+        OBJECT_INIT$build: function(t) {
+            return new Node(t, OBJECT_INIT);
+        },
+    
+        OBJECT_INIT$addProperty: function(n, n2) {
+            n.push(n2);
+        },
+    
+        OBJECT_INIT$finish: function(n) {
+        },
+    
+        PROPERTY_INIT$build: function(t) {
+            return new Node(t, PROPERTY_INIT);
+        },
+    
+        PROPERTY_INIT$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        PROPERTY_INIT$finish: function(n) {
+        },
+    
+        COMMA$build: function(t) {
+            return new Node(t, COMMA);
+        },
+    
+        COMMA$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        COMMA$finish: function(n) {
+        },
+    
+        LIST$build: function(t) {
+            return new Node(t, LIST);
+        },
+    
+        LIST$addOperand: function(n, n2) {
+            n.push(n2);
+        },
+    
+        LIST$finish: function(n) {
+        },
+    
+        setHoists: function(id, vds) {
         }
-    },
-
-    COMP_TAIL$build: function(t) {
-        return new Node(t, COMP_TAIL);
-    },
-
-    COMP_TAIL$setGuard: function(n, e) {
-        n.guard = e;
-    },
-
-    COMP_TAIL$addFor: function(n, n2) {
-        n.push(n2);
-    },
-
-    COMP_TAIL$finish: function(n) {
-    },
-
-    OBJECT_INIT$build: function(t) {
-        return new Node(t, OBJECT_INIT);
-    },
-
-    OBJECT_INIT$addProperty: function(n, n2) {
-        n.push(n2);
-    },
-
-    OBJECT_INIT$finish: function(n) {
-    },
-
-    PROPERTY_INIT$build: function(t) {
-        return new Node(t, PROPERTY_INIT);
-    },
-
-    PROPERTY_INIT$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    PROPERTY_INIT$finish: function(n) {
-    },
-
-    COMMA$build: function(t) {
-        return new Node(t, COMMA);
-    },
-
-    COMMA$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    COMMA$finish: function(n) {
-    },
-
-    LIST$build: function(t) {
-        return new Node(t, LIST);
-    },
-
-    LIST$addOperand: function(n, n2) {
-        n.push(n2);
-    },
-
-    LIST$finish: function(n) {
-    },
-
-    setHoists: function(id, vds) {
-    }
-};
-
-function CompilerContext(inFunction, builder) {
-    this.inFunction = inFunction;
-    this.hasEmptyReturn = false;
-    this.hasReturnWithValue = false;
-    this.isGenerator = false;
-    this.blockId = 0;
-    this.builder = builder;
-    this.stmtStack = [];
-    this.funDecls = [];
-    this.varDecls = [];
-}
-
-CompilerContext.prototype = {
-    bracketLevel: 0,
-    curlyLevel: 0,
-    parenLevel: 0,
-    hookLevel: 0,
-    ecma3OnlyMode: false,
-    inForLoopInit: false,
-};
-
-/*
- * Script :: (tokenizer, compiler context) -> node
- *
- * Parses the toplevel and function bodies.
- */
-function Script(t, x) {
-    var n = Statements(t, x);
-    n.type = SCRIPT;
-    n.funDecls = x.funDecls;
-    n.varDecls = x.varDecls;
-    return n;
-}
-
-// Node extends Array, which we extend slightly with a top-of-stack method.
-defineProperty(Array.prototype, "top",
-               function() {
-                   return this.length && this[this.length-1];
-               }, false, false, true);
-
-/*
- * Node :: (tokenizer, optional type) -> node
- */
-function Node(t, type) {
-    var token = t.token;
-    if (token) {
-        this.type = type || token.type;
-        this.value = token.value;
-        this.lineno = token.lineno;
-        // Start & end are file positions for error handling.
-        this.start = token.start;
-        this.end = token.end;
-    } else {
-        this.type = type;
-        this.lineno = t.lineno;
+    };
+    
+    function CompilerContext(inFunction, builder) {
+        this.inFunction = inFunction;
+        this.hasEmptyReturn = false;
+        this.hasReturnWithValue = false;
+        this.isGenerator = false;
+        this.blockId = 0;
+        this.builder = builder;
+        this.stmtStack = [];
+        this.funDecls = [];
+        this.varDecls = [];
     }
-    // Nodes use a tokenizer for debugging (getSource, filename getter).
-    this.tokenizer = t;
-
-    for (var i = 2; i < arguments.length; i++)
-        this.push(arguments[i]);
-}
-
-var Np = Node.prototype = new Array;
-Np.constructor = Node;
-Np.toSource = Object.prototype.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;
+    
+    CompilerContext.prototype = {
+        bracketLevel: 0,
+        curlyLevel: 0,
+        parenLevel: 0,
+        hookLevel: 0,
+        ecma3OnlyMode: false,
+        inForLoopInit: false,
+    };
+    
+    /*
+     * Script :: (tokenizer, compiler context) -> node
+     *
+     * Parses the toplevel and function bodies.
+     */
+    function Script(t, x) {
+        var n = Statements(t, x);
+        n.type = SCRIPT;
+        n.funDecls = x.funDecls;
+        n.varDecls = x.varDecls;
+        return n;
     }
-    return Array.prototype.push.call(this, kid);
-}
-
-Node.indentLevel = 0;
-
-function tokenstr(tt) {
-    var t = tokens[tt];
-    return /^\W/.test(t) ? 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]});
+    
+    // Node extends Array, which we extend slightly with a top-of-stack method.
+    jsdefs.defineProperty(Array.prototype, "top",
+                   function() {
+                       return this.length && this[this.length-1];
+                   }, false, false, true);
+    
+    /*
+     * Node :: (tokenizer, optional type) -> node
+     */
+    function Node(t, type) {
+        var token = t.token;
+        if (token) {
+            this.type = type || token.type;
+            this.value = token.value;
+            this.lineno = token.lineno;
+            // Start & end are file positions for error handling.
+            this.start = token.start;
+            this.end = token.end;
+        } else {
+            this.type = type;
+            this.lineno = t.lineno;
+        }
+        // Nodes use a tokenizer for debugging (getSource, filename getter).
+        this.tokenizer = t;
+    
+        for (var i = 2; i < arguments.length; i++)
+            this.push(arguments[i]);
+    }
+    
+    var Np = Node.prototype = new Array;
+    Np.constructor = Node;
+    Np.toSource = Object.prototype.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 Array.prototype.push.call(this, kid);
     }
-    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: " + tokenstr(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);
-};
-
-defineGetter(Np, "filename",
-             function() {
-                 return this.tokenizer.filename;
-             });
-
-defineProperty(String.prototype, "repeat",
-               function(n) {
-                   var s = "", t = this + s;
-                   while (--n >= 0)
-                       s += t;
-                   return s;
-               }, false, false, true);
-
-// Statement stack and nested statement handler.
-function nest(t, x, node, func, end) {
-    x.stmtStack.push(node);
-    var n = func(t, x);
-    x.stmtStack.pop();
-    end && t.mustMatch(end);
-    return n;
-}
-
-/*
- * Statements :: (tokenizer, compiler context) -> node
- *
- * Parses a list of Statements.
- */
-function Statements(t, x) {
-    var b = x.builder;
-    var n = b.BLOCK$build(t, x.blockId++);
-    b.BLOCK$hoistLets(n);
-    x.stmtStack.push(n);
-    while (!t.done && t.peek(true) != RIGHT_CURLY)
-        b.BLOCK$addStatement(n, Statement(t, x));
-    x.stmtStack.pop();
-    b.BLOCK$finish(n);
-    if (n.needsHoisting) {
-        b.setHoists(n.id, n.varDecls);
-        // Propagate up to the function.
-        x.needsHoisting = true;
+    
+    Node.indentLevel = 0;
+    
+    function tokenstr(tt) {
+        var t = jsdefs.tokens[tt];
+        return /^\W/.test(t) ? jsdefs.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: " + tokenstr(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;
     }
-    return n;
-}
-
-function Block(t, x) {
-    t.mustMatch(LEFT_CURLY);
-    var n = Statements(t, x);
-    t.mustMatch(RIGHT_CURLY);
-    return n;
-}
-
-const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
-
-/*
- * Statement :: (tokenizer, compiler context) -> node
- *
- * Parses a Statement.
- */
-function Statement(t, x) {
-    var i, label, n, n2, ss, tt = t.get(true);
-    var b = x.builder;
-
-    // Cases for statements ending in a right curly return early, avoiding the
-    // common semicolon insertion magic after this switch.
-    switch (tt) {
-      case FUNCTION:
-        // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't.
-        return FunctionDefinition(t, x, true,
-                                  (x.stmtStack.length > 1)
-                                  ? STATEMENT_FORM
-                                  : DECLARED_FORM);
-
-      case LEFT_CURLY:
-        n = Statements(t, x);
+    
+    Np.getSource = function () {
+        return this.tokenizer.source.slice(this.start, this.end);
+    };
+    
+    jsdefs.defineGetter(Np, "filename",
+                 function() {
+                     return this.tokenizer.filename;
+                 });
+    
+    jsdefs.defineProperty(String.prototype, "repeat",
+                   function(n) {
+                       var s = "", t = this + s;
+                       while (--n >= 0)
+                           s += t;
+                       return s;
+                   }, false, false, true);
+    
+    // Statement stack and nested statement handler.
+    function nest(t, x, node, func, end) {
+        x.stmtStack.push(node);
+        var n = func(t, x);
+        x.stmtStack.pop();
+        end && t.mustMatch(end);
+        return n;
+    }
+    
+    /*
+     * Statements :: (tokenizer, compiler context) -> node
+     *
+     * Parses a list of Statements.
+     */
+    function Statements(t, x) {
+        var b = x.builder;
+        var n = b.BLOCK$build(t, x.blockId++);
+        b.BLOCK$hoistLets(n);
+        x.stmtStack.push(n);
+        while (!t.done && t.peek(true) != RIGHT_CURLY)
+            b.BLOCK$addStatement(n, Statement(t, x));
+        x.stmtStack.pop();
+        b.BLOCK$finish(n);
+        if (n.needsHoisting) {
+            b.setHoists(n.id, n.varDecls);
+            // Propagate up to the function.
+            x.needsHoisting = true;
+        }
+        return n;
+    }
+    
+    function Block(t, x) {
+        t.mustMatch(LEFT_CURLY);
+        var n = Statements(t, x);
         t.mustMatch(RIGHT_CURLY);
         return n;
-
-      case IF:
-        n = b.IF$build(t);
-        b.IF$setCondition(n, ParenExpression(t, x));
-        x.stmtStack.push(n);
-        b.IF$setThenPart(n, Statement(t, x));
-        if (t.match(ELSE))
-            b.IF$setElsePart(n, Statement(t, x));
-        x.stmtStack.pop();
-        b.IF$finish(n);
-        return n;
-
-      case SWITCH:
-        // This allows CASEs after a DEFAULT, which is in the standard.
-        n = b.SWITCH$build(t);
-        b.SWITCH$setDiscriminant(n, ParenExpression(t, x));
-        x.stmtStack.push(n);
-        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");
-                n2 = b.DEFAULT$build(t);
-                b.SWITCH$setDefaultIndex(n, n.cases.length);
-                t.mustMatch(COLON);
-                b.DEFAULT$initializeStatements(n2, t);
-                while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
-                       tt != RIGHT_CURLY)
-                    b.DEFAULT$addStatement(n2, Statement(t, x));
-                b.DEFAULT$finish(n2);
-                break;
-
-              case CASE:
-                n2 = b.CASE$build(t);
-                b.CASE$setLabel(n2, Expression(t, x, COLON));
-                t.mustMatch(COLON);
-                b.CASE$initializeStatements(n2, t);
-                while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
-                       tt != RIGHT_CURLY)
-                    b.CASE$addStatement(n2, Statement(t, x));
-                b.CASE$finish(n2);
-                break;
-
-              default:
-                throw t.newSyntaxError("Invalid switch case");
+    }
+    
+    const DECLARED_FORM = 0, EXPRESSED_FORM = 1, STATEMENT_FORM = 2;
+    
+    /*
+     * Statement :: (tokenizer, compiler context) -> node
+     *
+     * Parses a Statement.
+     */
+    function Statement(t, x) {
+        var i, label, n, n2, ss, tt = t.get(true);
+        var b = x.builder;
+    
+        // Cases for statements ending in a right curly return early, avoiding the
+        // common semicolon insertion magic after this switch.
+        switch (tt) {
+          case FUNCTION:
+            // DECLARED_FORM extends funDecls of x, STATEMENT_FORM doesn't.
+            return FunctionDefinition(t, x, true,
+                                      (x.stmtStack.length > 1)
+                                      ? STATEMENT_FORM
+                                      : DECLARED_FORM);
+    
+          case LEFT_CURLY:
+            n = Statements(t, x);
+            t.mustMatch(RIGHT_CURLY);
+            return n;
+    
+          case IF:
+            n = b.IF$build(t);
+            b.IF$setCondition(n, ParenExpression(t, x));
+            x.stmtStack.push(n);
+            b.IF$setThenPart(n, Statement(t, x));
+            if (t.match(ELSE))
+                b.IF$setElsePart(n, Statement(t, x));
+            x.stmtStack.pop();
+            b.IF$finish(n);
+            return n;
+    
+          case SWITCH:
+            // This allows CASEs after a DEFAULT, which is in the standard.
+            n = b.SWITCH$build(t);
+            b.SWITCH$setDiscriminant(n, ParenExpression(t, x));
+            x.stmtStack.push(n);
+            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");
+                    n2 = b.DEFAULT$build(t);
+                    b.SWITCH$setDefaultIndex(n, n.cases.length);
+                    t.mustMatch(COLON);
+                    b.DEFAULT$initializeStatements(n2, t);
+                    while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
+                           tt != RIGHT_CURLY)
+                        b.DEFAULT$addStatement(n2, Statement(t, x));
+                    b.DEFAULT$finish(n2);
+                    break;
+    
+                  case CASE:
+                    n2 = b.CASE$build(t);
+                    b.CASE$setLabel(n2, Expression(t, x, COLON));
+                    t.mustMatch(COLON);
+                    b.CASE$initializeStatements(n2, t);
+                    while ((tt=t.peek(true)) != CASE && tt != DEFAULT &&
+                           tt != RIGHT_CURLY)
+                        b.CASE$addStatement(n2, Statement(t, x));
+                    b.CASE$finish(n2);
+                    break;
+    
+                  default:
+                    throw t.newSyntaxError("Invalid switch case");
+                }
+                b.SWITCH$addCase(n, n2);
             }
-            b.SWITCH$addCase(n, n2);
-        }
-        x.stmtStack.pop();
-        b.SWITCH$finish(n);
-        return n;
-
-      case FOR:
-        n = b.FOR$build(t);
-        if (t.match(IDENTIFIER) && t.token.value == "each")
-            b.FOR$rebuildForEach(n);
-        t.mustMatch(LEFT_PAREN);
-        if ((tt = t.peek()) != SEMICOLON) {
-            x.inForLoopInit = true;
-            if (tt == VAR || tt == CONST) {
-                t.get();
-                n2 = Variables(t, x);
-            } else if (tt == LET) {
-                t.get();
-                if (t.peek() == LEFT_PAREN) {
-                    n2 = LetBlock(t, x, false);
+            x.stmtStack.pop();
+            b.SWITCH$finish(n);
+            return n;
+    
+          case FOR:
+            n = b.FOR$build(t);
+            if (t.match(IDENTIFIER) && t.token.value == "each")
+                b.FOR$rebuildForEach(n);
+            t.mustMatch(LEFT_PAREN);
+            if ((tt = t.peek()) != SEMICOLON) {
+                x.inForLoopInit = true;
+                if (tt == VAR || tt == CONST) {
+                    t.get();
+                    n2 = Variables(t, x);
+                } else if (tt == LET) {
+                    t.get();
+                    if (t.peek() == LEFT_PAREN) {
+                        n2 = LetBlock(t, x, false);
+                    } else {
+                        /*
+                         * Let in for head, we need to add an implicit block
+                         * around the rest of the for.
+                         */
+                        var forBlock = b.BLOCK$build(t, x.blockId++);
+                        x.stmtStack.push(forBlock);
+                        n2 = Variables(t, x, forBlock);
+                    }
                 } else {
-                    /*
-                     * Let in for head, we need to add an implicit block
-                     * around the rest of the for.
-                     */
-                    var forBlock = b.BLOCK$build(t, x.blockId++);
-                    x.stmtStack.push(forBlock);
-                    n2 = Variables(t, x, forBlock);
+                    n2 = Expression(t, x);
+                }
+                x.inForLoopInit = false;
+            }
+            if (n2 && t.match(IN)) {
+                b.FOR$rebuildForIn(n);
+                b.FOR$setObject(n, Expression(t, x), forBlock);
+                if (n2.type == VAR || n2.type == LET) {
+                    if (n2.length != 1) {
+                        throw new SyntaxError("Invalid for..in left-hand side",
+                                              t.filename, n2.lineno);
+                    }
+                    b.FOR$setIterator(n, n2[0], n2, forBlock);
+                } else {
+                    b.FOR$setIterator(n, n2, null, forBlock);
                 }
             } else {
-                n2 = Expression(t, x);
-            }
-            x.inForLoopInit = false;
-        }
-        if (n2 && t.match(IN)) {
-            b.FOR$rebuildForIn(n);
-            b.FOR$setObject(n, Expression(t, x), forBlock);
-            if (n2.type == VAR || n2.type == LET) {
-                if (n2.length != 1) {
-                    throw new SyntaxError("Invalid for..in left-hand side",
-                                          t.filename, n2.lineno);
-                }
-                b.FOR$setIterator(n, n2[0], n2, forBlock);
-            } else {
-                b.FOR$setIterator(n, n2, null, forBlock);
-            }
-        } else {
-            b.FOR$setSetup(n, n2);
-            t.mustMatch(SEMICOLON);
-            if (n.isEach)
-                throw t.newSyntaxError("Invalid for each..in loop");
-            b.FOR$setCondition(n, (t.peek() == SEMICOLON)
-                              ? null
-                              : Expression(t, x));
-            t.mustMatch(SEMICOLON);
-            b.FOR$setUpdate(n, (t.peek() == RIGHT_PAREN)
-                               ? null
-                               : Expression(t, x));
-        }
-        t.mustMatch(RIGHT_PAREN);
-        b.FOR$setBody(n, nest(t, x, n, Statement));
-        if (forBlock) {
-            b.BLOCK$finish(forBlock);
-            x.stmtStack.pop();
-        }
-        b.FOR$finish(n);
-        return n;
-
-      case WHILE:
-        n = b.WHILE$build(t);
-        b.WHILE$setCondition(n, ParenExpression(t, x));
-        b.WHILE$setBody(n, nest(t, x, n, Statement));
-        b.WHILE$finish(n);
-        return n;
-
-      case DO:
-        n = b.DO$build(t);
-        b.DO$setBody(n, nest(t, x, n, Statement, WHILE));
-        b.DO$setCondition(n, ParenExpression(t, x));
-        b.DO$finish(n);
-        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 = tt == BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t);
-
-        if (t.peekOnSameLine() == IDENTIFIER) {
-            t.get();
-            if (tt == BREAK)
-                b.BREAK$setLabel(n, t.token.value);
-            else
-                b.CONTINUE$setLabel(n, t.token.value);
-        }
-
-        ss = x.stmtStack;
-        i = ss.length;
-        label = n.label;
-
-        if (label) {
-            do {
-                if (--i < 0)
-                    throw t.newSyntaxError("Label not found");
-            } while (ss[i].label != label);
-
-            /*
-             * Both break and continue to label need to be handled specially
-             * within a labeled loop, so that they target that loop. If not in
-             * a loop, then break targets its labeled statement. Labels can be
-             * nested so we skip all labels immediately enclosing the nearest
-             * non-label statement.
-             */
-            while (i < ss.length - 1 && ss[i+1].type == LABEL)
-                i++;
-            if (i < ss.length - 1 && ss[i+1].isLoop)
-                i++;
-            else if (tt == CONTINUE)
-                throw t.newSyntaxError("Invalid continue");
-        } else {
-            do {
-                if (--i < 0) {
-                    throw t.newSyntaxError("Invalid " + ((tt == BREAK)
-                                                         ? "break"
-                                                         : "continue"));
-                }
-            } while (!ss[i].isLoop && !(tt == BREAK && ss[i].type == SWITCH));
-        }
-        if (tt == BREAK) {
-            b.BREAK$setTarget(n, ss[i]);
-            b.BREAK$finish(n);
-        } else {
-            b.CONTINUE$setTarget(n, ss[i]);
-            b.CONTINUE$finish(n);
-        }
-        break;
-
-      case TRY:
-        n = b.TRY$build(t);
-        b.TRY$setTryBlock(n, Block(t, x));
-        while (t.match(CATCH)) {
-            n2 = b.CATCH$build(t);
-            t.mustMatch(LEFT_PAREN);
-            switch (t.get()) {
-              case LEFT_BRACKET:
-              case LEFT_CURLY:
-                // Destructured catch identifiers.
-                t.unget();
-                b.CATCH$setVarName(n2, DestructuringExpression(t, x, true));
-              case IDENTIFIER:
-                b.CATCH$setVarName(n2, 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");
-                b.CATCH$setGuard(n2, Expression(t, x));
-            } else {
-                b.CATCH$setGuard(n2, null);
+                b.FOR$setSetup(n, n2);
+                t.mustMatch(SEMICOLON);
+                if (n.isEach)
+                    throw t.newSyntaxError("Invalid for each..in loop");
+                b.FOR$setCondition(n, (t.peek() == SEMICOLON)
+                                  ? null
+                                  : Expression(t, x));
+                t.mustMatch(SEMICOLON);
+                b.FOR$setUpdate(n, (t.peek() == RIGHT_PAREN)
+                                   ? null
+                                   : Expression(t, x));
             }
             t.mustMatch(RIGHT_PAREN);
-            b.CATCH$setBlock(n2, Block(t, x));
-            b.CATCH$finish(n2);
-            b.TRY$addCatch(n, n2);
+            b.FOR$setBody(n, nest(t, x, n, Statement));
+            if (forBlock) {
+                b.BLOCK$finish(forBlock);
+                x.stmtStack.pop();
+            }
+            b.FOR$finish(n);
+            return n;
+    
+          case WHILE:
+            n = b.WHILE$build(t);
+            b.WHILE$setCondition(n, ParenExpression(t, x));
+            b.WHILE$setBody(n, nest(t, x, n, Statement));
+            b.WHILE$finish(n);
+            return n;
+    
+          case DO:
+            n = b.DO$build(t);
+            b.DO$setBody(n, nest(t, x, n, Statement, WHILE));
+            b.DO$setCondition(n, ParenExpression(t, x));
+            b.DO$finish(n);
+            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 = tt == BREAK ? b.BREAK$build(t) : b.CONTINUE$build(t);
+    
+            if (t.peekOnSameLine() == IDENTIFIER) {
+                t.get();
+                if (tt == BREAK)
+                    b.BREAK$setLabel(n, t.token.value);
+                else
+                    b.CONTINUE$setLabel(n, t.token.value);
+            }
+    
+            ss = x.stmtStack;
+            i = ss.length;
+            label = n.label;
+    
+            if (label) {
+                do {
+                    if (--i < 0)
+                        throw t.newSyntaxError("Label not found");
+                } while (ss[i].label != label);
+    
+                /*
+                 * Both break and continue to label need to be handled specially
+                 * within a labeled loop, so that they target that loop. If not in
+                 * a loop, then break targets its labeled statement. Labels can be
+                 * nested so we skip all labels immediately enclosing the nearest
+                 * non-label statement.
+                 */
+                while (i < ss.length - 1 && ss[i+1].type == LABEL)
+                    i++;
+                if (i < ss.length - 1 && ss[i+1].isLoop)
+                    i++;
+                else if (tt == CONTINUE)
+                    throw t.newSyntaxError("Invalid continue");
+            } else {
+                do {
+                    if (--i < 0) {
+                        throw t.newSyntaxError("Invalid " + ((tt == BREAK)
+                                                             ? "break"
+                                                             : "continue"));
+                    }
+                } while (!ss[i].isLoop && !(tt == BREAK && ss[i].type == SWITCH));
+            }
+            if (tt == BREAK) {
+                b.BREAK$setTarget(n, ss[i]);
+                b.BREAK$finish(n);
+            } else {
+                b.CONTINUE$setTarget(n, ss[i]);
+                b.CONTINUE$finish(n);
+            }
+            break;
+    
+          case TRY:
+            n = b.TRY$build(t);
+            b.TRY$setTryBlock(n, Block(t, x));
+            while (t.match(CATCH)) {
+                n2 = b.CATCH$build(t);
+                t.mustMatch(LEFT_PAREN);
+                switch (t.get()) {
+                  case LEFT_BRACKET:
+                  case LEFT_CURLY:
+                    // Destructured catch identifiers.
+                    t.unget();
+                    b.CATCH$setVarName(n2, DestructuringExpression(t, x, true));
+                  case IDENTIFIER:
+                    b.CATCH$setVarName(n2, 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");
+                    b.CATCH$setGuard(n2, Expression(t, x));
+                } else {
+                    b.CATCH$setGuard(n2, null);
+                }
+                t.mustMatch(RIGHT_PAREN);
+                b.CATCH$setBlock(n2, Block(t, x));
+                b.CATCH$finish(n2);
+                b.TRY$addCatch(n, n2);
+            }
+            b.TRY$finishCatches(n);
+            if (t.match(FINALLY))
+                b.TRY$setFinallyBlock(n, Block(t, x));
+            if (!n.catchClauses.length && !n.finallyBlock)
+                throw t.newSyntaxError("Invalid try statement");
+            b.TRY$finish(n);
+            return n;
+    
+          case CATCH:
+          case FINALLY:
+            throw t.newSyntaxError(jsdefs.tokens[tt] + " without preceding try");
+    
+          case THROW:
+            n = b.THROW$build(t);
+            b.THROW$setException(n, Expression(t, x));
+            b.THROW$finish(n);
+            break;
+    
+          case RETURN:
+            n = returnOrYield(t, x);
+            break;
+    
+          case WITH:
+            n = b.WITH$build(t);
+            b.WITH$setObject(n, ParenExpression(t, x));
+            b.WITH$setBody(n, nest(t, x, n, Statement));
+            b.WITH$finish(n);
+            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 = b.DEBUGGER$build(t);
+            break;
+    
+          case NEWLINE:
+          case SEMICOLON:
+            n = b.SEMICOLON$build(t);
+            b.SEMICOLON$setExpression(n, null);
+            b.SEMICOLON$finish(t);
+            return n;
+    
+          default:
+            if (tt == IDENTIFIER) {
+                tt = t.peek();
+                // Labeled statement.
+                if (tt == COLON) {
+                    label = t.token.value;
+                    ss = x.stmtStack;
+                    for (i = ss.length-1; i >= 0; --i) {
+                        if (ss[i].label == label)
+                            throw t.newSyntaxError("Duplicate label");
+                    }
+                    t.get();
+                    n = b.LABEL$build(t);
+                    b.LABEL$setLabel(n, label)
+                    b.LABEL$setStatement(n, nest(t, x, n, Statement));
+                    b.LABEL$finish(n);
+                    return n;
+                }
+            }
+    
+            // Expression statement.
+            // We unget the current token to parse the expression as a whole.
+            n = b.SEMICOLON$build(t);
+            t.unget();
+            b.SEMICOLON$setExpression(n, Expression(t, x));
+            n.end = n.expression.end;
+            b.SEMICOLON$finish(n);
+            break;
         }
-        b.TRY$finishCatches(n);
-        if (t.match(FINALLY))
-            b.TRY$setFinallyBlock(n, Block(t, x));
-        if (!n.catchClauses.length && !n.finallyBlock)
-            throw t.newSyntaxError("Invalid try statement");
-        b.TRY$finish(n);
-        return n;
-
-      case CATCH:
-      case FINALLY:
-        throw t.newSyntaxError(tokens[tt] + " without preceding try");
-
-      case THROW:
-        n = b.THROW$build(t);
-        b.THROW$setException(n, Expression(t, x));
-        b.THROW$finish(n);
-        break;
-
-      case RETURN:
-        n = returnOrYield(t, x);
-        break;
-
-      case WITH:
-        n = b.WITH$build(t);
-        b.WITH$setObject(n, ParenExpression(t, x));
-        b.WITH$setBody(n, nest(t, x, n, Statement));
-        b.WITH$finish(n);
+    
+        MagicalSemicolon(t);
         return n;
-
-      case VAR:
-      case CONST:
-        n = Variables(t, x);
-        break;
-
-      case LET:
-        if (t.peek() == LEFT_PAREN)
-            n = LetBlock(t, x, true);
+    }
+    
+    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);
+    }
+    
+    function returnOrYield(t, x) {
+        var n, b = x.builder, tt = t.token.type, tt2;
+    
+        if (tt == RETURN) {
+            if (!x.inFunction)
+                throw t.newSyntaxError("Return not in function");
+            n = b.RETURN$build(t);
+        } else /* (tt == YIELD) */ {
+            if (!x.inFunction)
+                throw t.newSyntaxError("Yield not in function");
+            x.isGenerator = true;
+            n = b.YIELD$build(t);
+        }
+    
+        tt2 = 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) {
+                b.RETURN$setValue(n, Expression(t, x));
+                x.hasReturnWithValue = true;
+            } else {
+                b.YIELD$setValue(n, AssignExpression(t, x));
+            }
+        } else if (tt == RETURN) {
+            x.hasEmptyReturn = true;
+        }
+    
+        // Disallow return v; in generator.
+        if (x.hasReturnWithValue && x.isGenerator)
+            throw t.newSyntaxError("Generator returns a value");
+    
+        if (tt == RETURN)
+            b.RETURN$finish(n);
         else
-            n = Variables(t, x);
-        break;
-
-      case DEBUGGER:
-        n = b.DEBUGGER$build(t);
-        break;
-
-      case NEWLINE:
-      case SEMICOLON:
-        n = b.SEMICOLON$build(t);
-        b.SEMICOLON$setExpression(n, null);
-        b.SEMICOLON$finish(t);
+            b.YIELD$finish(n);
+    
         return n;
-
-      default:
-        if (tt == IDENTIFIER) {
-            tt = t.peek();
-            // Labeled statement.
-            if (tt == COLON) {
-                label = t.token.value;
-                ss = x.stmtStack;
-                for (i = ss.length-1; i >= 0; --i) {
-                    if (ss[i].label == label)
-                        throw t.newSyntaxError("Duplicate label");
+    }
+    
+    /*
+     * FunctionDefinition :: (tokenizer, compiler context, boolean,
+     *                        DECLARED_FORM or EXPRESSED_FORM or STATEMENT_FORM)
+     *                    -> node
+     */
+    function FunctionDefinition(t, x, requireName, functionForm) {
+        var b = x.builder;
+        var f = b.FUNCTION$build(t);
+        if (t.match(IDENTIFIER))
+            b.FUNCTION$setName(f, t.token.value);
+        else if (requireName)
+            throw t.newSyntaxError("missing function identifier");
+    
+        t.mustMatch(LEFT_PAREN);
+        if (!t.match(RIGHT_PAREN)) {
+            do {
+                switch (t.get()) {
+                  case LEFT_BRACKET:
+                  case LEFT_CURLY:
+                    // Destructured formal parameters.
+                    t.unget();
+                    b.FUNCTION$addParam(f, DestructuringExpression(t, x));
+                    break;
+                  case IDENTIFIER:
+                    b.FUNCTION$addParam(f, t.token.value);
+                    break;
+                  default:
+                    throw t.newSyntaxError("missing formal parameter");
+                    break;
                 }
-                t.get();
-                n = b.LABEL$build(t);
-                b.LABEL$setLabel(n, label)
-                b.LABEL$setStatement(n, nest(t, x, n, Statement));
-                b.LABEL$finish(n);
-                return n;
+            } while (t.match(COMMA));
+            t.mustMatch(RIGHT_PAREN);
+        }
+    
+        // Do we have an expression closure or a normal body?
+        var tt = t.get();
+        if (tt != LEFT_CURLY)
+            t.unget();
+    
+        var x2 = new CompilerContext(true, b);
+        var rp = t.save();
+        if (x.inFunction) {
+            /*
+             * Inner functions don't reset block numbering. They also need to
+             * remember which block they were parsed in for hoisting (see comment
+             * below).
+             */
+            x2.blockId = x.blockId;
+        }
+    
+        if (tt != LEFT_CURLY) {
+            b.FUNCTION$setBody(f, AssignExpression(t, x));
+            if (x.isGenerator)
+                throw t.newSyntaxError("Generator returns a value");
+        } else {
+            b.FUNCTION$hoistVars(x2.blockId);
+            b.FUNCTION$setBody(f, Script(t, x2));
+        }
+    
+        /*
+         * To linearize hoisting with nested blocks needing hoists, if a toplevel
+         * function has any hoists we reparse the entire thing. Each toplevel
+         * function is parsed at most twice.
+         *
+         * Pass 1: If there needs to be hoisting at any child block or inner
+         * function, the entire function gets reparsed.
+         *
+         * Pass 2: It's possible that hoisting has changed the upvars of
+         * functions. That is, consider:
+         *
+         * function f() {
+         *   x = 0;
+         *   g();
+         *   x; // x's forward pointer should be invalidated!
+         *   function g() {
+         *     x = 'g';
+         *   }
+         *   var x;
+         * }
+         *
+         * So, a function needs to remember in which block it is parsed under
+         * (since the function body is _not_ hoisted, only the declaration) and
+         * upon hoisting, needs to recalculate all its upvars up front.
+         */
+        if (x2.needsHoisting) {
+            // Order is important here! funDecls must come _after_ varDecls!
+            b.setHoists(f.body.id, x2.varDecls.concat(x2.funDecls));
+    
+            if (x.inFunction) {
+                // Propagate up to the parent function if we're an inner function.
+                x.needsHoisting = true;
+            } else {
+                // Only re-parse toplevel functions.
+                var x3 = x2;
+                x2 = new CompilerContext(true, b);
+                t.rewind(rp);
+                // Set a flag in case the builder wants to have different behavior
+                // on the second pass.
+                b.secondPass = true;
+                b.FUNCTION$hoistVars(f.body.id, true);
+                b.FUNCTION$setBody(f, Script(t, x2));
+                b.secondPass = false;
             }
         }
-
-        // Expression statement.
-        // We unget the current token to parse the expression as a whole.
-        n = b.SEMICOLON$build(t);
-        t.unget();
-        b.SEMICOLON$setExpression(n, Expression(t, x));
-        n.end = n.expression.end;
-        b.SEMICOLON$finish(n);
-        break;
-    }
-
-    MagicalSemicolon(t);
-    return n;
-}
-
-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);
-}
-
-function returnOrYield(t, x) {
-    var n, b = x.builder, tt = t.token.type, tt2;
-
-    if (tt == RETURN) {
-        if (!x.inFunction)
-            throw t.newSyntaxError("Return not in function");
-        n = b.RETURN$build(t);
-    } else /* (tt == YIELD) */ {
-        if (!x.inFunction)
-            throw t.newSyntaxError("Yield not in function");
-        x.isGenerator = true;
-        n = b.YIELD$build(t);
-    }
-
-    tt2 = 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) {
-            b.RETURN$setValue(n, Expression(t, x));
-            x.hasReturnWithValue = true;
-        } else {
-            b.YIELD$setValue(n, AssignExpression(t, x));
-        }
-    } else if (tt == RETURN) {
-        x.hasEmptyReturn = true;
-    }
-
-    // Disallow return v; in generator.
-    if (x.hasReturnWithValue && x.isGenerator)
-        throw t.newSyntaxError("Generator returns a value");
-
-    if (tt == RETURN)
-        b.RETURN$finish(n);
-    else
-        b.YIELD$finish(n);
-
-    return n;
-}
-
-/*
- * FunctionDefinition :: (tokenizer, compiler context, boolean,
- *                        DECLARED_FORM or EXPRESSED_FORM or STATEMENT_FORM)
- *                    -> node
- */
-function FunctionDefinition(t, x, requireName, functionForm) {
-    var b = x.builder;
-    var f = b.FUNCTION$build(t);
-    if (t.match(IDENTIFIER))
-        b.FUNCTION$setName(f, t.token.value);
-    else if (requireName)
-        throw t.newSyntaxError("Missing function identifier");
-
-    t.mustMatch(LEFT_PAREN);
-    if (!t.match(RIGHT_PAREN)) {
-        do {
-            switch (t.get()) {
-              case LEFT_BRACKET:
-              case LEFT_CURLY:
-                // Destructured formal parameters.
-                t.unget();
-                b.FUNCTION$addParam(f, DestructuringExpression(t, x));
-                break;
-              case IDENTIFIER:
-                b.FUNCTION$addParam(f, 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?
-    var tt = t.get();
-    if (tt != LEFT_CURLY)
-        t.unget();
-
-    var x2 = new CompilerContext(true, b);
-    var rp = t.save();
-    if (x.inFunction) {
-        /*
-         * Inner functions don't reset block numbering. They also need to
-         * remember which block they were parsed in for hoisting (see comment
-         * below).
-         */
-        x2.blockId = x.blockId;
-    }
-
-    if (tt != LEFT_CURLY) {
-        b.FUNCTION$setBody(f, AssignExpression(t, x));
-        if (x.isGenerator)
-            throw t.newSyntaxError("Generator returns a value");
-    } else {
-        b.FUNCTION$hoistVars(x2.blockId);
-        b.FUNCTION$setBody(f, Script(t, x2));
+    
+        if (tt == LEFT_CURLY)
+            t.mustMatch(RIGHT_CURLY);
+    
+        f.end = t.token.end;
+        f.functionForm = functionForm;
+        if (functionForm == DECLARED_FORM)
+            x.funDecls.push(f);
+        b.FUNCTION$finish(f, x);
+        return f;
     }
-
+    
     /*
-     * To linearize hoisting with nested blocks needing hoists, if a toplevel
-     * function has any hoists we reparse the entire thing. Each toplevel
-     * function is parsed at most twice.
-     *
-     * Pass 1: If there needs to be hoisting at any child block or inner
-     * function, the entire function gets reparsed.
-     *
-     * Pass 2: It's possible that hoisting has changed the upvars of
-     * functions. That is, consider:
-     *
-     * function f() {
-     *   x = 0;
-     *   g();
-     *   x; // x's forward pointer should be invalidated!
-     *   function g() {
-     *     x = 'g';
-     *   }
-     *   var x;
-     * }
+     * Variables :: (tokenizer, compiler context) -> node
      *
-     * So, a function needs to remember in which block it is parsed under
-     * (since the function body is _not_ hoisted, only the declaration) and
-     * upon hoisting, needs to recalculate all its upvars up front.
+     * Parses a comma-separated list of var declarations (and maybe
+     * initializations).
      */
-    if (x2.needsHoisting) {
-        // Order is important here! funDecls must come _after_ varDecls!
-        b.setHoists(f.body.id, x2.varDecls.concat(x2.funDecls));
-
-        if (x.inFunction) {
-            // Propagate up to the parent function if we're an inner function.
-            x.needsHoisting = true;
-        } else {
-            // Only re-parse toplevel functions.
-            var x3 = x2;
-            x2 = new CompilerContext(true, b);
-            t.rewind(rp);
-            // Set a flag in case the builder wants to have different behavior
-            // on the second pass.
-            b.secondPass = true;
-            b.FUNCTION$hoistVars(f.body.id, true);
-            b.FUNCTION$setBody(f, Script(t, x2));
-            b.secondPass = false;
+    function Variables(t, x, letBlock) {
+        var b = x.builder;
+        var n, ss, i, s;
+        var build, addDecl, finish;
+        switch (t.token.type) {
+          case VAR:
+            build = b.VAR$build;
+            addDecl = b.VAR$addDecl;
+            finish = b.VAR$finish;
+            s = x;
+            break;
+          case CONST:
+            build = b.CONST$build;
+            addDecl = b.CONST$addDecl;
+            finish = b.CONST$finish;
+            s = x;
+            break;
+          case LET:
+          case LEFT_PAREN:
+            build = b.LET$build;
+            addDecl = b.LET$addDecl;
+            finish = b.LET$finish;
+            if (!letBlock) {
+                ss = x.stmtStack;
+                i = ss.length;
+                while (ss[--i].type !== BLOCK) ; // a BLOCK *must* be found.
+                /*
+                 * Lets at the function toplevel are just vars, at least in
+                 * SpiderMonkey.
+                 */
+                if (i == 0) {
+                    build = b.VAR$build;
+                    addDecl = b.VAR$addDecl;
+                    finish = b.VAR$finish;
+                    s = x;
+                } else {
+                    s = ss[i];
+                }
+            } else {
+                s = letBlock;
+            }
+            break;
         }
-    }
-
-    if (tt == LEFT_CURLY)
-        t.mustMatch(RIGHT_CURLY);
-
-    f.end = t.token.end;
-    f.functionForm = functionForm;
-    if (functionForm == DECLARED_FORM)
-        x.funDecls.push(f);
-    b.FUNCTION$finish(f, x);
-    return f;
-}
-
-/*
- * Variables :: (tokenizer, compiler context) -> node
- *
- * Parses a comma-separated list of var declarations (and maybe
- * initializations).
- */
-function Variables(t, x, letBlock) {
-    var b = x.builder;
-    var n, ss, i, s;
-    var build, addDecl, finish;
-    switch (t.token.type) {
-      case VAR:
-        build = b.VAR$build;
-        addDecl = b.VAR$addDecl;
-        finish = b.VAR$finish;
-        s = x;
-        break;
-      case CONST:
-        build = b.CONST$build;
-        addDecl = b.CONST$addDecl;
-        finish = b.CONST$finish;
-        s = x;
-        break;
-      case LET:
-      case LEFT_PAREN:
-        build = b.LET$build;
-        addDecl = b.LET$addDecl;
-        finish = b.LET$finish;
-        if (!letBlock) {
-            ss = x.stmtStack;
-            i = ss.length;
-            while (ss[--i].type !== BLOCK) ; // a BLOCK *must* be found.
+        n = build.call(b, t);
+        initializers = [];
+        do {
+            var tt = t.get();
             /*
-             * Lets at the function toplevel are just vars, at least in
-             * SpiderMonkey.
+             * FIXME Should have a special DECLARATION node instead of overloading
+             * IDENTIFIER to mean both identifier declarations and destructured
+             * declarations.
              */
-            if (i == 0) {
-                build = b.VAR$build;
-                addDecl = b.VAR$addDecl;
-                finish = b.VAR$finish;
-                s = x;
-            } else {
-                s = ss[i];
-            }
-        } else {
-            s = letBlock;
-        }
-        break;
-    }
-    n = build.call(b, t);
-    initializers = [];
-    do {
-        var tt = t.get();
-        /*
-         * FIXME Should have a special DECLARATION node instead of overloading
-         * IDENTIFIER to mean both identifier declarations and destructured
-         * declarations.
-         */
-        var n2 = b.DECL$build(t);
-        if (tt == LEFT_BRACKET || tt == LEFT_CURLY) {
-            // Pass in s if we need to add each pattern matched into
-            // its varDecls, else pass in x.
-            var data = null;
-            // Need to unget to parse the full destructured expression.
-            t.unget();
-            b.DECL$setName(n2, DestructuringExpression(t, x, true, s));
-            if (x.inForLoopInit && t.peek() == IN) {
+            var n2 = b.DECL$build(t);
+            if (tt == LEFT_BRACKET || tt == LEFT_CURLY) {
+                // Pass in s if we need to add each pattern matched into
+                // its varDecls, else pass in x.
+                var data = null;
+                // Need to unget to parse the full destructured expression.
+                t.unget();
+                b.DECL$setName(n2, DestructuringExpression(t, x, true, s));
+                if (x.inForLoopInit && t.peek() == IN) {
+                    addDecl.call(b, n, n2, s);
+                    continue;
+                }
+    
+                t.mustMatch(ASSIGN);
+                if (t.token.assignOp)
+                    throw t.newSyntaxError("Invalid variable initialization");
+    
+                // Parse the init as a normal assignment.
+                var n3 = b.ASSIGN$build(t);
+                b.ASSIGN$addOperand(n3, n2.name);
+                b.ASSIGN$addOperand(n3, AssignExpression(t, x));
+                b.ASSIGN$finish(n3);
+    
+                // But only add the rhs as the initializer.
+                b.DECL$setInitializer(n2, n3[1]);
+                b.DECL$finish(n2);
                 addDecl.call(b, n, n2, s);
                 continue;
             }
-
-            t.mustMatch(ASSIGN);
-            if (t.token.assignOp)
-                throw t.newSyntaxError("Invalid variable initialization");
-
-            // Parse the init as a normal assignment.
-            var n3 = b.ASSIGN$build(t);
-            b.ASSIGN$addOperand(n3, n2.name);
-            b.ASSIGN$addOperand(n3, AssignExpression(t, x));
-            b.ASSIGN$finish(n3);
-
-            // But only add the rhs as the initializer.
-            b.DECL$setInitializer(n2, n3[1]);
-            b.DECL$finish(n2);
+    
+            if (tt != IDENTIFIER)
+                throw t.newSyntaxError("missing variable name");
+    
+            b.DECL$setName(n2, t.token.value);
+            b.DECL$setReadOnly(n2, n.type == CONST);
             addDecl.call(b, n, n2, s);
-            continue;
-        }
-
-        if (tt != IDENTIFIER)
-            throw t.newSyntaxError("Missing variable name");
-
-        b.DECL$setName(n2, t.token.value);
-        b.DECL$setReadOnly(n2, n.type == CONST);
-        addDecl.call(b, n, n2, s);
-
-        if (t.match(ASSIGN)) {
-            if (t.token.assignOp)
-                throw t.newSyntaxError("Invalid variable initialization");
-
-            // Parse the init as a normal assignment.
-            var id = mkIdentifier(n2.tokenizer, n2.name, true);
-            var n3 = b.ASSIGN$build(t);
-            b.ASSIGN$addOperand(n3, id);
-            b.ASSIGN$addOperand(n3, AssignExpression(t, x));
-            b.ASSIGN$finish(n3);
-            initializers.push(n3);
-
-            // But only add the rhs as the initializer.
-            b.DECL$setInitializer(n2, n3[1]);
+    
+            if (t.match(ASSIGN)) {
+                if (t.token.assignOp)
+                    throw t.newSyntaxError("Invalid variable initialization");
+    
+                // Parse the init as a normal assignment with a fake lhs.
+                var id = new Node(n2.tokenizer, IDENTIFIER);
+                var n3 = b.ASSIGN$build(t);
+                id.name = id.value = n2.name;
+                b.ASSIGN$addOperand(n3, id);
+                b.ASSIGN$addOperand(n3, AssignExpression(t, x));
+                b.ASSIGN$finish(n3);
+                initializers.push(n3);
+    
+                // But only add the rhs as the initializer.
+                b.DECL$setInitializer(n2, n3[1]);
+            }
+    
+            b.DECL$finish(n2);
+            s.varDecls.push(n2);
+        } while (t.match(COMMA));
+        finish.call(b, n);
+        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, binds;
+        var b = x.builder;
+    
+        // t.token.type must be LET
+        n = b.LET_BLOCK$build(t);
+        t.mustMatch(LEFT_PAREN);
+        b.LET_BLOCK$setVariables(n, 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 = b.SEMICOLON$build(t);
+            b.SEMICOLON$setExpression(n2, n);
+            b.SEMICOLON$finish(n2);
+            isStatement = false;
         }
-
-        b.DECL$finish(n2);
-        s.varDecls.push(n2);
-    } while (t.match(COMMA));
-    finish.call(b, n);
-    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, binds;
-    var b = x.builder;
-
-    // t.token.type must be LET
-    n = b.LET_BLOCK$build(t);
-    t.mustMatch(LEFT_PAREN);
-    b.LET_BLOCK$setVariables(n, 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 = b.SEMICOLON$build(t);
-        b.SEMICOLON$setExpression(n2, n);
-        b.SEMICOLON$finish(n2);
-        isStatement = false;
+    
+        if (isStatement) {
+            n2 = Block(t, x);
+            b.LET_BLOCK$setBlock(n, n2);
+        } else {
+            n2 = AssignExpression(t, x);
+            b.LET_BLOCK$setExpression(n, n2);
+        }
+    
+        b.LET_BLOCK$finish(n);
+    
+        return n;
     }
-
-    if (isStatement) {
-        n2 = Block(t, x);
-        b.LET_BLOCK$setBlock(n, n2);
-    } else {
-        n2 = AssignExpression(t, x);
-        b.LET_BLOCK$setExpression(n, n2);
-    }
-
-    b.LET_BLOCK$finish(n);
-
-    return n;
-}
-
-function checkDestructuring(t, x, n, simpleNamesOnly, data) {
-    if (n.type == ARRAY_COMP)
-        throw t.newSyntaxError("Invalid array comprehension left-hand side");
-    if (n.type != ARRAY_INIT && n.type != OBJECT_INIT)
-        return;
-
-    var b = x.builder;
-
-    for (var i = 0, j = n.length; i < j; i++) {
-        var nn = n[i], lhs, rhs;
-        if (!nn)
-            continue;
-        if (nn.type == PROPERTY_INIT)
-            lhs = nn[0], rhs = nn[1];
-        else
-            lhs = null, rhs = null;
-        if (rhs && (rhs.type == ARRAY_INIT || rhs.type == OBJECT_INIT))
-            checkDestructuring(t, x, rhs, simpleNamesOnly, data);
-        if (lhs && simpleNamesOnly) {
-            // In declarations, lhs must be simple names
-            if (lhs.type != IDENTIFIER) {
-                throw t.newSyntaxError("Missing name in pattern");
-            } else if (data) {
-                var n2 = b.DECL$build(t);
-                b.DECL$setName(n2, lhs.value);
-                // Don't need to set initializer because it's just for
-                // hoisting anyways.
-                b.DECL$finish(n2);
-                // Each pattern needs to be added to varDecls.
-                data.varDecls.push(n2);
+    
+    function checkDestructuring(t, x, n, simpleNamesOnly, data) {
+        if (n.type == ARRAY_COMP)
+            throw t.newSyntaxError("Invalid array comprehension left-hand side");
+        if (n.type != ARRAY_INIT && n.type != OBJECT_INIT)
+            return;
+    
+        var b = x.builder;
+    
+        for (var i = 0, j = n.length; i < j; i++) {
+            var nn = n[i], lhs, rhs;
+            if (!nn)
+                continue;
+            if (nn.type == PROPERTY_INIT)
+                lhs = nn[0], rhs = nn[1];
+            else
+                lhs = null, rhs = null;
+            if (rhs && (rhs.type == ARRAY_INIT || rhs.type == OBJECT_INIT))
+                checkDestructuring(t, x, rhs, simpleNamesOnly, data);
+            if (lhs && simpleNamesOnly) {
+                // In declarations, lhs must be simple names
+                if (lhs.type != IDENTIFIER) {
+                    throw t.newSyntaxError("missing name in pattern");
+                } else if (data) {
+                    var n2 = b.DECL$build(t);
+                    b.DECL$setName(n2, lhs.value);
+                    // Don't need to set initializer because it's just for
+                    // hoisting anyways.
+                    b.DECL$finish(n2);
+                    // Each pattern needs to be added to varDecls.
+                    data.varDecls.push(n2);
+                }
             }
         }
     }
-}
-
-function DestructuringExpression(t, x, simpleNamesOnly, data) {
-    var n = PrimaryExpression(t, x);
-    checkDestructuring(t, x, n, simpleNamesOnly, data);
-    return n;
-}
-
-function GeneratorExpression(t, x, e) {
-    var n;
-
-    n = b.GENERATOR$build(t);
-    b.GENERATOR$setExpression(n, e);
-    b.GENERATOR$setTail(n, comprehensionTail(t, x));
-    b.GENERATOR$finish(n);
-
-    return n;
-}
-
-function comprehensionTail(t, x) {
-    var body, n;
-    var b = x.builder;
-    // t.token.type must be FOR
-    body = b.COMP_TAIL$build(t);
-
-    do {
-        n = b.FOR$build(t);
-        // Comprehension tails are always for..in loops.
-        b.FOR$rebuildForIn(n);
-        if (t.match(IDENTIFIER)) {
-            // But sometimes they're for each..in.
-            if (t.token.value == "each")
-                b.FOR$rebuildForEach(n);
-            else
+    
+    function DestructuringExpression(t, x, simpleNamesOnly, data) {
+        var n = PrimaryExpression(t, x);
+        checkDestructuring(t, x, n, simpleNamesOnly, data);
+        return n;
+    }
+    
+    function GeneratorExpression(t, x, e) {
+        var n;
+    
+        n = b.GENERATOR$build(t);
+        b.GENERATOR$setExpression(n, e);
+        b.GENERATOR$setTail(n, comprehensionTail(t, x));
+        b.GENERATOR$finish(n);
+    
+        return n;
+    }
+    
+    function comprehensionTail(t, x) {
+        var body, n;
+        var b = x.builder;
+        // t.token.type must be FOR
+        body = b.COMP_TAIL$build(t);
+    
+        do {
+            n = b.FOR$build(t);
+            // Comprehension tails are always for..in loops.
+            b.FOR$rebuildForIn(n);
+            if (t.match(IDENTIFIER)) {
+                // But sometimes they're for each..in.
+                if (t.token.value == "each")
+                    b.FOR$rebuildForEach(n);
+                else
+                    t.unget();
+            }
+            t.mustMatch(LEFT_PAREN);
+            switch(t.get()) {
+              case LEFT_BRACKET:
+              case LEFT_CURLY:
                 t.unget();
-        }
+                // Destructured left side of for in comprehension tails.
+                b.FOR$setIterator(n, DestructuringExpression(t, x), null);
+                break;
+    
+              case IDENTIFIER:
+                var n3 = b.DECL$build(t);
+                b.DECL$setName(n3, n3.value);
+                b.DECL$finish(n3);
+                var n2 = b.VAR$build(t);
+                b.VAR$addDecl(n2, n3);
+                b.VAR$finish(n2);
+                b.FOR$setIterator(n, n3, n2);
+                /*
+                 * 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);
+            b.FOR$setObject(n, Expression(t, x));
+            t.mustMatch(RIGHT_PAREN);
+            b.COMP_TAIL$addFor(body, n);
+        } while (t.match(FOR));
+    
+        // Optional guard.
+        if (t.match(IF))
+            b.COMP_TAIL$setGuard(body, ParenExpression(t, x));
+    
+        b.COMP_TAIL$finish(body);
+        return body;
+    }
+    
+    function ParenExpression(t, x) {
         t.mustMatch(LEFT_PAREN);
-        switch(t.get()) {
-          case LEFT_BRACKET:
-          case LEFT_CURLY:
-            t.unget();
-            // Destructured left side of for in comprehension tails.
-            b.FOR$setIterator(n, DestructuringExpression(t, x), null);
-            break;
-
-          case IDENTIFIER:
-            var n3 = b.DECL$build(t);
-            b.DECL$setName(n3, n3.value);
-            b.DECL$finish(n3);
-            var n2 = b.VAR$build(t);
-            b.VAR$addDecl(n2, n3);
-            b.VAR$finish(n2);
-            b.FOR$setIterator(n, n3, n2);
-            /*
-             * 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);
-        b.FOR$setObject(n, Expression(t, x));
-        t.mustMatch(RIGHT_PAREN);
-        b.COMP_TAIL$addFor(body, n);
-    } while (t.match(FOR));
-
-    // Optional guard.
-    if (t.match(IF))
-        b.COMP_TAIL$setGuard(body, ParenExpression(t, x));
-
-    b.COMP_TAIL$finish(body);
-    return body;
-}
-
-function ParenExpression(t, x) {
-    t.mustMatch(LEFT_PAREN);
-
-    /*
-     * 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 oldLoopInit = x.inForLoopInit;
-    x.inForLoopInit = false;
-    var n = Expression(t, x);
-    x.inForLoopInit = oldLoopInit;
-
-    var err = "expression must be parenthesized";
-    if (t.match(FOR)) {
-        if (n.type == YIELD && !n.parenthesized)
-            throw t.newSyntaxError("Yield " + err);
-        if (n.type == COMMA && !n.parenthesized)
-            throw t.newSyntaxError("Generator " + err);
-        n = GeneratorExpression(t, x, n);
-    }
-
-    t.mustMatch(RIGHT_PAREN);
-
-    return n;
-}
-
-/*
- * Expression: (tokenizer, compiler context) -> node
- *
- * Top-down expression parser matched against SpiderMonkey.
- */
-function Expression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = AssignExpression(t, x);
-    if (t.match(COMMA)) {
-        n2 = b.COMMA$build(t);
-        b.COMMA$addOperand(n2, n);
-        n = n2;
-        do {
-            n2 = n[n.length-1];
-            if (n2.type == YIELD && !n2.parenthesized)
-                throw t.newSyntaxError("Yield expression must be parenthesized");
-            b.COMMA$addOperand(n, AssignExpression(t, x));
-        } while (t.match(COMMA));
-        b.COMMA$finish(n);
-    }
-
-    return n;
-}
-
-function AssignExpression(t, x) {
-    var n, lhs;
-    var b = x.builder;
-
-    // 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 = b.ASSIGN$build(t);
-    lhs = ConditionalExpression(t, x);
-
-    if (!t.match(ASSIGN)) {
-        b.ASSIGN$finish(n);
-        return lhs;
-    }
-
-    switch (lhs.type) {
-      case OBJECT_INIT:
-      case ARRAY_INIT:
-        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;
-    }
-
-    b.ASSIGN$setAssignOp(n, t.token.assignOp);
-    b.ASSIGN$addOperand(n, lhs);
-    b.ASSIGN$addOperand(n, AssignExpression(t, x));
-    b.ASSIGN$finish(n);
-
-    return n;
-}
-
-function ConditionalExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = OrExpression(t, x);
-    if (t.match(HOOK)) {
-        n2 = n;
-        n = b.HOOK$build(t);
-        b.HOOK$setCondition(n, n2);
+    
         /*
-         * Always accept the 'in' operator in the middle clause of a ternary,
+         * 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 oldLoopInit = x.inForLoopInit;
         x.inForLoopInit = false;
-        b.HOOK$setThenPart(n, AssignExpression(t, x));
+        var n = Expression(t, x);
         x.inForLoopInit = oldLoopInit;
-        if (!t.match(COLON))
-            throw t.newSyntaxError("Missing : after ?");
-        b.HOOK$setElsePart(n, AssignExpression(t, x));
-        b.HOOK$finish(n);
+    
+        var err = "expression must be parenthesized";
+        if (t.match(FOR)) {
+            if (n.type == YIELD && !n.parenthesized)
+                throw t.newSyntaxError("Yield " + err);
+            if (n.type == COMMA && !n.parenthesized)
+                throw t.newSyntaxError("Generator " + err);
+            n = GeneratorExpression(t, x, n);
+        }
+    
+        t.mustMatch(RIGHT_PAREN);
+    
+        return n;
     }
-
-    return n;
-}
-
-function OrExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = AndExpression(t, x);
-    while (t.match(OR)) {
-        n2 = b.OR$build(t);
-        b.OR$addOperand(n2, n);
-        b.OR$addOperand(n2, AndExpression(t, x));
-        b.OR$finish(n2);
-        n = n2;
+    
+    /*
+     * Expression: (tokenizer, compiler context) -> node
+     *
+     * Top-down expression parser matched against SpiderMonkey.
+     */
+    function Expression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = AssignExpression(t, x);
+        if (t.match(COMMA)) {
+            n2 = b.COMMA$build(t);
+            b.COMMA$addOperand(n2, n);
+            n = n2;
+            do {
+                n2 = n[n.length-1];
+                if (n2.type == YIELD && !n2.parenthesized)
+                    throw t.newSyntaxError("Yield expression must be parenthesized");
+                b.COMMA$addOperand(n, AssignExpression(t, x));
+            } while (t.match(COMMA));
+            b.COMMA$finish(n);
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function AndExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = BitwiseOrExpression(t, x);
-    while (t.match(AND)) {
-        n2 = b.AND$build(t);
-        b.AND$addOperand(n2, n);
-        b.AND$addOperand(n2, BitwiseOrExpression(t, x));
-        b.AND$finish(n2);
-        n = n2;
+    
+    function AssignExpression(t, x) {
+        var n, lhs;
+        var b = x.builder;
+    
+        // 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 = b.ASSIGN$build(t);
+        lhs = ConditionalExpression(t, x);
+    
+        if (!t.match(ASSIGN)) {
+            b.ASSIGN$finish(n);
+            return lhs;
+        }
+    
+        switch (lhs.type) {
+          case OBJECT_INIT:
+          case ARRAY_INIT:
+            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;
+        }
+    
+        b.ASSIGN$setAssignOp(n, t.token.assignOp);
+        b.ASSIGN$addOperand(n, lhs);
+        b.ASSIGN$addOperand(n, AssignExpression(t, x));
+        b.ASSIGN$finish(n);
+    
+        return n;
     }
-
-    return n;
-}
-
-function BitwiseOrExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = BitwiseXorExpression(t, x);
-    while (t.match(BITWISE_OR)) {
-        n2 = b.BITWISE_OR$build(t);
-        b.BITWISE_OR$addOperand(n2, n);
-        b.BITWISE_OR$addOperand(n2, BitwiseXorExpression(t, x));
-        b.BITWISE_OR$finish(n2);
-        n = n2;
+    
+    function ConditionalExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = OrExpression(t, x);
+        if (t.match(HOOK)) {
+            n2 = n;
+            n = b.HOOK$build(t);
+            b.HOOK$setCondition(n, 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.
+             */
+            var oldLoopInit = x.inForLoopInit;
+            x.inForLoopInit = false;
+            b.HOOK$setThenPart(n, AssignExpression(t, x));
+            x.inForLoopInit = oldLoopInit;
+            if (!t.match(COLON))
+                throw t.newSyntaxError("missing : after ?");
+            b.HOOK$setElsePart(n, AssignExpression(t, x));
+            b.HOOK$finish(n);
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function BitwiseXorExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = BitwiseAndExpression(t, x);
-    while (t.match(BITWISE_XOR)) {
-        n2 = b.BITWISE_XOR$build(t);
-        b.BITWISE_XOR$addOperand(n2, n);
-        b.BITWISE_XOR$addOperand(n2, BitwiseAndExpression(t, x));
-        b.BITWISE_XOR$finish(n2);
-        n = n2;
+    
+    function OrExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = AndExpression(t, x);
+        while (t.match(OR)) {
+            n2 = b.OR$build(t);
+            b.OR$addOperand(n2, n);
+            b.OR$addOperand(n2, AndExpression(t, x));
+            b.OR$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function AndExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = BitwiseOrExpression(t, x);
+        while (t.match(AND)) {
+            n2 = b.AND$build(t);
+            b.AND$addOperand(n2, n);
+            b.AND$addOperand(n2, BitwiseOrExpression(t, x));
+            b.AND$finish(n2);
+            n = n2;
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function BitwiseAndExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = EqualityExpression(t, x);
-    while (t.match(BITWISE_AND)) {
-        n2 = b.BITWISE_AND$build(t);
-        b.BITWISE_AND$addOperand(n2, n);
-        b.BITWISE_AND$addOperand(n2, EqualityExpression(t, x));
-        b.BITWISE_AND$finish(n2);
-        n = n2;
+    
+    function BitwiseOrExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = BitwiseXorExpression(t, x);
+        while (t.match(BITWISE_OR)) {
+            n2 = b.BITWISE_OR$build(t);
+            b.BITWISE_OR$addOperand(n2, n);
+            b.BITWISE_OR$addOperand(n2, BitwiseXorExpression(t, x));
+            b.BITWISE_OR$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function BitwiseXorExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = BitwiseAndExpression(t, x);
+        while (t.match(BITWISE_XOR)) {
+            n2 = b.BITWISE_XOR$build(t);
+            b.BITWISE_XOR$addOperand(n2, n);
+            b.BITWISE_XOR$addOperand(n2, BitwiseAndExpression(t, x));
+            b.BITWISE_XOR$finish(n2);
+            n = n2;
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function EqualityExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = RelationalExpression(t, x);
-    while (t.match(EQ) || t.match(NE) ||
-           t.match(STRICT_EQ) || t.match(STRICT_NE)) {
-        n2 = b.EQUALITY$build(t);
-        b.EQUALITY$addOperand(n2, n);
-        b.EQUALITY$addOperand(n2, RelationalExpression(t, x));
-        b.EQUALITY$finish(n2);
-        n = n2;
+    
+    function BitwiseAndExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = EqualityExpression(t, x);
+        while (t.match(BITWISE_AND)) {
+            n2 = b.BITWISE_AND$build(t);
+            b.BITWISE_AND$addOperand(n2, n);
+            b.BITWISE_AND$addOperand(n2, EqualityExpression(t, x));
+            b.BITWISE_AND$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function EqualityExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = RelationalExpression(t, x);
+        while (t.match(EQ) || t.match(NE) ||
+               t.match(STRICT_EQ) || t.match(STRICT_NE)) {
+            n2 = b.EQUALITY$build(t);
+            b.EQUALITY$addOperand(n2, n);
+            b.EQUALITY$addOperand(n2, RelationalExpression(t, x));
+            b.EQUALITY$finish(n2);
+            n = n2;
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function RelationalExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-    var oldLoopInit = x.inForLoopInit;
-
-    /*
-     * Uses of the in operator in shiftExprs are always unambiguous,
-     * so unset the flag that prohibits recognizing it.
-     */
-    x.inForLoopInit = false;
-    n = ShiftExpression(t, x);
-    while ((t.match(LT) || t.match(LE) || t.match(GE) || t.match(GT) ||
-           (oldLoopInit == false && t.match(IN)) ||
-           t.match(INSTANCEOF))) {
-        n2 = b.RELATIONAL$build(t);
-        b.RELATIONAL$addOperand(n2, n);
-        b.RELATIONAL$addOperand(n2, ShiftExpression(t, x));
-        b.RELATIONAL$finish(n2);
-        n = n2;
+    
+    function RelationalExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+        var oldLoopInit = x.inForLoopInit;
+    
+        /*
+         * Uses of the in operator in shiftExprs are always unambiguous,
+         * so unset the flag that prohibits recognizing it.
+         */
+        x.inForLoopInit = false;
+        n = ShiftExpression(t, x);
+        while ((t.match(LT) || t.match(LE) || t.match(GE) || t.match(GT) ||
+               (oldLoopInit == false && t.match(IN)) ||
+               t.match(INSTANCEOF))) {
+            n2 = b.RELATIONAL$build(t);
+            b.RELATIONAL$addOperand(n2, n);
+            b.RELATIONAL$addOperand(n2, ShiftExpression(t, x));
+            b.RELATIONAL$finish(n2);
+            n = n2;
+        }
+        x.inForLoopInit = oldLoopInit;
+    
+        return n;
     }
-    x.inForLoopInit = oldLoopInit;
-
-    return n;
-}
-
-function ShiftExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = AddExpression(t, x);
-    while (t.match(LSH) || t.match(RSH) || t.match(URSH)) {
-        n2 = b.SHIFT$build(t);
-        b.SHIFT$addOperand(n2, n);
-        b.SHIFT$addOperand(n2, AddExpression(t, x));
-        b.SHIFT$finish(n2);
-        n = n2;
+    
+    function ShiftExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = AddExpression(t, x);
+        while (t.match(LSH) || t.match(RSH) || t.match(URSH)) {
+            n2 = b.SHIFT$build(t);
+            b.SHIFT$addOperand(n2, n);
+            b.SHIFT$addOperand(n2, AddExpression(t, x));
+            b.SHIFT$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function AddExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = MultiplyExpression(t, x);
+        while (t.match(PLUS) || t.match(MINUS)) {
+            n2 = b.ADD$build(t);
+            b.ADD$addOperand(n2, n);
+            b.ADD$addOperand(n2, MultiplyExpression(t, x));
+            b.ADD$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function MultiplyExpression(t, x) {
+        var n, n2;
+        var b = x.builder;
+    
+        n = UnaryExpression(t, x);
+        while (t.match(MUL) || t.match(DIV) || t.match(MOD)) {
+            n2 = b.MULTIPLY$build(t);
+            b.MULTIPLY$addOperand(n2, n);
+            b.MULTIPLY$addOperand(n2, UnaryExpression(t, x));
+            b.MULTIPLY$finish(n2);
+            n = n2;
+        }
+    
+        return n;
     }
-
-    return n;
-}
-
-function AddExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = MultiplyExpression(t, x);
-    while (t.match(PLUS) || t.match(MINUS)) {
-        n2 = b.ADD$build(t);
-        b.ADD$addOperand(n2, n);
-        b.ADD$addOperand(n2, MultiplyExpression(t, x));
-        b.ADD$finish(n2);
-        n = n2;
-    }
-
-    return n;
-}
-
-function MultiplyExpression(t, x) {
-    var n, n2;
-    var b = x.builder;
-
-    n = UnaryExpression(t, x);
-    while (t.match(MUL) || t.match(DIV) || t.match(MOD)) {
-        n2 = b.MULTIPLY$build(t);
-        b.MULTIPLY$addOperand(n2, n);
-        b.MULTIPLY$addOperand(n2, UnaryExpression(t, x));
-        b.MULTIPLY$finish(n2);
-        n = n2;
+    
+    function UnaryExpression(t, x) {
+        var n, n2, tt;
+        var b = x.builder;
+    
+        switch (tt = t.get(true)) {
+          case DELETE: case VOID: case TYPEOF:
+          case NOT: case BITWISE_NOT: case PLUS: case MINUS:
+            n = b.UNARY$build(t);
+            b.UNARY$addOperand(n, UnaryExpression(t, x));
+            break;
+    
+          case INCREMENT:
+          case DECREMENT:
+            // Prefix increment/decrement.
+            n = b.UNARY$build(t)
+            b.UNARY$addOperand(n, 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 = b.UNARY$build(t);
+                    b.UNARY$setPostfix(n2);
+                    b.UNARY$finish(n);
+                    b.UNARY$addOperand(n2, n);
+                    n = n2;
+                }
+            }
+            break;
+        }
+    
+        b.UNARY$finish(n);
+        return n;
     }
-
-    return n;
-}
-
-function UnaryExpression(t, x) {
-    var n, n2, tt;
-    var b = x.builder;
-
-    switch (tt = t.get(true)) {
-      case DELETE: case VOID: case TYPEOF:
-      case NOT: case BITWISE_NOT: case PLUS: case MINUS:
-        n = b.UNARY$build(t);
-        b.UNARY$addOperand(n, UnaryExpression(t, x));
-        break;
-
-      case INCREMENT:
-      case DECREMENT:
-        // Prefix increment/decrement.
-        n = b.UNARY$build(t)
-        b.UNARY$addOperand(n, 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 = b.UNARY$build(t);
-                b.UNARY$setPostfix(n2);
-                b.UNARY$finish(n);
-                b.UNARY$addOperand(n2, n);
+    
+    function MemberExpression(t, x, allowCallSyntax) {
+        var n, n2, tt;
+        var b = x.builder;
+    
+        if (t.match(NEW)) {
+            n = b.MEMBER$build(t);
+            b.MEMBER$addOperand(n, MemberExpression(t, x, false));
+            if (t.match(LEFT_PAREN)) {
+                b.MEMBER$rebuildNewWithArgs(n);
+                b.MEMBER$addOperand(n, ArgumentList(t, x));
+            }
+            b.MEMBER$finish(n);
+        } else {
+            n = PrimaryExpression(t, x);
+        }
+    
+        while ((tt = t.get()) != END) {
+            switch (tt) {
+              case DOT:
+                n2 = b.MEMBER$build(t);
+                b.MEMBER$addOperand(n2, n);
+                t.mustMatch(IDENTIFIER);
+                b.MEMBER$addOperand(n2, b.MEMBER$build(t));
+                break;
+    
+              case LEFT_BRACKET:
+                n2 = b.MEMBER$build(t, INDEX);
+                b.MEMBER$addOperand(n2, n);
+                b.MEMBER$addOperand(n2, Expression(t, x));
+                t.mustMatch(RIGHT_BRACKET);
+                break;
+    
+              case LEFT_PAREN:
+                if (allowCallSyntax) {
+                    n2 = b.MEMBER$build(t, CALL);
+                    b.MEMBER$addOperand(n2, n);
+                    b.MEMBER$addOperand(n2, ArgumentList(t, x));
+                    break;
+                }
+    
+                // FALL THROUGH
+              default:
+                t.unget();
+                return n;
+            }
+    
+            b.MEMBER$finish(n2);
+            n = n2;
+        }
+    
+        return n;
+    }
+    
+    function ArgumentList(t, x) {
+        var n, n2;
+        var b = x.builder;
+        var err = "expression must be parenthesized";
+    
+        n = b.LIST$build(t);
+        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 " + err);
+            if (t.match(FOR)) {
+                n2 = GeneratorExpression(t, x, n2);
+                if (n.length > 1 || t.peek(true) == COMMA)
+                    throw t.newSyntaxError("Generator " + err);
+            }
+            b.LIST$addOperand(n, n2);
+        } while (t.match(COMMA));
+        t.mustMatch(RIGHT_PAREN);
+        b.LIST$finish(n);
+    
+        return n;
+    }
+    
+    function PrimaryExpression(t, x) {
+        var n, n2, n3, tt = t.get(true);
+        var b = x.builder;
+    
+        switch (tt) {
+          case FUNCTION:
+            n = FunctionDefinition(t, x, false, EXPRESSED_FORM);
+            break;
+    
+          case LEFT_BRACKET:
+            n = b.ARRAY_INIT$build(t);
+            while ((tt = t.peek()) != RIGHT_BRACKET) {
+                if (tt == COMMA) {
+                    t.get();
+                    b.ARRAY_INIT$addElement(n, null);
+                    continue;
+                }
+                b.ARRAY_INIT$addElement(n, 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.length == 1 && t.match(FOR)) {
+                n2 = b.ARRAY_COMP$build(t);
+                b.ARRAY_COMP$setExpression(n2, n[0]);
+                b.ARRAY_COMP$setTail(n2, comprehensionTail(t, x));
                 n = n2;
             }
-        }
-        break;
-    }
-
-    b.UNARY$finish(n);
-    return n;
-}
-
-function MemberExpression(t, x, allowCallSyntax) {
-    var n, n2, tt;
-    var b = x.builder;
-
-    if (t.match(NEW)) {
-        n = b.MEMBER$build(t);
-        b.MEMBER$addOperand(n, MemberExpression(t, x, false));
-        if (t.match(LEFT_PAREN)) {
-            b.MEMBER$rebuildNewWithArgs(n);
-            b.MEMBER$addOperand(n, ArgumentList(t, x));
-        }
-        b.MEMBER$finish(n);
-    } else {
-        n = PrimaryExpression(t, x);
-    }
-
-    while ((tt = t.get()) != END) {
-        switch (tt) {
-          case DOT:
-            n2 = b.MEMBER$build(t);
-            b.MEMBER$addOperand(n2, n);
-            t.mustMatch(IDENTIFIER);
-            b.MEMBER$addOperand(n2, b.MEMBER$build(t));
-            break;
-
-          case LEFT_BRACKET:
-            n2 = b.MEMBER$build(t, INDEX);
-            b.MEMBER$addOperand(n2, n);
-            b.MEMBER$addOperand(n2, Expression(t, x));
             t.mustMatch(RIGHT_BRACKET);
+            b.PRIMARY$finish(n);
             break;
-
-          case LEFT_PAREN:
-            if (allowCallSyntax) {
-                n2 = b.MEMBER$build(t, CALL);
-                b.MEMBER$addOperand(n2, n);
-                b.MEMBER$addOperand(n2, ArgumentList(t, x));
-                break;
-            }
-
-            // FALL THROUGH
-          default:
-            t.unget();
-            return n;
-        }
-
-        b.MEMBER$finish(n2);
-        n = n2;
-    }
-
-    return n;
-}
-
-function ArgumentList(t, x) {
-    var n, n2;
-    var b = x.builder;
-    var err = "expression must be parenthesized";
-
-    n = b.LIST$build(t);
-    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 " + err);
-        if (t.match(FOR)) {
-            n2 = GeneratorExpression(t, x, n2);
-            if (n.length > 1 || t.peek(true) == COMMA)
-                throw t.newSyntaxError("Generator " + err);
-        }
-        b.LIST$addOperand(n, n2);
-    } while (t.match(COMMA));
-    t.mustMatch(RIGHT_PAREN);
-    b.LIST$finish(n);
-
-    return n;
-}
-
-function PrimaryExpression(t, x) {
-    var n, n2, n3, tt = t.get(true);
-    var b = x.builder;
-
-    switch (tt) {
-      case FUNCTION:
-        n = FunctionDefinition(t, x, false, EXPRESSED_FORM);
-        break;
-
-      case LEFT_BRACKET:
-        n = b.ARRAY_INIT$build(t);
-        while ((tt = t.peek()) != RIGHT_BRACKET) {
-            if (tt == COMMA) {
-                t.get();
-                b.ARRAY_INIT$addElement(n, null);
-                continue;
-            }
-            b.ARRAY_INIT$addElement(n, 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.length == 1 && t.match(FOR)) {
-            n2 = b.ARRAY_COMP$build(t);
-            b.ARRAY_COMP$setExpression(n2, n[0]);
-            b.ARRAY_COMP$setTail(n2, comprehensionTail(t, x));
-            n = n2;
-        }
-        t.mustMatch(RIGHT_BRACKET);
-        b.PRIMARY$finish(n);
-        break;
-
-      case LEFT_CURLY:
-        var id;
-        n = b.OBJECT_INIT$build(t);
-
-      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");
-                    var fd = FunctionDefinition(t, x, true, EXPRESSED_FORM);
-                    b.OBJECT_INIT$addProperty(n, fd);
-                } else {
-                    switch (tt) {
-                      case IDENTIFIER: case NUMBER: case STRING:
-                        id = b.PRIMARY$build(t, IDENTIFIER);
-                        b.PRIMARY$finish(id);
-                        break;
-                      case RIGHT_CURLY:
+    
+          case LEFT_CURLY:
+            var id;
+            n = b.OBJECT_INIT$build(t);
+    
+          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 trailing ,");
-                        break object_init;
-                      default:
-                        if (t.token.value in keywords) {
+                            throw t.newSyntaxError("Illegal property accessor");
+                        var fd = FunctionDefinition(t, x, true, EXPRESSED_FORM);
+                        b.OBJECT_INIT$addProperty(n, fd);
+                    } else {
+                        switch (tt) {
+                          case IDENTIFIER: case NUMBER: case STRING:
                             id = b.PRIMARY$build(t, IDENTIFIER);
                             b.PRIMARY$finish(id);
                             break;
+                          case RIGHT_CURLY:
+                            if (x.ecma3OnlyMode)
+                                throw t.newSyntaxError("Illegal trailing ,");
+                            break object_init;
+                          default:
+                            if (t.token.value in jsdefs.keywords) {
+                                id = b.PRIMARY$build(t, IDENTIFIER);
+                                b.PRIMARY$finish(id);
+                                break;
+                            }
+                            throw t.newSyntaxError("Invalid property name");
                         }
-                        throw t.newSyntaxError("Invalid property name");
+                        if (t.match(COLON)) {
+                            n2 = b.PROPERTY_INIT$build(t);
+                            b.PROPERTY_INIT$addOperand(n2, id);
+                            b.PROPERTY_INIT$addOperand(n2, AssignExpression(t, x));
+                            b.PROPERTY_INIT$finish(n2);
+                            b.OBJECT_INIT$addProperty(n, 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");
+                            b.OBJECT_INIT$addProperty(n, id);
+                        }
                     }
-                    if (t.match(COLON)) {
-                        n2 = b.PROPERTY_INIT$build(t);
-                        b.PROPERTY_INIT$addOperand(n2, id);
-                        b.PROPERTY_INIT$addOperand(n2, AssignExpression(t, x));
-                        b.PROPERTY_INIT$finish(n2);
-                        b.OBJECT_INIT$addProperty(n, 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");
-                        b.OBJECT_INIT$addProperty(n, id);
-                    }
-                }
-            } while (t.match(COMMA));
-            t.mustMatch(RIGHT_CURLY);
+                } while (t.match(COMMA));
+                t.mustMatch(RIGHT_CURLY);
+            }
+            b.OBJECT_INIT$finish(n);
+            break;
+    
+          case LEFT_PAREN:
+            // ParenExpression does its own matching on parentheses, so we need to
+            // unget.
+            t.unget();
+            n = ParenExpression(t, x);
+            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 = b.PRIMARY$build(t);
+            b.PRIMARY$finish(n);
+            break;
+    
+          default:
+            throw t.newSyntaxError("missing operand");
+            break;
         }
-        b.OBJECT_INIT$finish(n);
-        break;
-
-      case LEFT_PAREN:
-        // ParenExpression does its own matching on parentheses, so we need to
-        // unget.
-        t.unget();
-        n = ParenExpression(t, x);
-        n.parenthesized = true;
-        break;
+    
+        return n;
+    }
+    
+    /*
+     * parse :: (builder, file ptr, path, line number) -> node
+     */
+    function parse(b, s, f, l) {
+        var t = new jslex.Tokenizer(s, f, l);
+        var x = new CompilerContext(false, b);
+        var n = Script(t, x);
+        if (!t.done)
+            throw t.newSyntaxError("Syntax error");
+    
+        return n;
+    }
+    
+    return {
+        "parse": parse,
+        "VanillaBuilder": VanillaBuilder,
+        "DECLARED_FORM": DECLARED_FORM,
+        "EXPRESSED_FORM": EXPRESSED_FORM,
+        "STATEMENT_FORM": STATEMENT_FORM,
+        "Tokenizer": jslex.Tokenizer,
+        "FunctionDefinition": FunctionDefinition
+    };
 
-      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 = b.PRIMARY$build(t);
-        b.PRIMARY$finish(n);
-        break;
-
-      default:
-        throw t.newSyntaxError("Missing operand");
-        break;
-    }
-
-    return n;
-}
-
-/*
- * parse :: (builder, file ptr, path, line number) -> node
- */
-function parse(b, s, f, l) {
-    var t = new Tokenizer(s, f, l);
-    var x = new CompilerContext(false, b);
-    var n = Script(t, x);
-    if (!t.done)
-        throw t.newSyntaxError("Syntax error");
-
-    return n;
-}
+}());
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1251,16 +1251,67 @@ jsvalToFloat(JSContext *cx, jsval val, F
       }
     }
   }
   // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
   // does it. It's likely to be a mistake.
   return false;
 }
 
+template<class IntegerType>
+static bool
+StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
+{
+  JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
+
+  const jschar* cp = string->chars();
+  const jschar* end = cp + string->length();
+  if (cp == end)
+    return false;
+
+  IntegerType sign = 1;
+  if (cp[0] == '-') {
+    if (!numeric_limits<IntegerType>::is_signed)
+      return false;
+
+    sign = -1;
+    ++cp;
+  }
+
+  // Assume base-10, unless the string begins with '0x' or '0X'.
+  IntegerType base = 10;
+  if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
+    cp += 2;
+    base = 16;
+  }
+
+  // Scan the string left to right and build the number,
+  // checking for valid characters 0 - 9, a - f, A - F and overflow.
+  IntegerType i = 0;
+  while (cp != end) {
+    jschar c = *cp++;
+    if (c >= '0' && c <= '9')
+      c -= '0';
+    else if (base == 16 && c >= 'a' && c <= 'f')
+      c = c - 'a' + 10;
+    else if (base == 16 && c >= 'A' && c <= 'F')
+      c = c - 'A' + 10;
+    else
+      return false;
+
+    IntegerType ii = i;
+    i = ii * base + sign * c;
+    if (i / base != ii) // overflow
+      return false;
+  }
+
+  *result = i;
+  return true;
+}
+
 // Implicitly convert val to IntegerType, allowing jsint, jsdouble,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsvalToBigInteger(JSContext* cx,
                   jsval val,
                   bool allowString,
@@ -1494,67 +1545,16 @@ IntegerToString(IntegerType i, jsuint ra
 
   if (isNegative)
     *--cp = '-';
 
   JS_ASSERT(cp >= buffer);
   result.append(cp, end);
 }
 
-template<class IntegerType>
-static bool
-StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
-{
-  JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
-
-  const jschar* cp = string->chars();
-  const jschar* end = cp + string->length();
-  if (cp == end)
-    return false;
-
-  IntegerType sign = 1;
-  if (cp[0] == '-') {
-    if (!numeric_limits<IntegerType>::is_signed)
-      return false;
-
-    sign = -1;
-    ++cp;
-  }
-
-  // Assume base-10, unless the string begins with '0x' or '0X'.
-  IntegerType base = 10;
-  if (end - cp > 2 && cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X')) {
-    cp += 2;
-    base = 16;
-  }
-
-  // Scan the string left to right and build the number,
-  // checking for valid characters 0 - 9, a - f, A - F and overflow.
-  IntegerType i = 0;
-  while (cp != end) {
-    jschar c = *cp++;
-    if (c >= '0' && c <= '9')
-      c -= '0';
-    else if (base == 16 && c >= 'a' && c <= 'f')
-      c = c - 'a' + 10;
-    else if (base == 16 && c >= 'A' && c <= 'F')
-      c = c - 'A' + 10;
-    else
-      return false;
-
-    IntegerType ii = i;
-    i = ii * base + sign * c;
-    if (i / base != ii) // overflow
-      return false;
-  }
-
-  *result = i;
-  return true;
-}
-
 template<class CharType>
 static size_t
 strnlen(const CharType* begin, size_t max)
 {
   for (const CharType* s = begin; s != begin + max; ++s)
     if (*s == 0)
       return s - begin;
 
--- a/js/src/ctypes/libffi.patch
+++ b/js/src/ctypes/libffi.patch
@@ -1,9 +1,9 @@
-Patch libffi to fix bug 550602, bug 528129, bug 538216, bug 556902, and bug 538002.
+Patch libffi to fix bug 550602, bug 528129, bug 538216, bug 556902, bug 538002, and bug 581909.
 
 diff --git a/js/src/ctypes/libffi/Makefile.in b/js/src/ctypes/libffi/Makefile.in
 --- a/js/src/ctypes/libffi/Makefile.in
 +++ b/js/src/ctypes/libffi/Makefile.in
 @@ -192,17 +192,17 @@ LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIB
  CCLD = $(CC)
  LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
  	--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
@@ -124,36 +124,58 @@ diff --git a/js/src/ctypes/libffi/config
  	;;
    i?86-*-darwin*)
  	TARGET=X86_DARWIN; TARGETDIR=x86
  	;;
    i?86-*-solaris2.1[0-9]*)
 diff --git a/js/src/ctypes/libffi/msvcc.sh b/js/src/ctypes/libffi/msvcc.sh
 --- a/js/src/ctypes/libffi/msvcc.sh
 +++ b/js/src/ctypes/libffi/msvcc.sh
-@@ -37,17 +37,17 @@
+@@ -37,20 +37,21 @@
  #
  # ***** END LICENSE BLOCK *****
  
  #
  # GCC-compatible wrapper for cl.exe and ml.exe. Arguments are given in GCC
  # format and translated into something sensible for cl or ml.
  #
  
 -args="-nologo -W3"
 +args="-nologo"
  md=-MD
  cl="cl"
  ml="ml"
++safeseh="-safeseh"
  output=
  
  while [ $# -gt 0 ]
  do
    case $1
-@@ -103,17 +103,18 @@ do
+   in
+     -fexceptions)
+       # Don't enable exceptions for now.
+@@ -58,16 +59,17 @@ do
+       shift 1
+     ;;
+     -m32)
+       shift 1
+     ;;
+     -m64)
+       cl="cl"   # "$MSVC/x86_amd64/cl"
+       ml="ml64" # "$MSVC/x86_amd64/ml64"
++      safeseh=
+       shift 1
+     ;;
+     -O*)
+       args="$args $1"
+       shift 1
+     ;;
+     -g)
+       # Can't specify -RTC1 or -Zi in opt. -Gy is ok. Use -OPT:REF?
+@@ -103,17 +105,18 @@ do
        includes="$includes $1"
        shift 1
      ;;
      -W|-Wextra)
        # TODO map extra warnings
        shift 1
      ;;
      -Wall)
@@ -163,16 +185,35 @@ diff --git a/js/src/ctypes/libffi/msvcc.
        shift 1
      ;;
      -Werror)
        args="$args -WX"
        shift 1
      ;;
      -W*)
        # TODO map specific warnings
+@@ -158,17 +161,17 @@ done
+ if [ -n "$assembly" ]; then
+     if [ -z "$outdir" ]; then
+       outdir="."
+     fi
+     ppsrc="$outdir/$(basename $src|sed 's/.S$/.asm/g')"
+     echo "$cl -nologo -EP $includes $defines $src > $ppsrc"
+     "$cl" -nologo -EP $includes $defines $src > $ppsrc || exit $?
+     output="$(echo $output | sed 's%/F[dpa][^ ]*%%g')"
+-    args="-nologo -safeseh $single $output $ppsrc"
++    args="-nologo $safeseh $single $output $ppsrc"
+ 
+     echo "$ml $args"
+     eval "\"$ml\" $args"
+     result=$?
+ 
+     # required to fix ml64 broken output?
+     #mv *.obj $outdir
+ else
 diff --git a/js/src/ctypes/libffi/src/powerpc/ffi_darwin.c b/js/src/ctypes/libffi/src/powerpc/ffi_darwin.c
 --- a/js/src/ctypes/libffi/src/powerpc/ffi_darwin.c
 +++ b/js/src/ctypes/libffi/src/powerpc/ffi_darwin.c
 @@ -339,17 +339,17 @@ aix_adjust_aggregate_sizes (ffi_type *s)
    /* Do not add additional tail padding.  */
  }
  
  /* Perform machine dependent cif processing.  */
--- a/js/src/ctypes/libffi/msvcc.sh
+++ b/js/src/ctypes/libffi/msvcc.sh
@@ -41,16 +41,17 @@
 # GCC-compatible wrapper for cl.exe and ml.exe. Arguments are given in GCC
 # format and translated into something sensible for cl or ml.
 #
 
 args="-nologo"
 md=-MD
 cl="cl"
 ml="ml"
+safeseh="-safeseh"
 output=
 
 while [ $# -gt 0 ]
 do
   case $1
   in
     -fexceptions)
       # Don't enable exceptions for now.
@@ -58,16 +59,17 @@ do
       shift 1
     ;;
     -m32)
       shift 1
     ;;
     -m64)
       cl="cl"   # "$MSVC/x86_amd64/cl"
       ml="ml64" # "$MSVC/x86_amd64/ml64"
+      safeseh=
       shift 1
     ;;
     -O*)
       args="$args $1"
       shift 1
     ;;
     -g)
       # Can't specify -RTC1 or -Zi in opt. -Gy is ok. Use -OPT:REF?
@@ -159,17 +161,17 @@ done
 if [ -n "$assembly" ]; then
     if [ -z "$outdir" ]; then
       outdir="."
     fi
     ppsrc="$outdir/$(basename $src|sed 's/.S$/.asm/g')"
     echo "$cl -nologo -EP $includes $defines $src > $ppsrc"
     "$cl" -nologo -EP $includes $defines $src > $ppsrc || exit $?
     output="$(echo $output | sed 's%/F[dpa][^ ]*%%g')"
-    args="-nologo -safeseh $single $output $ppsrc"
+    args="-nologo $safeseh $single $output $ppsrc"
 
     echo "$ml $args"
     eval "\"$ml\" $args"
     result=$?
 
     # required to fix ml64 broken output?
     #mv *.obj $outdir
 else
--- a/js/src/imacros.jsasm
+++ b/js/src/imacros.jsasm
@@ -683,41 +683,16 @@ 3:      swap                            
         pick 9                              # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6 call
         pop                                 # fun this arg0 arg1 arg2 arg3 arg4 arg5 arg6
         call 7                              #
         stop                                #
     .end                                    #
 
 .end apply
 
-.igroup objtostr JSOP_OBJTOSTR
-
-    .imacro toString                                # obj
-        dup                                         # obj obj
-        dup                                         # obj obj obj
-        getprop valueOf                             # obj obj valueOf?
-        ifprimtop 1                                 # obj obj valueOf?
-        swap                                        # obj valueOf obj
-        string void                                 # obj valueOf obj "void"
-        call 1                                      # obj val?
-        ifprimtop 3                                 # obj val?
-        pop                                         # obj
-        dup                                         # obj obj
-        goto 2
-1:      pop                                         # obj obj
-2:      callprop toString                           # obj toString obj
-        call 0                                      # obj val
-        primtop (JSTYPE_VOID)                       # obj val
-3:      swap                                        # val obj
-        pop                                         # val
-        stop
-    .end
-
-.end objtostr
-
 .igroup getprop JSOP_GETPROP
     .imacro scriptgetter        # obj
         .fixup +1               # getter obj
         call 0                  # val
         stop
     .end
 .end getprop
 
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -359,47 +359,8 @@ js_PopInterpFrame(JSContext* cx, TracerS
     /* Pop the frame and its memory. */
     cx->stack().popInlineFrame(cx, fp, fp->down);
 
     /* Update the inline call count. */
     *state->inlineCallCountp = *state->inlineCallCountp - 1;
     return JS_TRUE;
 }
 JS_DEFINE_CALLINFO_2(extern, BOOL, js_PopInterpFrame, CONTEXT, TRACERSTATE, 0, ACCSET_STORE_ANY)
-
-JSString* FASTCALL
-js_ConcatN(JSContext *cx, JSString **strArray, uint32 size)
-{
-    /* Calculate total size. */
-    size_t numChar = 1;
-    for (uint32 i = 0; i < size; ++i) {
-        size_t before = numChar;
-        numChar += strArray[i]->length();
-        if (numChar < before)
-            return NULL;
-    }
-
-
-    /* Allocate buffer. */
-    if (numChar & js::tl::MulOverflowMask<sizeof(jschar)>::result)
-        return NULL;
-    jschar *buf = (jschar *)cx->malloc(numChar * sizeof(jschar));
-    if (!buf)
-        return NULL;
-
-    /* Fill buffer. */
-    jschar *ptr = buf;
-    for (uint32 i = 0; i < size; ++i) {
-        const jschar *chars;
-        size_t length;
-        strArray[i]->getCharsAndLength(chars, length);
-        js_strncpy(ptr, chars, length);
-        ptr += length;
-    }
-    *ptr = '\0';
-
-    /* Create string. */
-    JSString *str = js_NewString(cx, buf, numChar - 1);
-    if (!str)
-        cx->free(buf);
-    return str;
-}
-JS_DEFINE_CALLINFO_3(extern, STRING, js_ConcatN, CONTEXT, STRINGPTR, UINT32, 0, ACCSET_STORE_ANY)
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -592,17 +592,16 @@ JS_DECLARE_CALLINFO(js_StringToInt32)
 JS_DECLARE_CALLINFO(js_AddProperty)
 JS_DECLARE_CALLINFO(js_AddAtomProperty)
 JS_DECLARE_CALLINFO(js_HasNamedProperty)
 JS_DECLARE_CALLINFO(js_HasNamedPropertyInt32)
 JS_DECLARE_CALLINFO(js_TypeOfObject)
 JS_DECLARE_CALLINFO(js_BooleanIntToString)
 JS_DECLARE_CALLINFO(js_NewNullClosure)
 JS_DECLARE_CALLINFO(js_PopInterpFrame)
-JS_DECLARE_CALLINFO(js_ConcatN)
 
 /* Defined in jsfun.cpp. */
 JS_DECLARE_CALLINFO(js_AllocFlatClosure)
 JS_DECLARE_CALLINFO(js_PutArguments)
 JS_DECLARE_CALLINFO(js_PutCallObjectOnTrace)
 JS_DECLARE_CALLINFO(js_SetCallVar)
 JS_DECLARE_CALLINFO(js_SetCallArg)
 JS_DECLARE_CALLINFO(js_CloneFunctionObject)
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -5963,57 +5963,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                 CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
                 *pc = pn->pn_op;
                 top += tmp;
             } while ((pn2 = pn2->pn_next)->pn_next);
         }
         break;
 
       case TOK_PLUS:
-        /* For TCF_IN_FUNCTION test, see TOK_RB concerning JSOP_NEWARRAY. */
-        if (pn->pn_arity == PN_LIST && pn->pn_count < JS_BIT(16) &&
-            cg->inFunction()) {
-            /* Emit up to the first string literal conventionally. */
-            for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
-                if (pn2->pn_type == TOK_STRING)
-                    break;
-                if (!js_EmitTree(cx, cg, pn2))
-                    return JS_FALSE;
-                if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
-                    return JS_FALSE;
-            }
-
-            if (!pn2)
-                break;
-
-            /*
-             * Having seen a string literal, we know statically that the rest
-             * of the additions are string concatenation, so we emit them as a
-             * single concatn. First, do string conversion on the result of the
-             * preceding zero or more additions so that any side effects of
-             * string conversion occur before the next operand begins.
-             */
-            if (pn2 == pn->pn_head) {
-                index = 0;
-            } else {
-                if (!js_Emit1(cx, cg, JSOP_OBJTOSTR))
-                    return JS_FALSE;
-                index = 1;
-            }
-
-            for (; pn2; pn2 = pn2->pn_next, index++) {
-                if (!js_EmitTree(cx, cg, pn2))
-                    return JS_FALSE;
-                if (!pn2->isLiteral() && js_Emit1(cx, cg, JSOP_OBJTOSTR) < 0)
-                    return JS_FALSE;
-            }
-
-            EMIT_UINT16_IMM_OP(JSOP_CONCATN, index);
-            break;
-        }
       case TOK_BITOR:
       case TOK_BITXOR:
       case TOK_BITAND:
       case TOK_EQOP:
       case TOK_RELOP:
       case TOK_IN:
       case TOK_INSTANCEOF:
       case TOK_SHOP:
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3482,45 +3482,16 @@ BEGIN_CASE(JSOP_ADD)
             l += r;
             regs.sp--;
             regs.sp[-1].setNumber(l);
         }
     }
 }
 END_CASE(JSOP_ADD)
 
-BEGIN_CASE(JSOP_OBJTOSTR)
-{
-    const Value &ref = regs.sp[-1];
-    if (ref.isObject()) {
-        JSString *str = js_ValueToString(cx, ref);
-        if (!str)
-            goto error;
-        regs.sp[-1].setString(str);
-    }
-}
-END_CASE(JSOP_OBJTOSTR)
-
-BEGIN_CASE(JSOP_CONCATN)
-{
-    JSCharBuffer buf(cx);
-    uintN argc = GET_ARGC(regs.pc);
-    for (Value *vp = regs.sp - argc; vp < regs.sp; vp++) {
-        JS_ASSERT(vp->isPrimitive());
-        if (!js_ValueToCharBuffer(cx, *vp, buf))
-            goto error;
-    }
-    JSString *str = js_NewStringFromCharBuffer(cx, buf);
-    if (!str)
-        goto error;
-    regs.sp -= argc - 1;
-    regs.sp[-1].setString(str);
-}
-END_CASE(JSOP_CONCATN)
-
 #define BINARY_OP(OP)                                                         \
     JS_BEGIN_MACRO                                                            \
         double d1, d2;                                                        \
         if (!ValueToNumber(cx, regs.sp[-2], &d1) ||                           \
             !ValueToNumber(cx, regs.sp[-1], &d2)) {                           \
             goto error;                                                       \
         }                                                                     \
         double d = d1 OP d2;                                                  \
@@ -5890,24 +5861,23 @@ BEGIN_CASE(JSOP_SETTER)
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_BAD_GETTER_OR_SETTER,
                              (op == JSOP_GETTER)
                              ? js_getter_str
                              : js_setter_str);
         goto error;
     }
 
-    /*
-     * Getters and setters are just like watchpoints from an access control
-     * point of view.
-     */
+    /* Legacy security check. This can't fail. See bug 583850. */
     Value rtmp;
     uintN attrs;
-    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))
+    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &rtmp, &attrs)) {
+        JS_NOT_REACHED("getter/setter access check failed");
         goto error;
+    }
 
     PropertyOp getter, setter;
     if (op == JSOP_GETTER) {
         getter = CastAsPropertyOp(&rval.toObject());
         setter = PropertyStub;
         attrs = JSPROP_GETTER;
     } else {
         getter = PropertyStub;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -367,24 +367,19 @@ ParseIntStringHelper(JSContext *cx, cons
     /* 15.1.2.2 step 9. */
     int radix = maybeRadix;
     if (radix == 0) {
         if (end - s >= 2 && s[0] == '0' && (s[1] != 'x' && s[1] != 'X')) {
             /*
              * Non-standard: ES5 requires that parseInt interpret leading-zero
              * strings not starting with "0x" or "0X" as decimal (absent an
              * explicitly specified non-zero radix), but we continue to
-             * interpret such strings as octal when the caller is not in strict
-             * mode code.  This strictness check throws us off trace, but it
-             * only happens in code that doesn't specify an explicit, non-zero
-             * radix; thus it is easily avoidable, and idiomatic code will not
-             * suffer.
+             * interpret such strings as octal, as per ES3 and web practice.
              */
-            JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
-            radix = (fp && !fp->script->strictModeCode) ? 8 : 10;
+            radix = 8;
         } else {
             radix = 10;
         }
     }
 
     /* 15.1.2.2 step 10. */
     if (stripPrefix) {
         if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -137,17 +137,21 @@ static JSPropertySpec object_props[] = {
 };
 
 static JSBool
 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     /* Let CheckAccess get the slot's value, based on the access mode. */
     uintN attrs;
     id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
-    return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
+
+    /* Legacy security check. This can't fail. See bug 583850. */
+    JSBool ok = CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
+    JS_ASSERT(ok);
+    return ok;
 }
 
 static JSBool
 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     if (!vp->isObjectOrNull())
         return JS_TRUE;
 
@@ -158,20 +162,23 @@ obj_setProto(JSContext *cx, JSObject *ob
          * outer object. This ensures that any with statements only grant
          * access to the inner object.
          */
         OBJ_TO_INNER_OBJECT(cx, pobj);
         if (!pobj)
             return JS_FALSE;
     }
 
+    /* Legacy security check. This can't fail. See bug 583850. */
     uintN attrs;
     id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
-    if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
+    if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs)) {
+        JS_NOT_REACHED("setProto access check failed");
         return JS_FALSE;
+    }
 
     return SetProto(cx, obj, pobj, JS_TRUE);
 }
 
 #else  /* !JS_HAS_OBJ_PROTO_PROP */
 
 #define object_props NULL
 
@@ -1293,40 +1300,46 @@ obj_watch_handler(JSContext *cx, JSObjec
     return ok;
 }
 
 static JSBool
 obj_watch(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc <= 1) {
         js_ReportMissingArg(cx, *vp, 1);
-        return JS_FALSE;
+        return false;
     }
 
     JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
     if (!callable)
-        return JS_FALSE;
+        return false;
 
     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
     jsid propid;
     if (!ValueToId(cx, vp[2], &propid))
-        return JS_FALSE;
+        return false;
 
     JSObject *obj = ComputeThisFromVp(cx, vp);
+    if (!obj)
+        return false;
+
+    /* Legacy security check. This can't fail. See bug 583850. */
     Value tmp;
     uintN attrs;
-    if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
-        return JS_FALSE;
+    if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs)) {
+        JS_NOT_REACHED("watchpoint access check failed");
+        return false;
+    }
 
     vp->setUndefined();
 
     if (attrs & JSPROP_READONLY)
-        return JS_TRUE;
+        return true;
     if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
-        return JS_FALSE;
+        return false;
     return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
 }
 
 static JSBool
 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
@@ -1525,24 +1538,24 @@ js_obj_defineGetter(JSContext *cx, uintN
     PropertyOp getter = CastAsPropertyOp(&vp[3].toObject());
 
     jsid id;
     if (!ValueToId(cx, vp[2], &id))
         return JS_FALSE;
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
         return JS_FALSE;
-    /*
-     * Getters and setters are just like watchpoints from an access
-     * control point of view.
-     */
+
+    /* Legacy security check. This can't fail. See bug 583850. */
     Value junk;
     uintN attrs;
-    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
+    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs)) {
+        JS_NOT_REACHED("defineGetter access check failed");
         return JS_FALSE;
+    }
     vp->setUndefined();
     return obj->defineProperty(cx, id, UndefinedValue(), getter, PropertyStub,
                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
 }
 
 JS_FRIEND_API(JSBool)
 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
 {
@@ -1555,24 +1568,24 @@ js_obj_defineSetter(JSContext *cx, uintN
     PropertyOp setter = CastAsPropertyOp(&vp[3].toObject());
 
     jsid id;
     if (!ValueToId(cx, vp[2], &id))
         return JS_FALSE;
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
         return JS_FALSE;
-    /*
-     * Getters and setters are just like watchpoints from an access
-     * control point of view.
-     */
+
+    /* Legacy security check. This can't fail. See bug 583850. */
     Value junk;
     uintN attrs;
-    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
+    if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs)) {
+        JS_NOT_REACHED("defineSetter access check failed");
         return JS_FALSE;
+    }
     vp->setUndefined();
     return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter,
                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
 }
 
 static JSBool
 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
 {
@@ -1634,19 +1647,23 @@ obj_getPrototypeOf(JSContext *cx, uintN 
             return JS_FALSE;
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
         JS_free(cx, bytes);
         return JS_FALSE;
     }
 
     JSObject *obj = &vp[2].toObject();
+
+    /* Legacy security check. This can't fail. See bug 583850. */
     uintN attrs;
-    return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
-                       JSACC_PROTO, vp, &attrs);
+    JSBool ok = CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
+                            JSACC_PROTO, vp, &attrs);
+    JS_ASSERT(ok);
+    return ok;
 }
 
 extern JSBool
 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
                                const Value &getter, const Value &setter,
                                const Value &value, Value *vp)
 {
     /* We have our own property, so start creating the descriptor. */
@@ -1984,24 +2001,23 @@ DefinePropertyOnObject(JSContext *cx, JS
         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
             JS_ASSERT(!obj->getOps()->defineProperty);
             return js_DefineProperty(cx, obj, desc.id, &desc.value,
                                      PropertyStub, PropertyStub, desc.attrs);
         }
 
         JS_ASSERT(desc.isAccessorDescriptor());
 
-        /*
-         * Getters and setters are just like watchpoints from an access
-         * control point of view.
-         */
+        /* Legacy security check. This can't fail. See bug 583850. */
         Value dummy;
         uintN dummyAttrs;
-        if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
+        if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs)) {
+            JS_NOT_REACHED("defineProperty access check failed");
             return JS_FALSE;
+        }
 
         Value tmp = UndefinedValue();
         return js_DefineProperty(cx, obj, desc.id, &tmp,
                                  desc.getter(), desc.setter(), desc.attrs);
     }
 
     /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
     Value v = UndefinedValue();
@@ -2178,24 +2194,22 @@ DefinePropertyOnObject(JSContext *cx, JS
 
         if (desc.hasValue)
             v = desc.value;
         attrs = (desc.attrs & ~unchanged) | (sprop->attributes() & unchanged);
         getter = setter = PropertyStub;
     } else {
         JS_ASSERT(desc.isAccessorDescriptor());
 
-        /*
-         * Getters and setters are just like watchpoints from an access
-         * control point of view.
-         */
+        /* Legacy security check. This can't fail. See bug 583850. */
         Value dummy;
         if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs)) {
-             obj2->dropProperty(cx, current);
-             return JS_FALSE;
+            JS_NOT_REACHED("defineProperty access check failed");
+            obj2->dropProperty(cx, current);
+            return JS_FALSE;
         }
 
         JS_ASSERT_IF(sprop->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
         /* 8.12.9 step 12. */
         uintN changed = 0;
         if (desc.hasConfigurable)
             changed |= JSPROP_PERMANENT;
@@ -5573,49 +5587,16 @@ js_GetClassPrototype(JSContext *cx, JSOb
                 return true;
             }
         }
     }
 
     return FindClassPrototype(cx, scope, protoKey, protop, clasp);
 }
 
-/*
- * For shared precompilation of function objects, we support cloning on entry
- * to an execution context in which the function declaration or expression
- * should be processed as if it were not precompiled, where the precompiled
- * function's scope chain does not match the execution context's.  The cloned
- * function object carries its execution-context scope in its parent slot; it
- * links to the precompiled function (the "clone-parent") via its proto slot.
- *
- * Note that this prototype-based delegation leaves an unchecked access path
- * from the clone to the clone-parent's 'constructor' property.  If the clone
- * lives in a less privileged or shared scope than the clone-parent, this is
- * a security hole, a sharing hazard, or both.  Therefore we check all such
- * accesses with the following getter/setter pair, which we use when defining
- * 'constructor' in f.prototype for all function objects f.
- */
-static JSBool
-CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
-{
-    JSAtom *atom = cx->runtime->atomState.constructorAtom;
-    JS_ASSERT(id == ATOM_TO_JSID(atom));
-    uintN attrs;
-    return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs);
-}
-
-static JSBool
-CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
-{
-    JSAtom *atom = cx->runtime->atomState.constructorAtom;
-    JS_ASSERT(id == ATOM_TO_JSID(atom));
-    uintN attrs;
-    return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs);
-}
-
 JSBool
 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
 {
     /*
      * Use the given attributes for the prototype property of the constructor,
      * as user-defined constructors have a DontDelete prototype (which may be
      * reset), while native or "system" constructors have DontEnum | ReadOnly |
      * DontDelete.
@@ -5625,17 +5606,17 @@ js_SetClassPrototype(JSContext *cx, JSOb
         return JS_FALSE;
     }
 
     /*
      * ECMA says that Object.prototype.constructor, or f.prototype.constructor
      * for a user-defined function f, is DontEnum.
      */
     return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
-                                 ObjectOrNullValue(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0);
+                                 ObjectOrNullValue(ctor), PropertyStub, PropertyStub, 0);
 }
 
 JSBool
 js_PrimitiveToObject(JSContext *cx, Value *vp)
 {
     Value v = *vp;
     JS_ASSERT(v.isPrimitive());
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -209,18 +209,16 @@ js_GetVariableBytecodeLength(jsbytecode 
 uintN
 js_GetVariableStackUses(JSOp op, jsbytecode *pc)
 {
     JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
     JS_ASSERT(js_CodeSpec[op].nuses == -1);
     switch (op) {
       case JSOP_POPN:
         return GET_UINT16(pc);
-      case JSOP_CONCATN:
-        return GET_UINT16(pc);
       case JSOP_LEAVEBLOCK:
         return GET_UINT16(pc);
       case JSOP_LEAVEBLOCKEXPR:
         return GET_UINT16(pc) + 1;
       case JSOP_NEWARRAY:
         return GET_UINT16(pc);
       default:
         /* stack: fun, this, [argc arguments] */
@@ -3554,61 +3552,16 @@ Decompile(SprintStack *ss, jsbytecode *p
                         return NULL;
                     rval = POP_STR();
                     LOCAL_ASSERT(*rval != '\0');
                     js_printf(jp, "\t%s;\n", rval);
                     todo = -2;
                 }
                 break;
 
-              case JSOP_CONCATN:
-              {
-                argc = GET_UINT16(pc);
-                JS_ASSERT(argc > 0);
-
-                js::Vector<char *> argv(cx);
-                if (!argv.resize(argc))
-                    return NULL;
-
-                MUST_FLOW_THROUGH("out");
-                ok = JS_FALSE;
-
-                for (i = argc - 1; i >= 0; i--) {
-                    argv[i] = JS_strdup(cx, POP_STR_PREC(cs->prec + 1));
-                    if (!argv[i])
-                        goto out;
-                }
-
-                todo = Sprint(&ss->sprinter, "%s", argv[0]);
-                if (todo < 0)
-                    goto out;
-                for (i = 1; i < argc; i++) {
-                    if (Sprint(&ss->sprinter, " + %s", argv[i]) < 0)
-                        goto out;
-                }
-
-                /*
-                 * The only way that our next op could be a JSOP_ADD is
-                 * if we are about to concatenate at least one non-string
-                 * literal. Deal with that here in order to avoid extra
-                 * parentheses (because JSOP_ADD is left-associative).
-                 */
-                if (pc[len] == JSOP_ADD)
-                    saveop = JSOP_NOP;
-
-                ok = JS_TRUE;
-
-              out:
-                for (i = 0; i < argc; i++)
-                    JS_free(cx, argv[i]);
-                if (!ok)
-                    return NULL;
-                break;
-              }
-
               case JSOP_NEW:
               case JSOP_CALL:
               case JSOP_EVAL:
               case JSOP_APPLY:
               case JSOP_SETCALL:
                 argc = GET_ARGC(pc);
                 argv = (char **)
                     cx->malloc((size_t)(argc + 1) * sizeof *argv);
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -586,27 +586,16 @@ OPDEF(JSOP_TRACE,         228, "trace", 
  */
 OPDEF(JSOP_GETUPVAR_DBG,  229,"getupvar_dbg",  NULL,  3,  0,  1, 19,  JOF_UINT16|JOF_NAME)
 OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL,  3,  0,  2, 19,  JOF_UINT16|JOF_NAME|JOF_CALLOP)
 OPDEF(JSOP_DEFFUN_DBGFC,     231,"deffun_dbgfc",     NULL,  3,  0,  0,  0,  JOF_OBJECT|JOF_DECLARING)
 OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL,  5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING)
 OPDEF(JSOP_LAMBDA_DBGFC,     233,"lambda_dbgfc",     NULL,  3,  0,  1, 19,  JOF_OBJECT)
 
 /*
- * Concatenate N values, coercing to string if necessary, where N is concatn's
- * immediate.  See record_JSOP_CONCATN for recording behavior.
- */
-OPDEF(JSOP_CONCATN,       234,"concatn",       NULL,  3, -1,  1, 13,  JOF_UINT16|JOF_TMPSLOT2)
-
-/*
  * Joined function object as method optimization support.
  */
-OPDEF(JSOP_SETMETHOD,     235,"setmethod",     NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
-OPDEF(JSOP_INITMETHOD,    236,"initmethod",    NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
-OPDEF(JSOP_UNBRAND,       237,"unbrand",       NULL,  1,  1,  1,  0,  JOF_BYTE)
-OPDEF(JSOP_UNBRANDTHIS,   238,"unbrandthis",   NULL,  1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_SETMETHOD,     234,"setmethod",     NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
+OPDEF(JSOP_INITMETHOD,    235,"initmethod",    NULL,  3,  2,  1,  3,  JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
+OPDEF(JSOP_UNBRAND,       236,"unbrand",       NULL,  1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_UNBRANDTHIS,   237,"unbrandthis",   NULL,  1,  0,  0,  0,  JOF_BYTE)
 
-OPDEF(JSOP_SHARPINIT,     239,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
-
-/*
- * If the argument is an object, perform toString conversion (E-262-3 section 9.8).
- */
-OPDEF(JSOP_OBJTOSTR,      240,"objtostr",      NULL,  1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_SHARPINIT,     238,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -4934,27 +4934,33 @@ TraceRecorder::closeLoop(SlotMap& slotMa
             debug_only_printf(LC_TMTracer,
                               "Joining type-unstable trace to target fragment %p.\n",
                               (void*)peer);
             peer->dependentTrees.addUnique(tree);
             tree->linkedTrees.addUnique(peer);
         }
     } else {
         exit->exitType = LOOP_EXIT;
-        exit->target = tree;
         debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit,
                           getExitName(LOOP_EXIT));
 
         JS_ASSERT((fragment == fragment->root) == !!loopLabel);
         if (loopLabel) {
             lir->insBranch(LIR_j, NULL, loopLabel);
-            fragment->lastIns = lir->ins1(LIR_livep, lirbuf->state);
-        } else {
-            fragment->lastIns = lir->insGuard(LIR_x, NULL, createGuardRecord(exit));
-        }
+            lir->ins1(LIR_livep, lirbuf->state);
+        }
+
+        exit->target = tree;
+        /*
+         * This guard is dead code.  However, it must be present because it
+         * can keep alive values on the stack.  Without it, StackFilter can
+         * remove some stack stores that it shouldn't.  See bug 582766 comment
+         * 19.
+         */
+        fragment->lastIns = lir->insGuard(LIR_x, NULL, createGuardRecord(exit));
     }
 
     CHECK_STATUS_A(compile());
 
     debug_only_printf(LC_TMTreeVis, "TREEVIS CLOSELOOP EXIT=%p PEER=%p\n", (void*)exit, (void*)peer);
 
     JS_ASSERT(LookupLoop(traceMonitor, tree->ip, tree->globalObj, tree->globalShape, tree->argc) ==
               tree->first);
@@ -13741,16 +13747,25 @@ TraceRecorder::denseArrayElement(Value& 
     /* Don't let the hole value escape. Turn it into an undefined. */
     if (vp->isMagic()) {
         CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, MISMATCH_EXIT));
         v_ins = INS_UNDEFINED();
     }
     return RECORD_CONTINUE;
 }
 
+/* See comments in TypedArrayTemplate<double>::copyIndexToValue. */
+LIns *
+TraceRecorder::canonicalizeNaNs(LIns *dval_ins)
+{
+    /* NaNs are the only floating point values that do not == themselves. */
+    LIns *isnonnan_ins = lir->ins2(LIR_eqd, dval_ins, dval_ins);
+    return lir->insChoose(isnonnan_ins, dval_ins, lir->insImmD(js_NaN), true);
+}
+
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::typedArrayElement(Value& oval, Value& ival, Value*& vp, LIns*& v_ins,
                                  LIns*& addr_ins)
 {
     JS_ASSERT(oval.isObject() && ival.isInt32());
 
     JSObject* obj = &oval.toObject();
     LIns* obj_ins = get(&oval);
@@ -13820,21 +13835,21 @@ TraceRecorder::typedArrayElement(Value& 
         v_ins = lir->ins1(LIR_i2d, lir->insLoad(LIR_ldi, addr_ins, 0, ACCSET_OTHER));
         break;
       case js::TypedArray::TYPE_UINT32:
         addr_ins = lir->ins2(LIR_addp, data_ins, lir->ins2ImmI(LIR_lshp, pidx_ins, 2));
         v_ins = lir->ins1(LIR_ui2d, lir->insLoad(LIR_ldi, addr_ins, 0, ACCSET_OTHER));
         break;
       case js::TypedArray::TYPE_FLOAT32:
         addr_ins = lir->ins2(LIR_addp, data_ins, lir->ins2ImmI(LIR_lshp, pidx_ins, 2));
-        v_ins = lir->insLoad(LIR_ldf2d, addr_ins, 0, ACCSET_OTHER);
+        v_ins = canonicalizeNaNs(lir->insLoad(LIR_ldf2d, addr_ins, 0, ACCSET_OTHER));
         break;
       case js::TypedArray::TYPE_FLOAT64:
         addr_ins = lir->ins2(LIR_addp, data_ins, lir->ins2ImmI(LIR_lshp, pidx_ins, 3));
-        v_ins = lir->insLoad(LIR_ldd, addr_ins, 0, ACCSET_OTHER);
+        v_ins = canonicalizeNaNs(lir->insLoad(LIR_ldd, addr_ins, 0, ACCSET_OTHER));
         break;
       default:
         JS_NOT_REACHED("Unknown typed array type in tracer");
     }
 
     return ARECORD_CONTINUE;
 }
 
@@ -15922,60 +15937,16 @@ TraceRecorder::record_JSOP_HOLE()
 }
 
 AbortableRecordingStatus
 TraceRecorder::record_JSOP_TRACE()
 {
     return ARECORD_CONTINUE;
 }
 
-static const uint32 sMaxConcatNSize = 32;
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_OBJTOSTR()
-{
-    Value &v = stackval(-1);
-    JS_ASSERT_IF(cx->fp->imacpc, v.isPrimitive() &&
-                                 *cx->fp->imacpc == JSOP_OBJTOSTR);
-    if (v.isPrimitive())
-        return ARECORD_CONTINUE;
-    CHECK_STATUS_A(guardNativeConversion(v));
-    return InjectStatus(callImacro(objtostr_imacros.toString));
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_CONCATN()
-{
-    JSFrameRegs regs = *cx->regs;
-    uint32 argc = GET_ARGC(regs.pc);
-    Value *argBase = regs.sp - argc;
-
-    /* Prevent code/alloca explosion. */
-    if (argc > sMaxConcatNSize)
-        return ARECORD_STOP;
-
-    /* Build an array of the stringified primitives. */
-    int32_t bufSize = argc * sizeof(JSString *);
-    LIns *buf_ins = lir->insAlloc(bufSize);
-    int32_t d = 0;
-    for (Value *vp = argBase; vp != regs.sp; ++vp, d += sizeof(void *)) {
-        JS_ASSERT(vp->isPrimitive());
-        lir->insStore(stringify(*vp), buf_ins, d, ACCSET_OTHER);
-    }
-
-    /* Perform concatenation using a builtin. */
-    LIns *args[] = { lir->insImmI(argc), buf_ins, cx_ins };
-    LIns *result_ins = lir->insCall(&js_ConcatN_ci, args);
-    guard(false, lir->insEqP_0(result_ins), OOM_EXIT);
-
-    /* Update tracker with result. */
-    set(argBase, result_ins);
-    return ARECORD_CONTINUE;
-}
-
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_SETMETHOD()
 {
     return record_JSOP_SETPROP();
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_INITMETHOD()
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1248,16 +1248,17 @@ class TraceRecorder
                                                     Value* outp);
     JS_REQUIRES_STACK RecordingStatus propTail(JSObject* obj, nanojit::LIns* obj_ins,
                                                JSObject* obj2, PCVal pcval,
                                                uint32 *slotp, nanojit::LIns** v_insp,
                                                Value* outp);
     JS_REQUIRES_STACK RecordingStatus denseArrayElement(Value& oval, Value& idx, Value*& vp,
                                                         nanojit::LIns*& v_ins,
                                                         nanojit::LIns*& addr_ins);
+    JS_REQUIRES_STACK nanojit::LIns *canonicalizeNaNs(nanojit::LIns *dval_ins);
     JS_REQUIRES_STACK AbortableRecordingStatus typedArrayElement(Value& oval, Value& idx, Value*& vp,
                                                                  nanojit::LIns*& v_ins,
                                                                  nanojit::LIns*& addr_ins);
     JS_REQUIRES_STACK AbortableRecordingStatus getProp(JSObject* obj, nanojit::LIns* obj_ins);
     JS_REQUIRES_STACK AbortableRecordingStatus getProp(Value& v);
     JS_REQUIRES_STACK RecordingStatus getThis(nanojit::LIns*& this_ins);
 
     JS_REQUIRES_STACK void storeMagic(JSWhyMagic why, nanojit::LIns *addr_ins, ptrdiff_t offset,
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1226,24 +1226,50 @@ TypedArrayTemplate<uint32>::copyIndexToV
     vp->setNumber(val);
 }
 
 template<>
 void
 TypedArrayTemplate<float>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
 {
     float val = getIndex(index);
-    vp->setDouble(val);
+    double dval = val;
+
+    /*
+     * Doubles in typed arrays could be typed-punned arrays of integers. This
+     * could allow user code to break the engine-wide invariant that only
+     * canonical nans are stored into jsvals, which means user code could
+     * confuse the engine into interpreting a double-typed jsval as an
+     * object-typed jsval.
+     *
+     * This could be removed for platforms/compilers known to convert a 32-bit
+     * non-canonical nan to a 64-bit canonical nan.
+     */
+    if (JS_UNLIKELY(JSDOUBLE_IS_NaN(dval)))
+        dval = js_NaN;
+
+    vp->setDouble(dval);
 }
 
 template<>
 void
 TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, uint32 index, Value *vp)
 {
     double val = getIndex(index);
+
+    /*
+     * Doubles in typed arrays could be typed-punned arrays of integers. This
+     * could allow user code to break the engine-wide invariant that only
+     * canonical nans are stored into jsvals, which means user code could
+     * confuse the engine into interpreting a double-typed jsval as an
+     * object-typed jsval.
+     */
+    if (JS_UNLIKELY(JSDOUBLE_IS_NaN(val)))
+        val = js_NaN;
+
     vp->setDouble(val);
 }
 
 /***
  *** JS impl
  ***/
 
 /*
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -286,17 +286,17 @@ typedef union jsval_layout
         JSValueTag tag;
     } s;
     double asDouble;
 } jsval_layout;
 # elif JS_BITS_PER_WORD == 64
 typedef union jsval_layout
 {
     uint64 asBits;
-#ifndef _MSC_VER
+#ifndef _WIN64
     /* MSVC does not pack these correctly :-( */
     struct {
         uint64             payload47 : 47;
         JSValueTag         tag : 17;
     } debugView;
 #endif
     struct {
         union {
--- a/js/src/jsvector.h
+++ b/js/src/jsvector.h
@@ -37,16 +37,22 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsvector_h_
 #define jsvector_h_
 
 #include "jstl.h"
 
+/* Silence dire "bugs in previous versions of MSVC have been fixed" warnings */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4345)
+#endif
+
 namespace js {
 
 /*
  * This template class provides a default implementation for vector operations
  * when the element type is not known to be a POD, as judged by IsPodType.
  */
 template <class T, size_t N, class AP, bool IsPod>
 struct VectorImpl
@@ -781,9 +787,13 @@ Vector<T,N,AP>::replaceRawBuffer(T *p, s
         mLengthOrCapacity = length;  /* marks us as !usingInlineStorage() */
         heapBegin() = p;
         heapEnd() = heapBegin() + length;
     }
 }
 
 }  /* namespace js */
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 #endif /* jsvector_h_ */
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -200,17 +200,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 66)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 67)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/lirasm/LInsClasses.tbl
+++ b/js/src/lirasm/LInsClasses.tbl
@@ -92,18 +92,19 @@ CL_64(  LOP_Q_Q,        0)  // 14%  (non
 CL___(  LOP_D_D,        2)  // 16%  LIR_fneg
 
 CL___(  LOP_I_II,       6)  // 32%  LIR_add, LIR_and, LIR_eq, etc.
 CL_64(  LOP_Q_QQ,       7)  // 39%  LIR_qiadd, LIR_qiand, LIR_qeq, etc.
 CL_64(  LOP_Q_QI,       2)  // 41%  LIR_qilsh, LIR_qirsh, LIR_qursh
 CL___(  LOP_D_DD,       0)  // 51%  LIR_fadd, etc.
 
 // cmov has a low weight because is also used with LIR_div/LIR_mod.
-CL___(  LOP_I_BII,      1)  // 52%  LIR_cmov
-CL_64(  LOP_Q_BQQ,      2)  // 54%  LIR_qcmov
+CL___(  LOP_I_BII,      1)  // 52%  LIR_cmovi
+CL_64(  LOP_Q_BQQ,      1)  // 53%  LIR_cmovq
+CL___(  LOP_D_BDD,      1)  // 54%  LIR_cmovd
 
 CL___(  LOP_B_II,       3)  // 57%  LIR_eq,  LIR_lt,  etc
 CL_64(  LOP_B_QQ,       3)  // 60%  LIR_qeq, LIR_qlt, etc
 CL___(  LOP_B_DD,       3)  // 63%  LIR_feq, LIR_flt, etc
 
 CL_64(  LOP_Q_I,        1)  // 64%  LIR_i2q, LIR_u2q
 CL___(  LOP_D_I,        1)  // 65%  LIR_i2f, LIR_u2f
 CL_64(  LOP_I_Q,        1)  // 66%  LIR_q2i
--- a/js/src/lirasm/lirasm.cpp
+++ b/js/src/lirasm/lirasm.cpp
@@ -1034,16 +1034,17 @@ FragmentAssembler::assembleFragment(LirT
             need(2);
             ins = mLir->ins2(mOpcode,
                              ref(mTokens[0]),
                              ref(mTokens[1]));
             break;
 
           case LIR_cmovi:
           CASE64(LIR_cmovq:)
+          case LIR_cmovd:
             need(3);
             ins = mLir->ins3(mOpcode,
                              ref(mTokens[0]),
                              ref(mTokens[1]),
                              ref(mTokens[2]));
             break;
 
           case LIR_j:
@@ -1394,16 +1395,19 @@ FragmentAssembler::assembleRandomFragmen
     vector<LOpcode> I_BII_ops;
     I_BII_ops.push_back(LIR_cmovi);
 
 #ifdef NANOJIT_64BIT
     vector<LOpcode> Q_BQQ_ops;
     Q_BQQ_ops.push_back(LIR_cmovq);
 #endif
 
+    vector<LOpcode> D_BDD_ops;
+    D_BDD_ops.push_back(LIR_cmovd);
+
     vector<LOpcode> B_II_ops;
     B_II_ops.push_back(LIR_eqi);
     B_II_ops.push_back(LIR_lti);
     B_II_ops.push_back(LIR_gti);
     B_II_ops.push_back(LIR_lei);
     B_II_ops.push_back(LIR_gei);
     B_II_ops.push_back(LIR_ltui);
     B_II_ops.push_back(LIR_gtui);
@@ -1735,16 +1739,24 @@ FragmentAssembler::assembleRandomFragmen
             if (!Bs.empty() && !Qs.empty()) {
                 ins = mLir->ins3(rndPick(Q_BQQ_ops), rndPick(Bs), rndPick(Qs), rndPick(Qs));
                 addOrReplace(Qs, ins);
                 n++;
             }
             break;
 #endif
 
+        case LOP_D_BDD:
+            if (!Bs.empty() && !Ds.empty()) {
+                ins = mLir->ins3(rndPick(D_BDD_ops), rndPick(Bs), rndPick(Ds), rndPick(Ds));
+                addOrReplace(Ds, ins);
+                n++;
+            }
+            break;
+
         case LOP_B_II:
            if (!Is.empty()) {
                ins = mLir->ins2(rndPick(B_II_ops), rndPick(Is), rndPick(Is));
                addOrReplace(Bs, ins);
                n++;
            }
             break;
 
--- a/js/src/nanojit-import-rev
+++ b/js/src/nanojit-import-rev
@@ -1,1 +1,1 @@
-962916dc590fe3f55d15f00ac91b13e4ec4409a2
+186f3f376d662375ff9182cf0b470468335ef442
--- a/js/src/nanojit/Assembler.cpp
+++ b/js/src/nanojit/Assembler.cpp
@@ -87,17 +87,17 @@ namespace nanojit
         verbose_only( outlineEOL[0] = '\0'; )
 
         reset();
     }
 
     // Per-opcode register hint table.  Default to no hints for all
     // instructions.  It's not marked const because individual back-ends can
     // install hint values for opcodes of interest in nInit().
-    RegisterMask hints[LIR_sentinel+1] = {
+    RegisterMask Assembler::nHints[LIR_sentinel+1] = {
 #define OP___(op, number, repKind, retType, isCse) \
         0,
 #include "LIRopcode.tbl"
 #undef OP___
         0
     };
 
 #ifdef _DEBUG
@@ -819,17 +819,17 @@ namespace nanojit
         // we are done producing the exit logic for the guard so demark where our exit block code begins
         NIns* jmpTarget = _nIns;     // target in exit path for our mainline conditional jump
 
         // swap back pointers, effectively storing the last location used in the exit path
         swapCodeChunks();
         _inExit = false;
 
         //verbose_only( verbose_outputf("         LIR_xt/xf swapCodeChunks, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) );
-        verbose_only( verbose_outputf("%010lx:", (unsigned long)jmpTarget);)
+        verbose_only( verbose_outputf("%p:", jmpTarget);)
         verbose_only( verbose_outputf("----------------------------------- ## BEGIN exit block (LIR_xt|LIR_xf)") );
 
 #ifdef NANOJIT_IA32
         NanoAssertMsgf(_fpuStkDepth == _sv_fpuStkDepth, "LIR_xtf, _fpuStkDepth=%d, expect %d",_fpuStkDepth, _sv_fpuStkDepth);
         debug_only( _fpuStkDepth = _sv_fpuStkDepth; _sv_fpuStkDepth = 9999; )
 #endif
 
         return jmpTarget;
@@ -1510,16 +1510,17 @@ namespace nanojit
                     ins->oprnd2()->setResultLive();
                     if (ins->isExtant()) {
                         asm_qjoin(ins);
                     }
                     break;
 #endif
                 case LIR_cmovi:
                 CASE64(LIR_cmovq:)
+                case LIR_cmovd:
                     countlir_cmov();
                     ins->oprnd1()->setResultLive();
                     ins->oprnd2()->setResultLive();
                     ins->oprnd3()->setResultLive();
                     if (ins->isExtant()) {
                         asm_cmov(ins);
                     }
                     break;
--- a/js/src/nanojit/Assembler.h
+++ b/js/src/nanojit/Assembler.h
@@ -169,17 +169,17 @@ namespace nanojit
 
     inline uint32_t AR::stackSlotsNeeded() const
     {
         // NB: _highWaterMark is an index, not a count
         return _highWaterMark+1;
     }
 
     #ifndef AVMPLUS_ALIGN16
-        #ifdef AVMPLUS_WIN32
+        #ifdef _MSC_VER
             #define AVMPLUS_ALIGN16(type) __declspec(align(16)) type
         #else
             #define AVMPLUS_ALIGN16(type) type __attribute__ ((aligned (16)))
         #endif
     #endif
 
     // error codes
     enum AssmError
@@ -473,17 +473,17 @@ namespace nanojit
             // platform specific implementation (see NativeXXX.cpp file)
             void        nInit(AvmCore *);
             void        nBeginAssembly();
             Register    nRegisterAllocFromSet(RegisterMask set);
             void        nRegisterResetAll(RegAlloc& a);
             static void nPatchBranch(NIns* branch, NIns* location);
             void        nFragExit(LIns* guard);
 
-            RegisterMask nHints[LIR_sentinel];
+            static RegisterMask nHints[LIR_sentinel+1];
             RegisterMask nHint(LIns* ins);
 
             // A special entry for hints[];  if an opcode has this value, we call
             // nHint() in the back-end.  Used for cases where you need to look at more
             // than just the opcode to decide.
             static const RegisterMask PREFER_SPECIAL = 0xffffffff;
 
             // platform specific methods
--- a/js/src/nanojit/LIR.cpp
+++ b/js/src/nanojit/LIR.cpp
@@ -1149,17 +1149,17 @@ namespace nanojit
             LOpcode op = LIR_cmovi;
             if (iftrue->isI() && iffalse->isI()) {
                 op = LIR_cmovi;
 #ifdef NANOJIT_64BIT
             } else if (iftrue->isQ() && iffalse->isQ()) {
                 op = LIR_cmovq;
 #endif
             } else if (iftrue->isD() && iffalse->isD()) {
-                NanoAssertMsg(0, "LIR_fcmov doesn't exist yet, sorry");
+                op = LIR_cmovd;
             } else {
                 NanoAssert(0);  // type error
             }
             return ins3(op, cond, iftrue, iffalse);
         }
 
         LIns* ncond = ins1(LIR_negi, cond); // cond ? -1 : 0
         return ins2(LIR_ori,
@@ -1473,16 +1473,17 @@ namespace nanojit
                 case LIR_file:
                 case LIR_line:
                     live.add(ins->oprnd1(), 0);
                     live.add(ins->oprnd2(), 0);
                     break;
 
                 case LIR_cmovi:
                 CASE64(LIR_cmovq:)
+                case LIR_cmovd:
                     live.add(ins->oprnd1(), 0);
                     live.add(ins->oprnd2(), 0);
                     live.add(ins->oprnd3(), 0);
                     break;
 
                 case LIR_calli:
                 case LIR_calld:
                 CASE64(LIR_callq:)
@@ -1622,40 +1623,38 @@ namespace nanojit
                 }
             }
             NanoAssert(VMPI_strlen(b) < buf->len);
         }
         return buf->buf;
     }
 
     char* LInsPrinter::formatImmI(RefBuf* buf, int32_t c) {
-        if (-10000 < c || c < 10000) {
+        if (-10000 < c && c < 10000) {
             VMPI_snprintf(buf->buf, buf->len, "%d", c);
         } else {
 #if !defined NANOJIT_64BIT
             formatAddr(buf, (void*)c);
 #else
             VMPI_snprintf(buf->buf, buf->len, "0x%x", (unsigned int)c);
 #endif
         }
         return buf->buf;
     }
 
+#if defined NANOJIT_64BIT
     char* LInsPrinter::formatImmQ(RefBuf* buf, uint64_t c) {
-        if (-10000 < (int64_t)c || c < 10000) {
+        if (-10000 < (int64_t)c && c < 10000) {
             VMPI_snprintf(buf->buf, buf->len, "%dLL", (int)c);
         } else {
-#if defined NANOJIT_64BIT
             formatAddr(buf, (void*)c);
-#else
-            VMPI_snprintf(buf->buf, buf->len, "0x%llxLL", c);
-#endif
         }
         return buf->buf;
     }
+#endif
 
     char* LInsPrinter::formatImmD(RefBuf* buf, double c) {
         VMPI_snprintf(buf->buf, buf->len, "%g", c);
         return buf->buf;
     }
 
     char* LInsPrinter::formatAddr(RefBuf* buf, void* p)
     {
@@ -1897,16 +1896,17 @@ namespace nanojit
 #endif
                 VMPI_snprintf(s, n, "%s = %s %s, %s", formatRef(&b1, i), lirNames[op],
                     formatRef(&b2, i->oprnd1()),
                     formatRef(&b3, i->oprnd2()));
                 break;
 
             CASE64(LIR_cmovq:)
             case LIR_cmovi:
+            case LIR_cmovd:
                 VMPI_snprintf(s, n, "%s = %s %s ? %s : %s", formatRef(&b1, i), lirNames[op],
                     formatRef(&b2, i->oprnd1()),
                     formatRef(&b3, i->oprnd2()),
                     formatRef(&b4, i->oprnd3()));
                 break;
 
             case LIR_ldi:
             CASE64(LIR_ldq:)
@@ -3146,16 +3146,22 @@ namespace nanojit
 #ifdef NANOJIT_64BIT
         case LIR_cmovq:
             checkLInsIsACondOrConst(op, 1, a);
             formals[1] = LTy_Q;
             formals[2] = LTy_Q;
             break;
 #endif
 
+        case LIR_cmovd:
+            checkLInsIsACondOrConst(op, 1, a);
+            formals[1] = LTy_D;
+            formals[2] = LTy_D;
+            break;
+
         default:
             NanoAssert(0);
         }
 
         typeCheckArgs(op, nArgs, formals, args);
 
         return out->ins3(op, a, b, c);
     }
--- a/js/src/nanojit/LIR.h
+++ b/js/src/nanojit/LIR.h
@@ -450,17 +450,18 @@ namespace nanojit
 #endif
             op == LIR_reti || op == LIR_retd;
     }
     inline bool isCmovOpcode(LOpcode op) {
         return
 #if defined NANOJIT_64BIT
             op == LIR_cmovq ||
 #endif
-            op == LIR_cmovi;
+            op == LIR_cmovi ||
+            op == LIR_cmovd;
     }
     inline bool isCmpIOpcode(LOpcode op) {
         return LIR_eqi <= op && op <= LIR_geui;
     }
     inline bool isCmpSIOpcode(LOpcode op) {
         return LIR_eqi <= op && op <= LIR_gei;
     }
     inline bool isCmpUIOpcode(LOpcode op) {
@@ -1737,17 +1738,19 @@ namespace nanojit
 
     class LInsPrinter
     {
     private:
         Allocator& alloc;
         const int EMB_NUM_USED_ACCS;
 
         char *formatImmI(RefBuf* buf, int32_t c);
+#ifdef NANOJIT_64BIT
         char *formatImmQ(RefBuf* buf, uint64_t c);
+#endif
         char *formatImmD(RefBuf* buf, double c);
         void formatGuard(InsBuf* buf, LIns* ins);       // defined by the embedder
         void formatGuardXov(InsBuf* buf, LIns* ins);    // defined by the embedder
         static const char* accNames[];                  // defined by the embedder
 
     public:
 
         LInsPrinter(Allocator& alloc, int embNumUsedAccs)
--- a/js/src/nanojit/LIRopcode.tbl
+++ b/js/src/nanojit/LIRopcode.tbl
@@ -240,64 +240,63 @@ OP_64(geuq,     71, Op2,  I,    1)  // u
 OP_UN(72)
 
 OP___(eqd,      73, Op2,  I,    1)  // double equality
 OP___(ltd,      74, Op2,  I,    1)  // double less-than
 OP___(gtd,      75, Op2,  I,    1)  // double greater-than
 OP___(led,      76, Op2,  I,    1)  // double less-than-or-equal
 OP___(ged,      77, Op2,  I,    1)  // double greater-than-or-equal
 
-OP_UN(78)
-
 //---------------------------------------------------------------------------
 // Arithmetic
 //---------------------------------------------------------------------------
-OP___(negi,     79, Op1,  I,    1)  // negate int
-OP___(addi,     80, Op2,  I,    1)  // add int
-OP___(subi,     81, Op2,  I,    1)  // subtract int
-OP___(muli,     82, Op2,  I,    1)  // multiply int
-OP_86(divi,     83, Op2,  I,    1)  // divide int
+OP___(negi,     78, Op1,  I,    1)  // negate int
+OP___(addi,     79, Op2,  I,    1)  // add int
+OP___(subi,     80, Op2,  I,    1)  // subtract int
+OP___(muli,     81, Op2,  I,    1)  // multiply int
+OP_86(divi,     82, Op2,  I,    1)  // divide int
 // LIR_modi is a hack.  It's only used on i386/X64.  The operand is the result
 // of a LIR_divi because on i386/X64 div and mod results are computed by the
 // same instruction.
-OP_86(modi,     84, Op1,  I,    1)  // modulo int
+OP_86(modi,     83, Op1,  I,    1)  // modulo int
 
-OP___(noti,     85, Op1,  I,    1)  // bitwise-NOT int
-OP___(andi,     86, Op2,  I,    1)  // bitwise-AND int
-OP___(ori,      87, Op2,  I,    1)  // bitwise-OR int
-OP___(xori,     88, Op2,  I,    1)  // bitwise-XOR int
+OP___(noti,     84, Op1,  I,    1)  // bitwise-NOT int
+OP___(andi,     85, Op2,  I,    1)  // bitwise-AND int
+OP___(ori,      86, Op2,  I,    1)  // bitwise-OR int
+OP___(xori,     87, Op2,  I,    1)  // bitwise-XOR int
 
-OP___(lshi,     89, Op2,  I,    1)  // left shift int
-OP___(rshi,     90, Op2,  I,    1)  // right shift int (>>)
-OP___(rshui,    91, Op2,  I,    1)  // right shift unsigned int (>>>)
+OP___(lshi,     88, Op2,  I,    1)  // left shift int
+OP___(rshi,     89, Op2,  I,    1)  // right shift int (>>)
+OP___(rshui,    90, Op2,  I,    1)  // right shift unsigned int (>>>)
 
-OP_64(addq,     92, Op2,  Q,    1)  // add quad
-OP_64(subq,     93, Op2,  Q,    1)  // subtract quad
+OP_64(addq,     91, Op2,  Q,    1)  // add quad
+OP_64(subq,     92, Op2,  Q,    1)  // subtract quad
 
-OP_64(andq,     94, Op2,  Q,    1)  // bitwise-AND quad
-OP_64(orq,      95, Op2,  Q,    1)  // bitwise-OR quad
-OP_64(xorq,     96, Op2,  Q,    1)  // bitwise-XOR quad
+OP_64(andq,     93, Op2,  Q,    1)  // bitwise-AND quad
+OP_64(orq,      94, Op2,  Q,    1)  // bitwise-OR quad
+OP_64(xorq,     95, Op2,  Q,    1)  // bitwise-XOR quad
 
-OP_64(lshq,     97, Op2,  Q,    1)  // left shift quad;           2nd operand is an int
-OP_64(rshq,     98, Op2,  Q,    1)  // right shift quad;          2nd operand is an int
-OP_64(rshuq,    99, Op2,  Q,    1)  // right shift unsigned quad; 2nd operand is an int
+OP_64(lshq,     96, Op2,  Q,    1)  // left shift quad;           2nd operand is an int
+OP_64(rshq,     97, Op2,  Q,    1)  // right shift quad;          2nd operand is an int
+OP_64(rshuq,    98, Op2,  Q,    1)  // right shift unsigned quad; 2nd operand is an int
 
-OP___(negd,    100, Op1,  D,    1)  // negate double
-OP___(addd,    101, Op2,  D,    1)  // add double
-OP___(subd,    102, Op2,  D,    1)  // subtract double
-OP___(muld,    103, Op2,  D,    1)  // multiply double
-OP___(divd,    104, Op2,  D,    1)  // divide double
+OP___(negd,     99, Op1,  D,    1)  // negate double
+OP___(addd,    100, Op2,  D,    1)  // add double
+OP___(subd,    101, Op2,  D,    1)  // subtract double
+OP___(muld,    102, Op2,  D,    1)  // multiply double
+OP___(divd,    103, Op2,  D,    1)  // divide double
 // LIR_modd is just a place-holder opcode, ie. the back-ends cannot generate
 // code for it.  It's used in TraceMonkey briefly but is always demoted to a
 // LIR_modl or converted to a function call before Nanojit has to do anything
 // serious with it.
-OP___(modd,    105, Op2,  D,    1)  // modulo double
+OP___(modd,    104, Op2,  D,    1)  // modulo double
 
-OP___(cmovi,   106, Op3,  I,    1)  // conditional move int
-OP_64(cmovq,   107, Op3,  Q,    1)  // conditional move quad
+OP___(cmovi,   105, Op3,  I,    1)  // conditional move int
+OP_64(cmovq,   106, Op3,  Q,    1)  // conditional move quad
+OP___(cmovd,   107, Op3,  D,    1)  // conditional move double
 
 //---------------------------------------------------------------------------
 // Conversions
 //---------------------------------------------------------------------------
 OP_64(i2q,     108, Op1,  Q,    1)  // sign-extend int to quad
 OP_64(ui2uq,   109, Op1,  Q,    1)  // zero-extend unsigned int to unsigned quad
 OP_64(q2i,     110, Op1,  I,    1)  // truncate quad to int (removes the high 32 bits)
 
--- a/js/src/nanojit/Native.h
+++ b/js/src/nanojit/Native.h
@@ -147,17 +147,17 @@ namespace nanojit {
         #define gpn(r)                    regNames[(r)]
     #elif defined(NJ_VERBOSE)
         // Used for printing native instructions.  Like Assembler::outputf(),
         // but only outputs if LC_Native is set.  Also prepends the output
         // with the address of the current native instruction.
         #define asm_output(...) do { \
             if (_logc->lcbits & LC_Native) { \
                 outline[0]='\0'; \
-               VMPI_sprintf(outline, "%010lx   ", (unsigned long)_nIns); \
+               VMPI_sprintf(outline, "%p   ", _nIns); \
                 sprintf(&outline[13], ##__VA_ARGS__); \
                 output(); \
             } \
         } while (0) /* no semi */
         #define gpn(r)                  regNames[(r)]
     #else
         #define asm_output(...)
         #define gpn(r)
--- a/js/src/nanojit/NativeARM.cpp
+++ b/js/src/nanojit/NativeARM.cpp
@@ -2763,37 +2763,69 @@ Assembler::asm_load32(LIns* ins)
 void
 Assembler::asm_cmov(LIns* ins)
 {
     LIns* condval = ins->oprnd1();
     LIns* iftrue  = ins->oprnd2();
     LIns* iffalse = ins->oprnd3();
 
     NanoAssert(condval->isCmp());
-    NanoAssert(ins->opcode() == LIR_cmovi && iftrue->isI() && iffalse->isI());
-
-    const Register rr = deprecated_prepResultReg(ins, GpRegs);
-
-    // this code assumes that neither LD nor MR nor MRcc set any of the condition flags.
-    // (This is true on Intel, is it true on all architectures?)
-    const Register iffalsereg = findRegFor(iffalse, GpRegs & ~rmask(rr));
-    switch (condval->opcode()) {
-        // note that these are all opposites...
-        case LIR_eqi:    MOVNE(rr, iffalsereg);  break;
-        case LIR_lti:    MOVGE(rr, iffalsereg);  break;
-        case LIR_lei:    MOVGT(rr, iffalsereg);  break;
-        case LIR_gti:    MOVLE(rr, iffalsereg);  break;
-        case LIR_gei:    MOVLT(rr, iffalsereg);  break;
-        case LIR_ltui:   MOVHS(rr, iffalsereg);  break;
-        case LIR_leui:   MOVHI(rr, iffalsereg);  break;
-        case LIR_gtui:   MOVLS(rr, iffalsereg);  break;
-        case LIR_geui:   MOVLO(rr, iffalsereg);  break;
-        default: debug_only( NanoAssert(0) );    break;
+    NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) ||
+               (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD()));
+
+    RegisterMask allow = ins->isD() ? FpRegs : GpRegs;
+
+    Register rr = prepareResultReg(ins, allow);
+
+    Register rf = findRegFor(iffalse, allow & ~rmask(rr));
+
+    // If 'iftrue' isn't in a register, it can be clobbered by 'ins'.
+    Register rt = iftrue->isInReg() ? iftrue->getReg() : rr;
+
+    if (ins->isop(LIR_cmovd)) {
+        NIns* target = _nIns;
+        asm_nongp_copy(rr, rf);
+        asm_branch(false, condval, target);
+        if (rr != rt)
+            asm_nongp_copy(rr, rt);
+        freeResourcesOf(ins);
+        if (!iftrue->isInReg()) {
+            NanoAssert(rt == rr);
+            findSpecificRegForUnallocated(iftrue, rr);
+        }
+        return;
     }
-    /*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr);
+
+    // WARNING: We cannot generate any code that affects the condition
+    // codes between the MRcc generation here and the asm_cmp() call
+    // below.  See asm_cmp() for more details.
+    if (ins->isop(LIR_cmovi)) {
+        switch (condval->opcode()) {
+            // note that these are all opposites...
+            case LIR_eqi:    MOVNE(rr, rf);  break;
+            case LIR_lti:    MOVGE(rr, rf);  break;
+            case LIR_lei:    MOVGT(rr, rf);  break;
+            case LIR_gti:    MOVLE(rr, rf);  break;
+            case LIR_gei:    MOVLT(rr, rf);  break;
+            case LIR_ltui:   MOVHS(rr, rf);  break;
+            case LIR_leui:   MOVHI(rr, rf);  break;
+            case LIR_gtui:   MOVLS(rr, rf);  break;
+            case LIR_geui:   MOVLO(rr, rf);  break;
+            default: debug_only( NanoAssert(0) );    break;
+        }
+    }
+    if (rr != rt)
+        MR(rr, rt);
+
+    freeResourcesOf(ins);
+    if (!iftrue->isInReg()) {
+        NanoAssert(rt == rr);
+        findSpecificRegForUnallocated(iftrue, rr);
+    }
+
     asm_cmp(condval);
 }
 
 void
 Assembler::asm_qhi(LIns* ins)
 {
     Register rr = deprecated_prepResultReg(ins, GpRegs);
     LIns *q = ins->oprnd1();
--- a/js/src/nanojit/NativeSparc.cpp
+++ b/js/src/nanojit/NativeSparc.cpp
@@ -655,16 +655,17 @@ namespace nanojit
                 allow &= ~rmask(rb);
             }
         else if ((op == LIR_addi || op == LIR_addxovi) && lhs->isop(LIR_allocp) && rhs->isImmI()) {
             // add alloc+const, use lea
             Register rr = deprecated_prepResultReg(ins, allow);
             int d = findMemFor(lhs) + rhs->immI();
             ADD(FP, L2, rr);
             SET32(d, L2);
+            return;
         }
 
         Register rr = deprecated_prepResultReg(ins, allow);
         // if this is last use of lhs in reg, we can re-use result reg
         // else, lhs already has a register assigned.
         Register ra = ( !lhs->isInReg()
                       ? findSpecificRegFor(lhs, rr)
                       : lhs->deprecated_getReg() );
--- a/js/src/nanojit/NativeX64.cpp
+++ b/js/src/nanojit/NativeX64.cpp
@@ -1105,43 +1105,61 @@ namespace nanojit
     }
 
     void Assembler::asm_cmov(LIns *ins) {
         LIns* cond    = ins->oprnd1();
         LIns* iftrue  = ins->oprnd2();
         LIns* iffalse = ins->oprnd3();
         NanoAssert(cond->isCmp());
         NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) ||
-                   (ins->isop(LIR_cmovq) && iftrue->isQ() && iffalse->isQ()));
+                   (ins->isop(LIR_cmovq) && iftrue->isQ() && iffalse->isQ()) ||
+                   (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD()));
 
-        Register rr = prepareResultReg(ins, GpRegs);
+        RegisterMask allow = ins->isD() ? FpRegs : GpRegs;
 
-        Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr));
+        Register rr = prepareResultReg(ins, allow);
+
+        Register rf = findRegFor(iffalse, allow & ~rmask(rr));
 
         // If 'iftrue' isn't in a register, it can be clobbered by 'ins'.
         Register rt = iftrue->isInReg() ? iftrue->getReg() : rr;
 
+        if (ins->isop(LIR_cmovd)) {
+            NIns* target = _nIns;
+            asm_nongp_copy(rr, rf);
+            asm_branch(false, cond, target);
+            if (rr != rt)
+                asm_nongp_copy(rr, rt);
+            freeResourcesOf(ins);
+            if (!iftrue->isInReg()) {
+                NanoAssert(rt == rr);
+                findSpecificRegForUnallocated(iftrue, rr);
+            }
+            return;
+        }
+
         // WARNING: We cannot generate any code that affects the condition
         // codes between the MRcc generation here and the asm_cmp() call
         // below.  See asm_cmp() for more details.
         LOpcode condop = cond->opcode();
-        if (ins->opcode() == LIR_cmovi) {
+        if (ins->isop(LIR_cmovi)) {
             switch (condop) {
             case LIR_eqi:  case LIR_eqq:    CMOVNE( rr, rf);  break;
             case LIR_lti:  case LIR_ltq:    CMOVNL( rr, rf);  break;
             case LIR_gti:  case LIR_gtq:    CMOVNG( rr, rf);  break;
             case LIR_lei:  case LIR_leq:    CMOVNLE(rr, rf);  break;
             case LIR_gei:  case LIR_geq:    CMOVNGE(rr, rf);  break;
             case LIR_ltui: case LIR_ltuq:   CMOVNB( rr, rf);  break;
             case LIR_gtui: case LIR_gtuq:   CMOVNA( rr, rf);  break;
             case LIR_leui: case LIR_leuq:   CMOVNBE(rr, rf);  break;
             case LIR_geui: case LIR_geuq:   CMOVNAE(rr, rf);  break;
             default:                        NanoAssert(0);    break;
             }
         } else {
+            NanoAssert(ins->isop(LIR_cmovq));
             switch (condop) {
             case LIR_eqi:  case LIR_eqq:    CMOVQNE( rr, rf); break;
             case LIR_lti:  case LIR_ltq:    CMOVQNL( rr, rf); break;
             case LIR_gti:  case LIR_gtq:    CMOVQNG( rr, rf); break;
             case LIR_lei:  case LIR_leq:    CMOVQNLE(rr, rf); break;
             case LIR_gei:  case LIR_geq:    CMOVQNGE(rr, rf); break;
             case LIR_ltui: case LIR_ltuq:   CMOVQNB( rr, rf); break;
             case LIR_gtui: case LIR_gtuq:   CMOVQNA( rr, rf); break;
--- a/js/src/nanojit/NativeX64.h
+++ b/js/src/nanojit/NativeX64.h
@@ -323,17 +323,17 @@ namespace nanojit
         X86_sete    = 0xC0940F0000000003LL, // no-rex version of X64_sete
         X86_setnp   = 0xC09B0F0000000003LL  // no-rex set byte if odd parity (ordered fcmp result) (PF == 0)
     };
 
     typedef uint32_t RegisterMask;
 
     static const RegisterMask GpRegs = 0xffff;
     static const RegisterMask FpRegs = 0xffff0000;
-#ifdef _MSC_VER
+#ifdef _WIN64
     static const RegisterMask SavedRegs = 1<<RBX | 1<<RSI | 1<<RDI | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15;
     static const int NumSavedRegs = 7; // rbx, rsi, rdi, r12-15
     static const int NumArgRegs = 4;
 #else
     static const RegisterMask SavedRegs = 1<<RBX | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15;
     static const int NumSavedRegs = 5; // rbx, r12-15
     static const int NumArgRegs = 6;
 #endif
--- a/js/src/nanojit/Nativei386.cpp
+++ b/js/src/nanojit/Nativei386.cpp
@@ -834,17 +834,17 @@ namespace nanojit
     inline void Assembler::FDIV( I32 d, R b) { count_fpu(); FPUm(0xdc06, d, b); asm_output("fdiv %d(%s)", d,gpn(b)); }
     inline void Assembler::FDIVR(I32 d, R b) { count_fpu(); FPUm(0xdc07, d, b); asm_output("fdivr %d(%s)",d,gpn(b)); }
 
     inline void Assembler::FADDdm( const double *dm) { count_ldq(); FPUdm(0xdc00, dm); asm_output("fadd (%p)", (void*)dm); }
     inline void Assembler::FSUBRdm(const double* dm) { count_ldq(); FPUdm(0xdc05, dm); asm_output("fsubr (%p)",(void*)dm); }
     inline void Assembler::FMULdm( const double* dm) { count_ldq(); FPUdm(0xdc01, dm); asm_output("fmul (%p)", (void*)dm); }
     inline void Assembler::FDIVRdm(const double* dm) { count_ldq(); FPUdm(0xdc07, dm); asm_output("fdivr (%p)",(void*)dm); }
 
-    inline void Assembler::FINCSTP()   { count_fpu(); FPUc(0xd9f7);    asm_output("fincstp"); }
+    inline void Assembler::FINCSTP()   { count_fpu(); FPUc(0xd9f7); asm_output("fincstp"); fpu_pop(); }
 
     inline void Assembler::FCOMP()     { count_fpu(); FPUc(0xD8D9);    asm_output("fcomp"); fpu_pop();}
     inline void Assembler::FCOMPP()    { count_fpu(); FPUc(0xDED9);    asm_output("fcompp"); fpu_pop();fpu_pop();}
     inline void Assembler::FLDr(R r)   { count_ldq(); FPU(0xd9c0,r);   asm_output("fld %s",gpn(r)); fpu_push(); }
     inline void Assembler::EMMS()      { count_fpu(); FPUc(0x0f77);    asm_output("emms"); }
 
     // standard direct call
     inline void Assembler::CALL(const CallInfo* ci) {
@@ -2016,25 +2016,73 @@ namespace nanojit
 
     void Assembler::asm_cmov(LIns* ins)
     {
         LIns* condval = ins->oprnd1();
         LIns* iftrue  = ins->oprnd2();
         LIns* iffalse = ins->oprnd3();
 
         NanoAssert(condval->isCmp());
-        NanoAssert(ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI());
-
-        Register rr = prepareResultReg(ins, GpRegs);
-
-        Register rf = findRegFor(iffalse, GpRegs & ~rmask(rr));
+        NanoAssert((ins->isop(LIR_cmovi) && iftrue->isI() && iffalse->isI()) ||
+                   (ins->isop(LIR_cmovd) && iftrue->isD() && iffalse->isD()));
+
+        if (!_config.i386_sse2 && ins->isop(LIR_cmovd)) {
+            debug_only( Register rr = ) prepareResultReg(ins, x87Regs);
+            NanoAssert(FST0 == rr);
+            NanoAssert(!iftrue->isInReg() || iftrue->getReg() == FST0);
+
+            NanoAssert(!iffalse->isInReg());
+
+            NIns* target = _nIns;
+
+            if (iffalse->isImmD()) {
+                asm_immd(FST0, iffalse->immDasQ(), iffalse->immD(), /*canClobberCCs*/false);
+            } else {
+                int df = findMemFor(iffalse);
+                FLDQ(df, FP);
+            }
+
+            FINCSTP();
+            // Its not sufficient to merely decrement the FP stack pointer, we have to
+            // also free FST0, otherwise the load above fails.
+            FFREE(FST0);
+            asm_branch(false, condval, target);
+
+            freeResourcesOf(ins);
+            if (!iftrue->isInReg())
+                findSpecificRegForUnallocated(iftrue, FST0);
+
+            return;
+        }
+
+        RegisterMask allow = ins->isD() ? XmmRegs : GpRegs;
+
+        Register rr = prepareResultReg(ins, allow);
+
+        Register rf = findRegFor(iffalse, allow & ~rmask(rr));
 
         // If 'iftrue' isn't in a register, it can be clobbered by 'ins'.
         Register rt = iftrue->isInReg() ? iftrue->getReg() : rr;
 
+        if (ins->isop(LIR_cmovd)) {
+            NIns* target = _nIns;
+            asm_nongp_copy(rr, rf);
+            asm_branch(false, condval, target);
+            if (rr != rt)
+                asm_nongp_copy(rr, rt);
+            freeResourcesOf(ins);
+            if (!iftrue->isInReg()) {
+                NanoAssert(rt == rr);
+                findSpecificRegForUnallocated(iftrue, rr);
+            }
+            return;
+        }
+
+        NanoAssert(ins->isop(LIR_cmovi));
+
         // WARNING: We cannot generate any code that affects the condition
         // codes between the MRcc generation here and the asm_cmp() call
         // below.  See asm_cmp() for more details.
         switch (condval->opcode()) {
             // Note that these are all opposites...
             case LIR_eqi:    MRNE(rr, rf);   break;
             case LIR_lti:    MRGE(rr, rf);   break;
             case LIR_lei:    MRG( rr, rf);   break;
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -79,17 +79,17 @@ pm_finalize(JSContext* cx, JSObject* obj
 
 #define GETTER(name)                                                    \
     static JSBool                                                       \
     pm_get_##name(JSContext* cx, JSObject* obj, jsid /*unused*/, jsval* vp) \
     {                                                                   \
         PerfMeasurement* p = GetPM(cx, obj, #name);                     \
         if (!p)                                                         \
             return JS_FALSE;                                            \
-        return JS_NewNumberValue(cx, p->name, vp);                      \
+        return JS_NewNumberValue(cx, jsdouble(p->name), vp);            \
     }
 
 GETTER(cpu_cycles)
 GETTER(instructions)
 GETTER(cache_references)
 GETTER(cache_misses)
 GETTER(branch_instructions)
 GETTER(branch_misses)
--- a/js/src/shell/Makefile.in
+++ b/js/src/shell/Makefile.in
@@ -36,24 +36,26 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+narcissusdir    = $(topsrcdir)/../narcissus
 
 include $(DEPTH)/config/autoconf.mk
 
 PROGRAM         = js$(BIN_SUFFIX)
 CPPSRCS		= \
   js.cpp \
   jsworkers.cpp \
   $(NULL)
+NJS             = njs
 
 DEFINES         += -DEXPORT_JS_API
 
 LIBS      = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
 
 LOCAL_INCLUDES += -I$(topsrcdir) -I..
 
 ifdef _MSC_VER
@@ -72,10 +74,15 @@ include $(topsrcdir)/config/rules.mk
 
 ifdef MOZ_SHARK
 CFLAGS += -F/System/Library/PrivateFrameworks
 CXXFLAGS += -F/System/Library/PrivateFrameworks
 LDFLAGS += -F/System/Library/PrivateFrameworks -framework CHUD
 endif
 
 # People expect the js shell to wind up in the top-level JS dir.
+# The njs script expects to be in the same directory as the js shell as well as
+# narcissus/js*.js.
 libs::
 	$(INSTALL) $(IFLAGS2) $(PROGRAM) $(DEPTH)
+	$(NSINSTALL) -D $(DEPTH)/narcissus
+	$(INSTALL) $(IFLAGS2) $(foreach f,$(wildcard $(narcissusdir)/js*.js),"$f") $(DEPTH)/narcissus
+	$(INSTALL) $(IFLAGS2) $(srcdir)/$(NJS) $(DEPTH)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1030,16 +1030,40 @@ ReadLine(JSContext *cx, uintN argc, jsva
         return JS_FALSE;
     }
 
     *vp = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
 
 static JSBool
+PutStr(JSContext *cx, uintN argc, jsval *vp)
+{
+    jsval *argv;
+    JSString *str;
+    char *bytes;
+
+    if (argc != 0) {
+        argv = JS_ARGV(cx, vp);
+        str = JS_ValueToString(cx, argv[0]);
+        if (!str)
+            return JS_FALSE;
+        bytes = JS_EncodeString(cx, str);
+        if (!bytes)
+            return JS_FALSE;
+        fputs(bytes, gOutFile);
+        JS_free(cx, bytes);
+        fflush(gOutFile);
+    }
+
+    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    return JS_TRUE;
+}
+
+static JSBool
 Print(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval *argv;
     uintN i;
     JSString *str;
     char *bytes;
 
     argv = JS_ARGV(cx, vp);
@@ -3867,16 +3891,17 @@ Wrap(JSContext *cx, uintN argc, jsval *v
 
 /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
 static JSFunctionSpec shell_functions[] = {
     JS_FS("version",        Version,        0,0,0),
     JS_FS("options",        Options,        0,0,0),
     JS_FS("load",           Load,           1,0,0),
     JS_FN("readline",       ReadLine,       0,0),
     JS_FN("print",          Print,          0,0),
+    JS_FN("putstr",         PutStr,         0,0),
     JS_FS("help",           Help,           0,0,0),
     JS_FS("quit",           Quit,           0,0,0),
     JS_FN("assertEq",       AssertEq,       2,0),
     JS_FN("gc",             ::GC,           0,0),
 #ifdef JS_GCMETER
     JS_FN("gcstats",        GCStats,        0,0),
 #endif
     JS_FN("gcparam",        GCParameter,    2,0),
@@ -3958,16 +3983,17 @@ static const char shell_help_header[] =
 "=======                  ===========\n";
 
 static const char *const shell_help_messages[] = {
 "version([number])        Get or set JavaScript version number",
 "options([option ...])    Get or toggle JavaScript options",
 "load(['foo.js' ...])     Load files named by string arguments",
 "readline()               Read a single line from stdin",
 "print([exp ...])         Evaluate and print expressions",
+"putstr([exp])            Evaluate and print expression without newline",
 "help([name ...])         Display usage and help messages",
 "quit()                   Quit the shell",
 "assertEq(actual, expected[, msg])\n"
 "  Throw if the first two arguments are not the same (both +0 or both -0,\n"
 "  both NaN, or non-zero and ===)",
 "gc()                     Run the garbage collector",
 #ifdef JS_GCMETER
 "gcstats()                Print garbage collector statistics",
rename from js/src/njs
rename to js/src/shell/njs
--- a/js/src/njs
+++ b/js/src/shell/njs
@@ -1,38 +1,41 @@
 #!/usr/bin/python
 #
 # Narcissus 'shell' for use with jstests.py
-# This must be run from the js/tests directory
+# Expects to be in the same directory as ./js
+# Expects the Narcissus src files to be in ./narcissus/
 
 import os, re, sys
 from subprocess import *
 from optparse import OptionParser
 
-js_cmd="../js"
-NARC_JS_DIR = "../../narcissus/"
-narc_jsdefs = NARC_JS_DIR + "jsdefs.js"
-narc_jslex = NARC_JS_DIR + "jslex.js"
-narc_jsparse = NARC_JS_DIR + "jsparse.js"
-narc_jsexec = NARC_JS_DIR + "jsexec.js"
+THIS_DIR = os.path.dirname(__file__)
+NARC_JS_DIR = os.path.abspath(os.path.join(THIS_DIR, 'narcissus'))
+
+js_cmd = os.path.abspath(os.path.join(THIS_DIR, "js"))
 
+narc_jsdefs = os.path.join(NARC_JS_DIR, "jsdefs.js")
+narc_jslex = os.path.join(NARC_JS_DIR, "jslex.js")
+narc_jsparse = os.path.join(NARC_JS_DIR, "jsparse.js")
+narc_jsexec = os.path.join(NARC_JS_DIR, "jsexec.js")
 
 
 if __name__ == '__main__':
     op = OptionParser(usage='%prog [TEST-SPECS]')
     op.add_option('-f', '--file', dest='js_files', action='append',
             help='JS file to load', metavar='FILE')
     op.add_option('-e', '--expression', dest='js_exps', action='append',
             help='JS expression to evaluate')
 
     (options, args) = op.parse_args()
 
-    cmd = 'evaluate("__NARCISSUS__=true;"); '
+    cmd = ""
     if options.js_exps:
         for exp in options.js_exps:
-            cmd += 'evaluate("%s"); ' % exp.replace('"', '\\"')
+            cmd += 'Narcissus.jsexec.evaluate("%s"); ' % exp.replace('"', '\\"')
 
     if options.js_files:
         for file in options.js_files:
-            cmd += 'evaluate(snarf("%(file)s"), "%(file)s", 1); ' % {'file':file }
+            cmd += 'Narcissus.jsexec.evaluate(snarf("%(file)s"), "%(file)s", 1); ' % { 'file':file }
 
     Popen([js_cmd, '-f', narc_jsdefs, '-f', narc_jslex, '-f', narc_jsparse, '-f', narc_jsexec, '-e', cmd]).wait()
 
--- a/js/src/tests/js1_8_5/extensions/parseInt-octal.js
+++ b/js/src/tests/js1_8_5/extensions/parseInt-octal.js
@@ -1,31 +1,31 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 var gTestfile = 'parseInt-octal.js';
 //-----------------------------------------------------------------------------
-var BUGNUMBER = 577536;
+var BUGNUMBER = 583925;
 var summary =
-  "parseInt should treat leading-zero inputs as decimal in strict mode";
+  "parseInt should treat leading-zero inputs as octal regardless of whether caller is strict or laissez-faire mode code";
 
 print(BUGNUMBER + ": " + summary);
 
 /**************
  * BEGIN TEST *
  **************/
 
 assertEq(parseInt("08"), 0);
 assertEq(parseInt("09"), 0);
 assertEq(parseInt("014"), 12);
 
 function strictParseInt(s) { "use strict"; return parseInt(s); }
 
-assertEq(strictParseInt("08"), 8);
-assertEq(strictParseInt("09"), 9);
-assertEq(strictParseInt("014"), 14);
+assertEq(strictParseInt("08"), 0);
+assertEq(strictParseInt("09"), 0);
+assertEq(strictParseInt("014"), 12);
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
 print("All tests passed!");
--- a/js/src/tests/narcissus-failures.txt
+++ b/js/src/tests/narcissus-failures.txt
@@ -81,17 +81,16 @@ narcissus/../e4x/Regress/regress-327564.
 narcissus/../e4x/Regress/regress-327691-01.js
 narcissus/../e4x/Regress/regress-327691-02.js
 narcissus/../e4x/Regress/regress-327697.js
 narcissus/../e4x/Regress/regress-328249.js
 narcissus/../e4x/Regress/regress-329257.js
 narcissus/../e4x/Regress/regress-331558.js
 narcissus/../e4x/Regress/regress-331664.js
 narcissus/../e4x/Regress/regress-344455.js
-narcissus/../e4x/Regress/regress-347155.js
 narcissus/../e4x/Regress/regress-350206-1.js
 narcissus/../e4x/Regress/regress-350206.js
 narcissus/../e4x/Regress/regress-350238.js
 narcissus/../e4x/Regress/regress-350629.js
 narcissus/../e4x/Regress/regress-352097.js
 narcissus/../e4x/Regress/regress-352103.js
 narcissus/../e4x/Regress/regress-352223.js
 narcissus/../e4x/Regress/regress-354145-01.js
@@ -119,31 +118,28 @@ narcissus/../e4x/Regress/regress-371369.
 narcissus/../e4x/Regress/regress-372563.js
 narcissus/../e4x/Regress/regress-372564.js
 narcissus/../e4x/Regress/regress-373082.js
 narcissus/../e4x/Regress/regress-374106.js
 narcissus/../e4x/Regress/regress-374112.js
 narcissus/../e4x/Regress/regress-374116.js
 narcissus/../e4x/Regress/regress-374160.js
 narcissus/../e4x/Regress/regress-375406.js
-narcissus/../e4x/Regress/regress-378492.js
 narcissus/../e4x/Regress/regress-380833.js
 narcissus/../e4x/Regress/regress-383255.js
 narcissus/../e4x/Regress/regress-394941.js
 narcissus/../e4x/Regress/regress-407323.js
 narcissus/../e4x/Regress/regress-426520.js
 narcissus/../e4x/Regress/regress-453915.js
 narcissus/../e4x/Regress/regress-458679-01.js
 narcissus/../e4x/Regress/regress-458679-02.js
 narcissus/../e4x/Regress/regress-460180.js
 narcissus/../e4x/Regress/regress-465063.js
 narcissus/../e4x/Regress/regress-470619.js
-narcissus/../e4x/Regress/regress-473709.js
 narcissus/../e4x/Regress/regress-474319.js
-narcissus/../e4x/Regress/regress-477053.js
 narcissus/../e4x/Regress/regress-561031.js
 narcissus/../e4x/Statements/12.1.js
 narcissus/../e4x/Statements/12.2.js
 narcissus/../e4x/TypeConversion/10.1.1.js
 narcissus/../e4x/TypeConversion/10.1.2.js
 narcissus/../e4x/TypeConversion/10.3.1.js
 narcissus/../e4x/TypeConversion/10.3.js
 narcissus/../e4x/TypeConversion/10.4.1.js
@@ -403,16 +399,17 @@ narcissus/../ecma/Math/15.8.2.11.js
 narcissus/../ecma/Math/15.8.2.13.js
 narcissus/../ecma/Math/15.8.2.15.js
 narcissus/../ecma/Math/15.8.2.16.js
 narcissus/../ecma/Math/15.8.2.17.js
 narcissus/../ecma/Math/15.8.2.2.js
 narcissus/../ecma/Math/15.8.2.6.js
 narcissus/../ecma/Math/15.8.2.7.js
 narcissus/../ecma/Math/15.8.2.9.js
+narcissus/../ecma/Number/0x-without-following-hexdigits.js
 narcissus/../ecma/Number/15.7.4.2-2-n.js
 narcissus/../ecma/Number/15.7.4.2-3-n.js
 narcissus/../ecma/Number/15.7.4.3-3-n.js
 narcissus/../ecma/ObjectObjects/15.2.4.2.js
 narcissus/../ecma/Statements/12.6.2-9-n.js
 narcissus/../ecma/Statements/12.6.3-5-n.js
 narcissus/../ecma/Statements/12.6.3-6-n.js
 narcissus/../ecma/Statements/12.6.3-7-n.js
@@ -460,99 +457,57 @@ narcissus/../ecma_2/Exceptions/date-003.
 narcissus/../ecma_2/Exceptions/date-004.js
 narcissus/../ecma_2/Exceptions/exception-001.js
 narcissus/../ecma_2/Exceptions/exception-002.js
 narcissus/../ecma_2/Exceptions/exception-003.js
 narcissus/../ecma_2/Exceptions/exception-004.js
 narcissus/../ecma_2/Exceptions/exception-005.js
 narcissus/../ecma_2/Exceptions/exception-006.js
 narcissus/../ecma_2/Exceptions/exception-007.js
-narcissus/../ecma_2/Exceptions/exception-008.js
 narcissus/../ecma_2/Exceptions/exception-010-n.js
 narcissus/../ecma_2/Exceptions/exception-011-n.js
-narcissus/../ecma_2/Exceptions/expression-001.js
 narcissus/../ecma_2/Exceptions/expression-002.js
 narcissus/../ecma_2/Exceptions/expression-003.js
 narcissus/../ecma_2/Exceptions/expression-004.js
 narcissus/../ecma_2/Exceptions/expression-005.js
 narcissus/../ecma_2/Exceptions/expression-006.js
 narcissus/../ecma_2/Exceptions/expression-007.js
 narcissus/../ecma_2/Exceptions/expression-008.js
 narcissus/../ecma_2/Exceptions/expression-009.js
 narcissus/../ecma_2/Exceptions/expression-010.js
 narcissus/../ecma_2/Exceptions/expression-011.js
 narcissus/../ecma_2/Exceptions/expression-012.js
 narcissus/../ecma_2/Exceptions/expression-013.js
 narcissus/../ecma_2/Exceptions/expression-014.js
-narcissus/../ecma_2/Exceptions/expression-015.js
 narcissus/../ecma_2/Exceptions/expression-016.js
 narcissus/../ecma_2/Exceptions/expression-017.js
 narcissus/../ecma_2/Exceptions/expression-019.js
 narcissus/../ecma_2/Exceptions/global-001.js
 narcissus/../ecma_2/Exceptions/global-002.js
 narcissus/../ecma_2/Exceptions/lexical-001.js
 narcissus/../ecma_2/Exceptions/lexical-002.js
-narcissus/../ecma_2/Exceptions/lexical-003.js
-narcissus/../ecma_2/Exceptions/lexical-004.js
-narcissus/../ecma_2/Exceptions/lexical-005.js
-narcissus/../ecma_2/Exceptions/lexical-006.js
-narcissus/../ecma_2/Exceptions/lexical-007.js
-narcissus/../ecma_2/Exceptions/lexical-008.js
-narcissus/../ecma_2/Exceptions/lexical-009.js
 narcissus/../ecma_2/Exceptions/lexical-011.js
-narcissus/../ecma_2/Exceptions/lexical-012.js
-narcissus/../ecma_2/Exceptions/lexical-013.js
 narcissus/../ecma_2/Exceptions/lexical-014.js
 narcissus/../ecma_2/Exceptions/lexical-015.js
 narcissus/../ecma_2/Exceptions/lexical-016.js
-narcissus/../ecma_2/Exceptions/lexical-017.js
-narcissus/../ecma_2/Exceptions/lexical-018.js
-narcissus/../ecma_2/Exceptions/lexical-019.js
-narcissus/../ecma_2/Exceptions/lexical-020.js
 narcissus/../ecma_2/Exceptions/lexical-021.js
 narcissus/../ecma_2/Exceptions/lexical-023.js
-narcissus/../ecma_2/Exceptions/lexical-024.js
-narcissus/../ecma_2/Exceptions/lexical-025.js
-narcissus/../ecma_2/Exceptions/lexical-026.js
-narcissus/../ecma_2/Exceptions/lexical-027.js
-narcissus/../ecma_2/Exceptions/lexical-028.js
-narcissus/../ecma_2/Exceptions/lexical-029.js
-narcissus/../ecma_2/Exceptions/lexical-030.js
-narcissus/../ecma_2/Exceptions/lexical-031.js
-narcissus/../ecma_2/Exceptions/lexical-032.js
 narcissus/../ecma_2/Exceptions/lexical-033.js
-narcissus/../ecma_2/Exceptions/lexical-034.js
-narcissus/../ecma_2/Exceptions/lexical-035.js
-narcissus/../ecma_2/Exceptions/lexical-036.js
-narcissus/../ecma_2/Exceptions/lexical-037.js
-narcissus/../ecma_2/Exceptions/lexical-038.js
-narcissus/../ecma_2/Exceptions/lexical-039.js
-narcissus/../ecma_2/Exceptions/lexical-040.js
-narcissus/../ecma_2/Exceptions/lexical-041.js
-narcissus/../ecma_2/Exceptions/lexical-042.js
 narcissus/../ecma_2/Exceptions/lexical-047.js
 narcissus/../ecma_2/Exceptions/lexical-048.js
 narcissus/../ecma_2/Exceptions/lexical-049.js
-narcissus/../ecma_2/Exceptions/lexical-050.js
 narcissus/../ecma_2/Exceptions/lexical-051.js
-narcissus/../ecma_2/Exceptions/lexical-052.js
-narcissus/../ecma_2/Exceptions/lexical-053.js
-narcissus/../ecma_2/Exceptions/lexical-054.js
 narcissus/../ecma_2/Exceptions/number-001.js
 narcissus/../ecma_2/Exceptions/number-002.js
 narcissus/../ecma_2/Exceptions/number-003.js
 narcissus/../ecma_2/Exceptions/statement-001.js
-narcissus/../ecma_2/Exceptions/statement-002.js
 narcissus/../ecma_2/Exceptions/statement-003.js
 narcissus/../ecma_2/Exceptions/statement-004.js
 narcissus/../ecma_2/Exceptions/statement-005.js
 narcissus/../ecma_2/Exceptions/statement-006.js
-narcissus/../ecma_2/Exceptions/statement-007.js
-narcissus/../ecma_2/Exceptions/statement-008.js
-narcissus/../ecma_2/Exceptions/statement-009.js
 narcissus/../ecma_2/Exceptions/string-001.js
 narcissus/../ecma_2/Exceptions/string-002.js
 narcissus/../ecma_2/FunctionObjects/apply-001-n.js
 narcissus/../ecma_2/LexicalConventions/keywords-001.js
 narcissus/../ecma_2/LexicalConventions/regexp-literals-002.js
 narcissus/../ecma_2/RegExp/exec-002.js
 narcissus/../ecma_2/RegExp/octal-003.js
 narcissus/../ecma_2/Statements/forin-002.js
@@ -570,53 +525,50 @@ narcissus/../ecma_2/instanceof/instanceo
 narcissus/../ecma_3/Array/15.4.5.1-01.js
 narcissus/../ecma_3/Array/15.5.4.8-01.js
 narcissus/../ecma_3/Array/regress-322135-01.js
 narcissus/../ecma_3/Array/regress-387501.js
 narcissus/../ecma_3/Array/regress-421325.js
 narcissus/../ecma_3/Array/regress-430717.js
 narcissus/../ecma_3/Date/15.9.5.4.js
 narcissus/../ecma_3/Date/regress-452786.js
-narcissus/../ecma_3/Exceptions/15.11.4.4-1.js
 narcissus/../ecma_3/Exceptions/binding-001.js
 narcissus/../ecma_3/Exceptions/regress-181914.js
 narcissus/../ecma_3/Exceptions/regress-95101.js
 narcissus/../ecma_3/ExecutionContexts/10.1.3-1.js
 narcissus/../ecma_3/ExecutionContexts/10.1.3-2.js
 narcissus/../ecma_3/FunExpr/fe-001-n.js
 narcissus/../ecma_3/Function/arguments-001.js
 narcissus/../ecma_3/Function/regress-131964.js
 narcissus/../ecma_3/Function/regress-313570.js
-narcissus/../ecma_3/Function/regress-49286.js
 narcissus/../ecma_3/Function/regress-58274.js
 narcissus/../ecma_3/Function/regress-85880.js
 narcissus/../ecma_3/Function/regress-94506.js
 narcissus/../ecma_3/Function/scope-001.js
 narcissus/../ecma_3/LexicalConventions/7.4-01.js
 narcissus/../ecma_3/LexicalConventions/7.8.3-01.js
+narcissus/../ecma_3/LexicalConventions/7.9.1.js
 narcissus/../ecma_3/Number/15.7.4.5-1.js
 narcissus/../ecma_3/Number/15.7.4.5-2.js
 narcissus/../ecma_3/Object/8.6.1-01.js
 narcissus/../ecma_3/Object/8.6.2.6-001.js
 narcissus/../ecma_3/Object/class-001.js
 narcissus/../ecma_3/Object/class-003.js
 narcissus/../ecma_3/Object/class-005.js
 narcissus/../ecma_3/Object/regress-385393-07.js
 narcissus/../ecma_3/Operators/11.13.1-001.js
 narcissus/../ecma_3/RegExp/15.10.4.1-5-n.js
 narcissus/../ecma_3/RegExp/15.10.4.1-6.js
-narcissus/../ecma_3/RegExp/regress-122076.js
 narcissus/../ecma_3/RegExp/regress-188206.js
 narcissus/../ecma_3/RegExp/regress-223273.js
 narcissus/../ecma_3/RegExp/regress-375715-01-n.js
 narcissus/../ecma_3/RegExp/regress-375715-04.js
 narcissus/../ecma_3/RegExp/regress-436700.js
 narcissus/../ecma_3/RegExp/regress-465862.js
 narcissus/../ecma_3/RegExp/regress-57631.js
-narcissus/../ecma_3/RegExp/regress-98306.js
 narcissus/../ecma_3/Regress/regress-385393-04.js
 narcissus/../ecma_3/Statements/regress-157509.js
 narcissus/../ecma_3/Statements/regress-302439.js
 narcissus/../ecma_3/String/regress-304376.js
 narcissus/../ecma_3/String/regress-313567.js
 narcissus/../ecma_3/Unicode/regress-352044-01.js
 narcissus/../ecma_3/Unicode/regress-352044-02-n.js
 narcissus/../ecma_3/Unicode/uc-001-n.js
@@ -625,19 +577,21 @@ narcissus/../ecma_3/Unicode/uc-002.js
 narcissus/../ecma_3/Unicode/uc-003.js
 narcissus/../ecma_3/Unicode/uc-005.js
 narcissus/../ecma_3/extensions/10.1.3-2.js
 narcissus/../ecma_3/extensions/7.9.1.js
 narcissus/../ecma_3/extensions/regress-188206-01.js
 narcissus/../ecma_3/extensions/regress-274152.js
 narcissus/../ecma_3/extensions/regress-368516.js
 narcissus/../ecma_3/extensions/regress-385393-03.js
+narcissus/../ecma_3_1/Object/regress-444787.js
 narcissus/../ecma_5/Array/toString-01.js
 narcissus/../ecma_5/Expressions/11.1.5-01.js
 narcissus/../ecma_5/Expressions/named-accessor-function.js
+narcissus/../ecma_5/Function/15.3.4.3-01.js
 narcissus/../ecma_5/JSON/cyclic-stringify.js
 narcissus/../ecma_5/Object/15.2.3.3-01.js
 narcissus/../ecma_5/Object/15.2.3.4-01.js
 narcissus/../ecma_5/Object/15.2.3.4-03.js
 narcissus/../ecma_5/Object/15.2.3.6-dictionary-redefinition-1-of-8.js
 narcissus/../ecma_5/Object/15.2.3.6-dictionary-redefinition-2-of-8.js
 narcissus/../ecma_5/Object/15.2.3.6-dictionary-redefinition-3-of-8.js
 narcissus/../ecma_5/Object/15.2.3.6-dictionary-redefinition-4-of-8.js
@@ -655,16 +609,17 @@ narcissus/../ecma_5/Object/15.2.3.6-midd
 narcissus/../ecma_5/Object/15.2.3.6-middle-redefinition-7-of-8.js
 narcissus/../ecma_5/Object/15.2.3.6-middle-redefinition-8-of-8.js
 narcissus/../ecma_5/Object/15.2.3.6-new-definition.js
 narcissus/../ecma_5/Object/15.2.3.6-redefinition-1-of-4.js
 narcissus/../ecma_5/Object/15.2.3.6-redefinition-2-of-4.js
 narcissus/../ecma_5/Object/15.2.3.6-redefinition-3-of-4.js
 narcissus/../ecma_5/Object/15.2.3.6-redefinition-4-of-4.js
 narcissus/../ecma_5/Object/15.2.3.7-01.js
+narcissus/../ecma_5/RegExp/15.10.7.5-01.js
 narcissus/../ecma_5/Types/8.12.5-01.js
 narcissus/../ecma_5/extensions/8.12.5-01.js
 narcissus/../ecma_5/extensions/regress-bug567606.js
 narcissus/../ecma_5/extensions/string-literal-getter-setter-decompilation.js
 narcissus/../ecma_5/misc/enumerate-undefined.js
 narcissus/../ecma_5/misc/global-numeric-properties.js
 narcissus/../ecma_5/strict/10.4.2.js
 narcissus/../ecma_5/strict/11.1.5.js
@@ -686,17 +641,16 @@ narcissus/../ecma_5/strict/regress-53225
 narcissus/../js1_2/regexp/alphanumeric.js
 narcissus/../js1_2/regexp/digit.js
 narcissus/../js1_2/regexp/whitespace.js
 narcissus/../js1_2/regexp/word_boundary.js
 narcissus/../js1_2/regress/regress-144834.js
 narcissus/../js1_3/inherit/proto_10.js
 narcissus/../js1_3/inherit/proto_12.js
 narcissus/../js1_3/inherit/proto_9.js
-narcissus/../js1_4/Eval/regress-531037.js
 narcissus/../js1_4/Functions/function-001.js
 narcissus/../js1_4/Regress/date-001-n.js
 narcissus/../js1_4/Regress/function-004-n.js
 narcissus/../js1_4/Regress/toString-001-n.js
 narcissus/../js1_5/Array/array-001.js
 narcissus/../js1_5/Array/regress-154338.js
 narcissus/../js1_5/Array/regress-157652.js
 narcissus/../js1_5/Array/regress-178722.js
@@ -741,55 +695,47 @@ narcissus/../js1_5/GC/regress-313479.js
 narcissus/../js1_5/GC/regress-316885-01.js
 narcissus/../js1_5/GC/regress-348532.js
 narcissus/../js1_5/GC/regress-352606.js
 narcissus/../js1_5/GC/regress-390078.js
 narcissus/../js1_5/GC/regress-418128.js
 narcissus/../js1_5/GetSet/regress-375976.js
 narcissus/../js1_5/LexicalConventions/lexical-001.js
 narcissus/../js1_5/LexicalConventions/regress-343675.js
-narcissus/../js1_5/LexicalConventions/regress-469940.js
 narcissus/../js1_5/Regress/regress-103602.js
 narcissus/../js1_5/Regress/regress-104077.js
-narcissus/../js1_5/Regress/regress-114491.js
 narcissus/../js1_5/Regress/regress-114493.js
 narcissus/../js1_5/Regress/regress-115436.js
 narcissus/../js1_5/Regress/regress-116228.js
 narcissus/../js1_5/Regress/regress-118849.js
 narcissus/../js1_5/Regress/regress-131510-001.js
 narcissus/../js1_5/Regress/regress-139316.js
 narcissus/../js1_5/Regress/regress-156354.js
 narcissus/../js1_5/Regress/regress-167328.js
 narcissus/../js1_5/Regress/regress-172699.js
 narcissus/../js1_5/Regress/regress-179524.js
-narcissus/../js1_5/Regress/regress-192414.js
 narcissus/../js1_5/Regress/regress-214761.js
 narcissus/../js1_5/Regress/regress-224956.js
-narcissus/../js1_5/Regress/regress-230216-2.js
 narcissus/../js1_5/Regress/regress-234389.js
-narcissus/../js1_5/Regress/regress-238881.js
 narcissus/../js1_5/Regress/regress-238945.js
 narcissus/../js1_5/Regress/regress-243389-n.js
 narcissus/../js1_5/Regress/regress-245113.js
 narcissus/../js1_5/Regress/regress-252892.js
 narcissus/../js1_5/Regress/regress-253150.js
-narcissus/../js1_5/Regress/regress-256501.js
 narcissus/../js1_5/Regress/regress-256617.js
 narcissus/../js1_5/Regress/regress-281606.js
 narcissus/../js1_5/Regress/regress-290575.js
 narcissus/../js1_5/Regress/regress-294302.js
-narcissus/../js1_5/Regress/regress-299209.js
 narcissus/../js1_5/Regress/regress-303213.js
 narcissus/../js1_5/Regress/regress-306633.js
 narcissus/../js1_5/Regress/regress-306794.js
 narcissus/../js1_5/Regress/regress-308566.js
 narcissus/../js1_5/Regress/regress-309242.js
 narcissus/../js1_5/Regress/regress-310993.js
 narcissus/../js1_5/Regress/regress-311071.js
-narcissus/../js1_5/Regress/regress-311629.js
 narcissus/../js1_5/Regress/regress-312260.js
 narcissus/../js1_5/Regress/regress-31255.js
 narcissus/../js1_5/Regress/regress-312588.js
 narcissus/../js1_5/Regress/regress-315990.js
 narcissus/../js1_5/Regress/regress-317533.js
 narcissus/../js1_5/Regress/regress-319391.js
 narcissus/../js1_5/Regress/regress-321757.js
 narcissus/../js1_5/Regress/regress-322430.js
@@ -799,41 +745,37 @@ narcissus/../js1_5/Regress/regress-32938
 narcissus/../js1_5/Regress/regress-329530.js
 narcissus/../js1_5/Regress/regress-334807-01.js
 narcissus/../js1_5/Regress/regress-334807-02.js
 narcissus/../js1_5/Regress/regress-334807-03.js
 narcissus/../js1_5/Regress/regress-334807-04.js
 narcissus/../js1_5/Regress/regress-334807-05.js
 narcissus/../js1_5/Regress/regress-334807-06.js
 narcissus/../js1_5/Regress/regress-336100.js
-narcissus/../js1_5/Regress/regress-340369.js
 narcissus/../js1_5/Regress/regress-344711-n.js
 narcissus/../js1_5/Regress/regress-349648.js
 narcissus/../js1_5/Regress/regress-350253.js
 narcissus/../js1_5/Regress/regress-350268.js
 narcissus/../js1_5/Regress/regress-350415.js
 narcissus/../js1_5/Regress/regress-350529.js
 narcissus/../js1_5/Regress/regress-351515.js
+narcissus/../js1_5/Regress/regress-352009.js
 narcissus/../js1_5/Regress/regress-352197.js
 narcissus/../js1_5/Regress/regress-352208.js
 narcissus/../js1_5/Regress/regress-355556.js
 narcissus/../js1_5/Regress/regress-356693.js
 narcissus/../js1_5/Regress/regress-360969-05.js
 narcissus/../js1_5/Regress/regress-360969-06.js
 narcissus/../js1_5/Regress/regress-361617.js
-narcissus/../js1_5/Regress/regress-366122.js
 narcissus/../js1_5/Regress/regress-372364.js
 narcissus/../js1_5/Regress/regress-383674.js
 narcissus/../js1_5/Regress/regress-383682.js
 narcissus/../js1_5/Regress/regress-407323.js
 narcissus/../js1_5/Regress/regress-407957.js
 narcissus/../js1_5/Regress/regress-410852.js
-narcissus/../js1_5/Regress/regress-416737-01.js
-narcissus/../js1_5/Regress/regress-416737-02.js
-narcissus/../js1_5/Regress/regress-417893.js
 narcissus/../js1_5/Regress/regress-419018.js
 narcissus/../js1_5/Regress/regress-420919.js
 narcissus/../js1_5/Regress/regress-422348.js
 narcissus/../js1_5/Regress/regress-426827.js
 narcissus/../js1_5/Regress/regress-428366.js
 narcissus/../js1_5/Regress/regress-438415-02.js
 narcissus/../js1_5/Regress/regress-440926.js
 narcissus/../js1_5/Regress/regress-449627.js
@@ -913,19 +855,17 @@ narcissus/../js1_5/Regress/regress-48278
 narcissus/../js1_5/Regress/regress-483103.js
 narcissus/../js1_5/Regress/regress-501124.js
 narcissus/../js1_5/Regress/regress-503860.js
 narcissus/../js1_5/Regress/regress-511859.js
 narcissus/../js1_5/Regress/regress-68498-003.js
 narcissus/../js1_5/Regress/regress-68498-004.js
 narcissus/../js1_5/Regress/regress-89474.js
 narcissus/../js1_5/Regress/regress-96128-n.js
-narcissus/../js1_5/Regress/regress-96526-002.js
 narcissus/../js1_5/Regress/regress-98901.js
-narcissus/../js1_5/Scope/regress-181834.js
 narcissus/../js1_5/Scope/regress-184107.js
 narcissus/../js1_5/Scope/regress-185485.js
 narcissus/../js1_5/Scope/regress-446026-01.js
 narcissus/../js1_5/decompilation/regress-344120.js
 narcissus/../js1_5/decompilation/regress-346892.js
 narcissus/../js1_5/decompilation/regress-346902.js
 narcissus/../js1_5/decompilation/regress-346915.js
 narcissus/../js1_5/decompilation/regress-349491.js
@@ -1006,21 +946,18 @@ narcissus/../js1_5/extensions/regress-35
 narcissus/../js1_5/extensions/regress-351102-01.js
 narcissus/../js1_5/extensions/regress-351102-02.js
 narcissus/../js1_5/extensions/regress-351102-06.js
 narcissus/../js1_5/extensions/regress-351448.js
 narcissus/../js1_5/extensions/regress-352261.js
 narcissus/../js1_5/extensions/regress-352372.js
 narcissus/../js1_5/extensions/regress-353214.js
 narcissus/../js1_5/extensions/regress-354297.js
-narcissus/../js1_5/extensions/regress-354541-01.js
-narcissus/../js1_5/extensions/regress-354541-02.js
-narcissus/../js1_5/extensions/regress-354541-03.js
-narcissus/../js1_5/extensions/regress-354541-04.js
 narcissus/../js1_5/extensions/regress-355497.js
+narcissus/../js1_5/extensions/regress-355736.js
 narcissus/../js1_5/extensions/regress-358594-01.js
 narcissus/../js1_5/extensions/regress-358594-02.js
 narcissus/../js1_5/extensions/regress-358594-03.js
 narcissus/../js1_5/extensions/regress-358594-04.js
 narcissus/../js1_5/extensions/regress-358594-05.js
 narcissus/../js1_5/extensions/regress-358594-06.js
 narcissus/../js1_5/extensions/regress-361346.js
 narcissus/../js1_5/extensions/regress-361856.js
@@ -1047,19 +984,16 @@ narcissus/../js1_5/extensions/regress-39
 narcissus/../js1_5/extensions/regress-407501.js
 narcissus/../js1_5/extensions/regress-412926.js
 narcissus/../js1_5/extensions/regress-416834.js
 narcissus/../js1_5/extensions/regress-420869-01.js
 narcissus/../js1_5/extensions/regress-424683-01.js
 narcissus/../js1_5/extensions/regress-427196-01.js
 narcissus/../js1_5/extensions/regress-429739.js
 narcissus/../js1_5/extensions/regress-434837-01.js
-narcissus/../js1_5/extensions/regress-435497-01.js
-narcissus/../js1_5/extensions/regress-435497-02.js
-narcissus/../js1_5/extensions/regress-435497-03.js
 narcissus/../js1_5/extensions/regress-452178.js
 narcissus/../js1_5/extensions/regress-452338.js
 narcissus/../js1_5/extensions/regress-452565.js
 narcissus/../js1_5/extensions/regress-453249.js
 narcissus/../js1_5/extensions/regress-454040.js
 narcissus/../js1_5/extensions/regress-455380.js
 narcissus/../js1_5/extensions/regress-455408.js
 narcissus/../js1_5/extensions/regress-455413.js
@@ -1073,21 +1007,18 @@ narcissus/../js1_5/extensions/regress-47
 narcissus/../js1_5/extensions/regress-479487.js
 narcissus/../js1_5/extensions/regress-50447-1.js
 narcissus/../js1_5/extensions/regress-543839.js
 narcissus/../js1_5/extensions/regress-90596-002.js
 narcissus/../js1_5/extensions/regress-96284-001.js
 narcissus/../js1_5/extensions/regress-96284-002.js
 narcissus/../js1_5/extensions/scope-001.js
 narcissus/../js1_6/Array/regress-352742-01.js
-narcissus/../js1_6/Array/regress-352742-02.js
 narcissus/../js1_6/Array/regress-415540.js
 narcissus/../js1_6/Regress/regress-301574.js
-narcissus/../js1_6/Regress/regress-311157-01.js
-narcissus/../js1_6/Regress/regress-311157-02.js
 narcissus/../js1_6/Regress/regress-314887.js
 narcissus/../js1_6/Regress/regress-350417.js
 narcissus/../js1_6/Regress/regress-351795.js
 narcissus/../js1_6/Regress/regress-352271.js
 narcissus/../js1_6/Regress/regress-355002.js
 narcissus/../js1_6/Regress/regress-378492.js
 narcissus/../js1_6/Regress/regress-382509.js
 narcissus/../js1_6/Regress/regress-475469.js
@@ -1119,83 +1050,79 @@ narcissus/../js1_7/block/regress-344139.
 narcissus/../js1_7/block/regress-344262.js
 narcissus/../js1_7/block/regress-344370.js
 narcissus/../js1_7/block/regress-344601.js
 narcissus/../js1_7/block/regress-345542.js
 narcissus/../js1_7/block/regress-347559.js
 narcissus/../js1_7/block/regress-348685.js
 narcissus/../js1_7/block/regress-349283.js
 narcissus/../js1_7/block/regress-349507.js
-narcissus/../js1_7/block/regress-349962.js
+narcissus/../js1_7/block/regress-349653.js
 narcissus/../js1_7/block/regress-350279.js
-narcissus/../js1_7/block/regress-350730.js
 narcissus/../js1_7/block/regress-350793-01.js
 narcissus/../js1_7/block/regress-351497.js
 narcissus/../js1_7/block/regress-351794.js
 narcissus/../js1_7/block/regress-352092.js
 narcissus/../js1_7/block/regress-352185.js
 narcissus/../js1_7/block/regress-352212.js
 narcissus/../js1_7/block/regress-352267.js
 narcissus/../js1_7/block/regress-352422.js
 narcissus/../js1_7/block/regress-352616.js
 narcissus/../js1_7/block/regress-352624.js
 narcissus/../js1_7/block/regress-352786.js
 narcissus/../js1_7/block/regress-352907.js
-narcissus/../js1_7/block/regress-357754.js
-narcissus/../js1_7/block/regress-358508.js
 narcissus/../js1_7/block/regress-376410.js
 narcissus/../js1_7/block/regress-396900.js
 narcissus/../js1_7/block/regress-411279.js
 narcissus/../js1_7/decompilation/regress-348904.js
 narcissus/../js1_7/decompilation/regress-349493.js
 narcissus/../js1_7/decompilation/regress-349499.js
 narcissus/../js1_7/decompilation/regress-349633.js
 narcissus/../js1_7/decompilation/regress-350704.js
 narcissus/../js1_7/decompilation/regress-350793-02.js
 narcissus/../js1_7/decompilation/regress-350810.js
 narcissus/../js1_7/decompilation/regress-351070-03.js
 narcissus/../js1_7/decompilation/regress-351496.js
-narcissus/../js1_7/decompilation/regress-352008.js
 narcissus/../js1_7/decompilation/regress-352015.js
 narcissus/../js1_7/decompilation/regress-352068.js
+narcissus/../js1_7/decompilation/regress-352079.js
 narcissus/../js1_7/decompilation/regress-352217.js
 narcissus/../js1_7/decompilation/regress-352269.js
+narcissus/../js1_7/decompilation/regress-352272.js
 narcissus/../js1_7/decompilation/regress-352283.js
 narcissus/../js1_7/decompilation/regress-352415.js
 narcissus/../js1_7/decompilation/regress-352441.js
 narcissus/../js1_7/decompilation/regress-352732.js
 narcissus/../js1_7/decompilation/regress-355049-01.js
 narcissus/../js1_7/decompilation/regress-355049-02.js
-narcissus/../js1_7/decompilation/regress-355105.js
+narcissus/../js1_7/decompilation/regress-355635.js
 narcissus/../js1_7/decompilation/regress-356247.js
-narcissus/../js1_7/decompilation/regress-374713.js
+narcissus/../js1_7/decompilation/regress-375794.js
<