2002/03/21 01:19:33
[org.ibex.core.git] / src / org / mozilla / javascript / ScriptRuntime.java
diff --git a/src/org/mozilla/javascript/ScriptRuntime.java b/src/org/mozilla/javascript/ScriptRuntime.java
new file mode 100644 (file)
index 0000000..e1d6821
--- /dev/null
@@ -0,0 +1,2168 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
+ *\r
+ * The contents of this file are subject to the Netscape Public\r
+ * License Version 1.1 (the "License"); you may not use this file\r
+ * except in compliance with the License. You may obtain a copy of\r
+ * the License at http://www.mozilla.org/NPL/\r
+ *\r
+ * Software distributed under the License is distributed on an "AS\r
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
+ * implied. See the License for the specific language governing\r
+ * rights and limitations under the License.\r
+ *\r
+ * The Original Code is Rhino code, released\r
+ * May 6, 1999.\r
+ *\r
+ * The Initial Developer of the Original Code is Netscape\r
+ * Communications Corporation.  Portions created by Netscape are\r
+ * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
+ * Rights Reserved.\r
+ *\r
+ * Contributor(s):\r
+ * Patrick Beard\r
+ * Norris Boyd\r
+ * Igor Bukanov \r
+ * Roger Lawrence\r
+ * Frank Mitchell\r
+ * Andrew Wason\r
+ *\r
+ * Alternatively, the contents of this file may be used under the\r
+ * terms of the GNU Public License (the "GPL"), in which case the\r
+ * provisions of the GPL are applicable instead of those above.\r
+ * If you wish to allow use of your version of this file only\r
+ * under the terms of the GPL and not to allow others to use your\r
+ * version of this file under the NPL, indicate your decision by\r
+ * deleting the provisions above and replace them with the notice\r
+ * and other provisions required by the GPL.  If you do not delete\r
+ * the provisions above, a recipient may use your version of this\r
+ * file under either the NPL or the GPL.\r
+ */\r
+\r
+package org.mozilla.javascript;\r
+\r
+import java.util.*;\r
+import java.lang.reflect.*;\r
+//import org.mozilla.classfile.DefiningClassLoader;\r
+\r
+/**\r
+ * This is the class that implements the runtime.\r
+ *\r
+ * @author Norris Boyd\r
+ */\r
+\r
+public class ScriptRuntime {\r
+\r
+    /**\r
+     * No instances should be created.\r
+     */\r
+    protected ScriptRuntime() {\r
+    }\r
+\r
+    /*\r
+     * There's such a huge space (and some time) waste for the Foo.class\r
+     * syntax: the compiler sticks in a test of a static field in the\r
+     * enclosing class for null and the code for creating the class value.\r
+     * It has to do this since the reference has to get pushed off til\r
+     * executiontime (i.e. can't force an early load), but for the\r
+     * 'standard' classes - especially those in java.lang, we can trust\r
+     * that they won't cause problems by being loaded early.\r
+     */\r
+\r
+    public final static Class UndefinedClass = Undefined.class;\r
+    public final static Class ScriptableClass = Scriptable.class;\r
+    public final static Class StringClass = String.class;\r
+    public final static Class NumberClass = Number.class;\r
+    public final static Class BooleanClass = Boolean.class;\r
+    public final static Class ByteClass = Byte.class;\r
+    public final static Class ShortClass = Short.class;\r
+    public final static Class IntegerClass = Integer.class;\r
+    public final static Class LongClass = Long.class;\r
+    public final static Class FloatClass = Float.class;\r
+    public final static Class DoubleClass = Double.class;\r
+    public final static Class CharacterClass = Character.class;\r
+    public final static Class ObjectClass = Object.class;\r
+    public final static Class FunctionClass = Function.class;\r
+    public final static Class ClassClass = Class.class;\r
+\r
+    /**\r
+     * Convert the value to a boolean.\r
+     *\r
+     * See ECMA 9.2.\r
+     */\r
+    public static boolean toBoolean(Object val) {\r
+        if (val == null)\r
+            return false;\r
+        if (val instanceof Scriptable) {\r
+            if (Context.getContext().isVersionECMA1()) {\r
+                // pure ECMA\r
+                return val != Undefined.instance;\r
+            }\r
+            // ECMA extension\r
+            val = ((Scriptable) val).getDefaultValue(BooleanClass);\r
+            if (val instanceof Scriptable)\r
+                throw errorWithClassName("msg.primitive.expected", val);\r
+            // fall through\r
+        }\r
+        if (val instanceof String)\r
+            return ((String) val).length() != 0;\r
+        if (val instanceof Number) {\r
+            double d = ((Number) val).doubleValue();\r
+            return (d == d && d != 0.0);\r
+        }\r
+        if (val instanceof Boolean)\r
+            return ((Boolean) val).booleanValue();\r
+        throw errorWithClassName("msg.invalid.type", val);\r
+    }\r
+\r
+    public static boolean toBoolean(Object[] args, int index) {\r
+        return (index < args.length) ? toBoolean(args[index]) : false;\r
+    }\r
+    /**\r
+     * Convert the value to a number.\r
+     *\r
+     * See ECMA 9.3.\r
+     */\r
+    public static double toNumber(Object val) {\r
+        if (val == null) \r
+            return +0.0;\r
+        if (val instanceof Scriptable) {\r
+            val = ((Scriptable) val).getDefaultValue(NumberClass);\r
+            if (val != null && val instanceof Scriptable)\r
+                throw errorWithClassName("msg.primitive.expected", val);\r
+            // fall through\r
+        }\r
+        if (val instanceof String)\r
+            return toNumber((String) val);\r
+        if (val instanceof Number)\r
+            return ((Number) val).doubleValue();\r
+        if (val instanceof Boolean)\r
+            return ((Boolean) val).booleanValue() ? 1 : +0.0;\r
+        throw errorWithClassName("msg.invalid.type", val);\r
+    }\r
+\r
+    public static double toNumber(Object[] args, int index) {\r
+        return (index < args.length) ? toNumber(args[index]) : NaN;\r
+    }\r
+    \r
+    // This definition of NaN is identical to that in java.lang.Double\r
+    // except that it is not final. This is a workaround for a bug in\r
+    // the Microsoft VM, versions 2.01 and 3.0P1, that causes some uses\r
+    // (returns at least) of Double.NaN to be converted to 1.0.\r
+    // So we use ScriptRuntime.NaN instead of Double.NaN.\r
+    public static double NaN = 0.0d / 0.0;\r
+    public static Double NaNobj = new Double(0.0d / 0.0);\r
+\r
+    // A similar problem exists for negative zero.\r
+    public static double negativeZero = -0.0;\r
+\r
+    /*\r
+     * Helper function for toNumber, parseInt, and TokenStream.getToken.\r
+     */\r
+    static double stringToNumber(String s, int start, int radix) {\r
+        char digitMax = '9';\r
+        char lowerCaseBound = 'a';\r
+        char upperCaseBound = 'A';\r
+        int len = s.length();\r
+        if (radix < 10) {\r
+            digitMax = (char) ('0' + radix - 1);\r
+        }\r
+        if (radix > 10) {\r
+            lowerCaseBound = (char) ('a' + radix - 10);\r
+            upperCaseBound = (char) ('A' + radix - 10);\r
+        }\r
+        int end;\r
+        double sum = 0.0;\r
+        for (end=start; end < len; end++) {\r
+            char c = s.charAt(end);\r
+            int newDigit;\r
+            if ('0' <= c && c <= digitMax)\r
+                newDigit = c - '0';\r
+            else if ('a' <= c && c < lowerCaseBound)\r
+                newDigit = c - 'a' + 10;\r
+            else if ('A' <= c && c < upperCaseBound)\r
+                newDigit = c - 'A' + 10;\r
+            else\r
+                break;\r
+            sum = sum*radix + newDigit;\r
+        }\r
+        if (start == end) {\r
+            return NaN;\r
+        }\r
+        if (sum >= 9007199254740992.0) {\r
+            if (radix == 10) {\r
+                /* If we're accumulating a decimal number and the number\r
+                 * is >= 2^53, then the result from the repeated multiply-add\r
+                 * above may be inaccurate.  Call Java to get the correct\r
+                 * answer.\r
+                 */\r
+                try {\r
+                    return Double.valueOf(s.substring(start, end)).doubleValue();\r
+                } catch (NumberFormatException nfe) {\r
+                    return NaN;\r
+                }\r
+            } else if (radix == 2 || radix == 4 || radix == 8 ||\r
+                       radix == 16 || radix == 32)\r
+            {\r
+                /* The number may also be inaccurate for one of these bases.\r
+                 * This happens if the addition in value*radix + digit causes\r
+                 * a round-down to an even least significant mantissa bit\r
+                 * when the first dropped bit is a one.  If any of the\r
+                 * following digits in the number (which haven't been added\r
+                 * in yet) are nonzero then the correct action would have\r
+                 * been to round up instead of down.  An example of this\r
+                 * occurs when reading the number 0x1000000000000081, which\r
+                 * rounds to 0x1000000000000000 instead of 0x1000000000000100.\r
+                 */\r
+                BinaryDigitReader bdr = new BinaryDigitReader(radix, s, start, end);\r
+                int bit;\r
+                sum = 0.0;\r
+\r
+                /* Skip leading zeros. */\r
+                do {\r
+                    bit = bdr.getNextBinaryDigit();\r
+                } while (bit == 0);\r
+\r
+                if (bit == 1) {\r
+                    /* Gather the 53 significant bits (including the leading 1) */\r
+                    sum = 1.0;\r
+                    for (int j = 52; j != 0; j--) {\r
+                        bit = bdr.getNextBinaryDigit();\r
+                        if (bit < 0)\r
+                            return sum;\r
+                        sum = sum*2 + bit;\r
+                    }\r
+                    /* bit54 is the 54th bit (the first dropped from the mantissa) */\r
+                    int bit54 = bdr.getNextBinaryDigit();\r
+                    if (bit54 >= 0) {\r
+                        double factor = 2.0;\r
+                        int sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */\r
+                        int bit3;\r
+\r
+                        while ((bit3 = bdr.getNextBinaryDigit()) >= 0) {\r
+                            sticky |= bit3;\r
+                            factor *= 2;\r
+                        }\r
+                        sum += bit54 & (bit | sticky);\r
+                        sum *= factor;\r
+                    }\r
+                }\r
+            }\r
+            /* We don't worry about inaccurate numbers for any other base. */\r
+        }\r
+        return sum;\r
+    }\r
+\r
+\r
+    /**\r
+     * ToNumber applied to the String type\r
+     *\r
+     * See ECMA 9.3.1\r
+     */\r
+    public static double toNumber(String s) {\r
+        int len = s.length();\r
+        int start = 0;\r
+        char startChar;\r
+        for (;;) {\r
+            if (start == len) {\r
+                // Empty or contains only whitespace\r
+                return +0.0;\r
+            }\r
+            startChar = s.charAt(start);\r
+            if (!Character.isWhitespace(startChar))\r
+                break;\r
+            start++;\r
+        }\r
+\r
+        if (startChar == '0' && start+2 < len &&\r
+            Character.toLowerCase(s.charAt(start+1)) == 'x')\r
+            // A hexadecimal number\r
+            return stringToNumber(s, start + 2, 16);\r
+\r
+        if ((startChar == '+' || startChar == '-') && start+3 < len &&\r
+            s.charAt(start+1) == '0' &&\r
+            Character.toLowerCase(s.charAt(start+2)) == 'x') {\r
+            // A hexadecimal number\r
+            double val = stringToNumber(s, start + 3, 16);\r
+            return startChar == '-' ? -val : val;\r
+        }\r
+\r
+        int end = len - 1;\r
+        char endChar;\r
+        while (Character.isWhitespace(endChar = s.charAt(end)))\r
+            end--;\r
+        if (endChar == 'y') {\r
+            // check for "Infinity"\r
+            if (startChar == '+' || startChar == '-')\r
+                start++;\r
+            if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))\r
+                return startChar == '-'\r
+                    ? Double.NEGATIVE_INFINITY\r
+                    : Double.POSITIVE_INFINITY;\r
+            return NaN;\r
+        }\r
+        // A non-hexadecimal, non-infinity number:\r
+        // just try a normal floating point conversion\r
+        String sub = s.substring(start, end+1);\r
+        if (MSJVM_BUG_WORKAROUNDS) {\r
+            // The MS JVM will accept non-conformant strings\r
+            // rather than throwing a NumberFormatException\r
+            // as it should.\r
+            for (int i=sub.length()-1; i >= 0; i--) {\r
+                char c = sub.charAt(i);\r
+                if (('0' <= c && c <= '9') || c == '.' ||\r
+                    c == 'e' || c == 'E'  ||\r
+                    c == '+' || c == '-')\r
+                    continue;\r
+                return NaN;\r
+            }\r
+        }\r
+        try {\r
+            return Double.valueOf(sub).doubleValue();\r
+        } catch (NumberFormatException ex) {\r
+            return NaN;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Helper function for builtin objects that use the varargs form.\r
+     * ECMA function formal arguments are undefined if not supplied;\r
+     * this function pads the argument array out to the expected\r
+     * length, if necessary.\r
+     */\r
+    public static Object[] padArguments(Object[] args, int count) {\r
+        if (count < args.length)\r
+            return args;\r
+\r
+        int i;\r
+        Object[] result = new Object[count];\r
+        for (i = 0; i < args.length; i++) {\r
+            result[i] = args[i];\r
+        }\r
+\r
+        for (; i < count; i++) {\r
+            result[i] = Undefined.instance;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /* Work around Microsoft Java VM bugs. */\r
+    private final static boolean MSJVM_BUG_WORKAROUNDS = true;\r
+\r
+    /**\r
+     * For escaping strings printed by object and array literals; not quite\r
+     * the same as 'escape.'\r
+     */\r
+    public static String escapeString(String s) {\r
+        // ack!  Java lacks \v.\r
+        String escapeMap = "\bb\ff\nn\rr\tt\u000bv\"\"''";\r
+        StringBuffer result = new StringBuffer(s.length());\r
+\r
+        for(int i=0; i < s.length(); i++) {\r
+            char c = s.charAt(i);\r
+\r
+            // an ordinary print character\r
+            if (c >= ' ' && c <= '~'     // string.h isprint()\r
+                && c != '"')\r
+            {\r
+                result.append(c);\r
+                continue;\r
+            }\r
+\r
+            // an \escaped sort of character\r
+            int index;\r
+            if ((index = escapeMap.indexOf(c)) >= 0) {\r
+                result.append("\\");\r
+                result.append(escapeMap.charAt(index + 1));\r
+                continue;\r
+            }\r
+\r
+            // 2-digit hex?\r
+            if (c < 256) {\r
+                String hex = Integer.toHexString((int) c);\r
+                if (hex.length() == 1) {\r
+                    result.append("\\x0");\r
+                    result.append(hex);\r
+                } else {\r
+                    result.append("\\x");\r
+                    result.append(hex);\r
+                }\r
+                continue;\r
+            }\r
+\r
+            // nope.  Unicode.\r
+            String hex = Integer.toHexString((int) c);\r
+            // cool idiom courtesy Shaver.\r
+            result.append("\\u");\r
+            for (int l = hex.length(); l < 4; l++)\r
+                result.append('0');\r
+            result.append(hex);\r
+        }\r
+\r
+        return result.toString();\r
+    }\r
+\r
+\r
+    /**\r
+     * Convert the value to a string.\r
+     *\r
+     * See ECMA 9.8.\r
+     */\r
+    public static String toString(Object val) {\r
+        for (;;) {\r
+            if (val == null)\r
+                return "null";\r
+            if (val instanceof Scriptable) {\r
+                val = ((Scriptable) val).getDefaultValue(StringClass);\r
+                if (val != Undefined.instance && val instanceof Scriptable) {\r
+                    throw errorWithClassName("msg.primitive.expected", val);\r
+                }\r
+                continue;\r
+            }\r
+            if (val instanceof Number) {\r
+                // XXX should we just teach NativeNumber.stringValue()\r
+                // about Numbers?\r
+                return numberToString(((Number) val).doubleValue(), 10);\r
+            }\r
+            return val.toString();\r
+        }\r
+    }\r
+\r
+    public static String toString(Object[] args, int index) {\r
+        return (index < args.length) ? toString(args[index]) : "undefined";\r
+    }\r
+\r
+    /**\r
+     * Optimized version of toString(Object) for numbers.\r
+     */\r
+    public static String toString(double val) {\r
+        return numberToString(val, 10);\r
+    }\r
+    \r
+    public static String numberToString(double d, int base) {\r
+        if (d != d)\r
+            return "NaN";\r
+        if (d == Double.POSITIVE_INFINITY)\r
+            return "Infinity";\r
+        if (d == Double.NEGATIVE_INFINITY)\r
+            return "-Infinity";\r
+        if (d == 0.0)\r
+            return "0";\r
+\r
+        if ((base < 2) || (base > 36)) {\r
+            throw Context.reportRuntimeError1(\r
+                "msg.bad.radix", Integer.toString(base));\r
+        }\r
+\r
+        if (base != 10) {\r
+            return DToA.JS_dtobasestr(base, d);\r
+        } else {\r
+            StringBuffer result = new StringBuffer();\r
+            DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);\r
+            return result.toString();\r
+        }\r
+    \r
+    }\r
+\r
+    /**\r
+     * Convert the value to an object.\r
+     *\r
+     * See ECMA 9.9.\r
+     */\r
+    public static Scriptable toObject(Scriptable scope, Object val) {\r
+        return toObject(scope, val, null);\r
+    }\r
+\r
+    public static Scriptable toObject(Scriptable scope, Object val,\r
+                                      Class staticClass)\r
+    {\r
+        if (val == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        if (val instanceof Scriptable) {\r
+            if (val == Undefined.instance) {\r
+                throw NativeGlobal.typeError0("msg.undef.to.object", scope);\r
+            }\r
+            return (Scriptable) val;\r
+        }\r
+        String className = val instanceof String ? "String" :\r
+                           val instanceof Number ? "Number" :\r
+                           val instanceof Boolean ? "Boolean" :\r
+                           null;\r
+\r
+        if (className == null) {\r
+            // Extension: Wrap as a LiveConnect object.\r
+            Object wrapped = NativeJavaObject.wrap(scope, val, staticClass);\r
+            if (wrapped instanceof Scriptable)\r
+                return (Scriptable) wrapped;\r
+            throw errorWithClassName("msg.invalid.type", val);\r
+        }\r
+\r
+        Object[] args = { val };\r
+        scope = ScriptableObject.getTopLevelScope(scope);\r
+        Scriptable result = newObject(Context.getContext(), org.xwt.util.JSObject.defaultObjects, className, args);\r
+        return result;\r
+    }\r
+\r
+    public static Scriptable newObject(Context cx, Scriptable scope,\r
+                                       String constructorName, Object[] args)\r
+    {\r
+        Exception re = null;\r
+        try {\r
+            return cx.newObject(scope, constructorName, args);\r
+        }\r
+        catch (NotAFunctionException e) {\r
+            re = e;\r
+        }\r
+        catch (PropertyException e) {\r
+            re = e;\r
+        }\r
+        catch (JavaScriptException e) {\r
+            re = e;\r
+        }\r
+        throw cx.reportRuntimeError(re.getMessage());\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.4.\r
+     */\r
+    public static double toInteger(Object val) {\r
+        return toInteger(toNumber(val));\r
+    }\r
+\r
+    // convenience method\r
+    public static double toInteger(double d) {\r
+        // if it's NaN\r
+        if (d != d)\r
+            return +0.0;\r
+\r
+        if (d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return d;\r
+\r
+        if (d > 0.0)\r
+            return Math.floor(d);\r
+        else\r
+            return Math.ceil(d);\r
+    }\r
+\r
+    public static double toInteger(Object[] args, int index) {\r
+        return (index < args.length) ? toInteger(args[index]) : +0.0;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.5.\r
+     */\r
+    public static int toInt32(Object val) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+        double two31 = 2147483648.0;\r
+\r
+        // short circuit for common small values; TokenStream\r
+        // returns them as Bytes.\r
+        if (val instanceof Byte)\r
+            return ((Number)val).intValue();\r
+\r
+        double d = toNumber(val);\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        d = Math.IEEEremainder(d, two32);\r
+\r
+        d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        if (d >= two31)\r
+            return (int)(d - two32);\r
+        else\r
+            return (int)d;\r
+    }\r
+\r
+    public static int toInt32(Object[] args, int index) {\r
+        return (index < args.length) ? toInt32(args[index]) : 0;\r
+    }\r
+\r
+    public static int toInt32(double d) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+        double two31 = 2147483648.0;\r
+\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        d = Math.IEEEremainder(d, two32);\r
+\r
+        d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        if (d >= two31)\r
+            return (int)(d - two32);\r
+        else\r
+            return (int)d;\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.6.\r
+     */\r
+\r
+    // must return long to hold an _unsigned_ int\r
+    public static long toUint32(double d) {\r
+        // 0x100000000 gives me a numeric overflow...\r
+        double two32 = 4294967296.0;\r
+\r
+        if (d != d || d == 0.0 ||\r
+            d == Double.POSITIVE_INFINITY ||\r
+            d == Double.NEGATIVE_INFINITY)\r
+            return 0;\r
+\r
+        if (d > 0.0)\r
+            d = Math.floor(d);\r
+        else\r
+            d = Math.ceil(d);\r
+\r
+    d = Math.IEEEremainder(d, two32);\r
+\r
+    d = d >= 0\r
+            ? d\r
+            : d + two32;\r
+\r
+        return (long) Math.floor(d);\r
+    }\r
+\r
+    public static long toUint32(Object val) {\r
+        return toUint32(toNumber(val));\r
+    }\r
+\r
+    /**\r
+     *\r
+     * See ECMA 9.7.\r
+     */\r
+    public static char toUint16(Object val) {\r
+    long int16 = 0x10000;\r
+\r
+    double d = toNumber(val);\r
+    if (d != d || d == 0.0 ||\r
+        d == Double.POSITIVE_INFINITY ||\r
+        d == Double.NEGATIVE_INFINITY)\r
+    {\r
+        return 0;\r
+    }\r
+\r
+    d = Math.IEEEremainder(d, int16);\r
+\r
+    d = d >= 0\r
+            ? d\r
+        : d + int16;\r
+\r
+    return (char) Math.floor(d);\r
+    }\r
+\r
+    /**\r
+     * Unwrap a JavaScriptException.  Sleight of hand so that we don't\r
+     * javadoc JavaScriptException.getRuntimeValue().\r
+     */\r
+    public static Object unwrapJavaScriptException(JavaScriptException jse) {\r
+        return jse.value;\r
+    }\r
+\r
+    /**\r
+     * Check a WrappedException. Unwrap a JavaScriptException and return\r
+     * the value, otherwise rethrow.\r
+     */\r
+    public static Object unwrapWrappedException(WrappedException we) {\r
+        Throwable t = we.getWrappedException();\r
+        if (t instanceof JavaScriptException)\r
+            return ((JavaScriptException) t).value;\r
+        throw we;\r
+    }\r
+\r
+    public static Object getProp(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null || start == Undefined.instance) {\r
+            String msg = start == null ? "msg.null.to.object"\r
+                                       : "msg.undefined";\r
+            throw NativeGlobal.constructError(\r
+                        Context.getContext(), "ConversionError",\r
+                        ScriptRuntime.getMessage0(msg),\r
+                        scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object getTopLevelProp(Scriptable scope, String id) {\r
+        Scriptable s = ScriptableObject.getTopLevelScope(scope);\r
+        Object v;\r
+        do {\r
+            v = s.get(id, s);\r
+            if (v != Scriptable.NOT_FOUND)\r
+                return v;\r
+            s = s.getPrototype();\r
+        } while (s != null);\r
+        return v;\r
+    }\r
+\r
+\r
+\r
+/***********************************************************************/\r
+\r
+    public static Scriptable getProto(Object obj, Scriptable scope) {\r
+        Scriptable s;\r
+        if (obj instanceof Scriptable) {\r
+            s = (Scriptable) obj;\r
+        } else {\r
+            s = toObject(scope, obj);\r
+        }\r
+        if (s == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        return s.getPrototype();\r
+    }\r
+\r
+    public static Scriptable getParent(Object obj) {\r
+        Scriptable s;\r
+        try {\r
+            s = (Scriptable) obj;\r
+        }\r
+        catch (ClassCastException e) {\r
+            return null;\r
+        }\r
+        if (s == null) {\r
+            return null;\r
+        }\r
+        return getThis(s.getParentScope());\r
+    }\r
+\r
+   public static Scriptable getParent(Object obj, Scriptable scope) {\r
+        Scriptable s;\r
+        if (obj instanceof Scriptable) {\r
+            s = (Scriptable) obj;\r
+        } else {\r
+            s = toObject(scope, obj);\r
+        }\r
+        if (s == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        return s.getParentScope();\r
+    }\r
+\r
+    public static Object setProto(Object obj, Object value, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        Scriptable result = value == null ? null : toObject(scope, value);\r
+        Scriptable s = result;\r
+        while (s != null) {\r
+            if (s == start) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.cyclic.value", "__proto__");\r
+            }\r
+            s = s.getPrototype();\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        start.setPrototype(result);\r
+        return result;\r
+    }\r
+\r
+    public static Object setParent(Object obj, Object value, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        Scriptable result = value == null ? null : toObject(scope, value);\r
+        Scriptable s = result;\r
+        while (s != null) {\r
+            if (s == start) {\r
+                throw Context.reportRuntimeError1(\r
+                    "msg.cyclic.value", "__parent__");\r
+            }\r
+            s = s.getParentScope();\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        start.setParentScope(result);\r
+        return result;\r
+    }\r
+\r
+    public static Object setProp(Object obj, String id, Object value,\r
+                                 Scriptable scope)\r
+    {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            if (m.has(id, start)) {\r
+                m.put(id, start, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+\r
+        start.put(id, start, value);\r
+        return value;\r
+    }\r
+\r
+    // Return -1L if str is not an index or the index value as lower 32 \r
+    // bits of the result\r
+    private static long indexFromString(String str) {\r
+        // It must be a string.\r
+\r
+        // The length of the decimal string representation of \r
+        //  Integer.MAX_VALUE, 2147483647\r
+        final int MAX_VALUE_LENGTH = 10;\r
+        \r
+        int len = str.length();\r
+        if (len > 0) {\r
+            int i = 0;\r
+            boolean negate = false;\r
+            int c = str.charAt(0);\r
+            if (c == '-') {\r
+                if (len > 1) {\r
+                    c = str.charAt(1); \r
+                    i = 1; \r
+                    negate = true;\r
+                }\r
+            }\r
+            c -= '0';\r
+            if (0 <= c && c <= 9 \r
+                && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))\r
+            {\r
+                // Use negative numbers to accumulate index to handle\r
+                // Integer.MIN_VALUE that is greater by 1 in absolute value\r
+                // then Integer.MAX_VALUE\r
+                int index = -c;\r
+                int oldIndex = 0;\r
+                i++;\r
+                if (index != 0) {\r
+                    // Note that 00, 01, 000 etc. are not indexes\r
+                    while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)\r
+                    {\r
+                        oldIndex = index;\r
+                        index = 10 * index - c;\r
+                        i++;\r
+                    }\r
+                }\r
+                // Make sure all characters were consumed and that it couldn't\r
+                // have overflowed.\r
+                if (i == len &&\r
+                    (oldIndex > (Integer.MIN_VALUE / 10) ||\r
+                     (oldIndex == (Integer.MIN_VALUE / 10) &&\r
+                      c <= (negate ? -(Integer.MIN_VALUE % 10) \r
+                                   : (Integer.MAX_VALUE % 10)))))\r
+                {\r
+                    return 0xFFFFFFFFL & (negate ? index : -index);\r
+                }\r
+            }\r
+        }\r
+        return -1L;\r
+    }\r
+\r
+    static String getStringId(Object id) {\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            int index = (int) d;\r
+            if (((double) index) == d)\r
+                return null;\r
+            return toString(id);\r
+        }\r
+        String s = toString(id);\r
+        long indexTest = indexFromString(s);\r
+        if (indexTest >= 0)\r
+            return null;\r
+        return s;\r
+    }\r
+\r
+    static int getIntId(Object id) {\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            int index = (int) d;\r
+            if (((double) index) == d)\r
+                return index;\r
+            return 0;\r
+        }\r
+        String s = toString(id);\r
+        long indexTest = indexFromString(s);\r
+        if (indexTest >= 0)\r
+            return (int)indexTest;\r
+        return 0;\r
+    }\r
+\r
+    public static Object getElem(Object obj, Object id, Scriptable scope) {\r
+        int index;\r
+        String s;\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            index = (int) d;\r
+            s = ((double) index) == d ? null : toString(id);\r
+        } else {\r
+            s = toString(id);\r
+            long indexTest = indexFromString(s);\r
+            if (indexTest >= 0) {\r
+                index = (int)indexTest;\r
+                s = null;\r
+            } else {\r
+                index = 0;\r
+            }                \r
+        }\r
+        Scriptable start = obj instanceof Scriptable\r
+                           ? (Scriptable) obj\r
+                           : toObject(scope, obj);\r
+        Scriptable m = start;\r
+        if (s != null) {\r
+            if (s.equals("__proto__"))\r
+                return start.getPrototype();\r
+            if (s.equals("__parent__"))\r
+                return start.getParentScope();\r
+            while (m != null) {\r
+                Object result = m.get(s, start);\r
+                if (result != Scriptable.NOT_FOUND)\r
+                    return result;\r
+                m = m.getPrototype();\r
+            }\r
+            return Undefined.instance;\r
+        }\r
+        while (m != null) {\r
+            Object result = m.get(index, start);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        }\r
+        return Undefined.instance;\r
+    }\r
+\r
+\r
+    /*\r
+     * A cheaper and less general version of the above for well-known argument\r
+     * types.\r
+     */\r
+    public static Object getElem(Scriptable obj, int index)\r
+    {\r
+        Scriptable m = obj;\r
+        while (m != null) {\r
+            Object result = m.get(index, obj);\r
+            if (result != Scriptable.NOT_FOUND)\r
+                return result;\r
+            m = m.getPrototype();\r
+        }\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object setElem(Object obj, Object id, Object value,\r
+                                 Scriptable scope)\r
+    {\r
+        int index;\r
+        String s;\r
+        if (id instanceof Number) {\r
+            double d = ((Number) id).doubleValue();\r
+            index = (int) d;\r
+            s = ((double) index) == d ? null : toString(id);\r
+        } else {\r
+            s = toString(id);\r
+            long indexTest = indexFromString(s);\r
+            if (indexTest >= 0) {\r
+                index = (int)indexTest;\r
+                s = null;\r
+            } else {\r
+                index = 0;\r
+            }\r
+        }\r
+\r
+        Scriptable start = obj instanceof Scriptable\r
+                     ? (Scriptable) obj\r
+                     : toObject(scope, obj);\r
+        Scriptable m = start;\r
+        if (s != null) {\r
+            if (s.equals("__proto__"))\r
+                return setProto(obj, value, scope);\r
+            if (s.equals("__parent__"))\r
+                return setParent(obj, value, scope);\r
+\r
+            do {\r
+                if (m.has(s, start)) {\r
+                    m.put(s, start, value);\r
+                    return value;\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            start.put(s, start, value);\r
+            return value;\r
+       }\r
+\r
+        do {\r
+            if (m.has(index, start)) {\r
+                m.put(index, start, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        start.put(index, start, value);\r
+        return value;\r
+    }\r
+\r
+    /*\r
+     * A cheaper and less general version of the above for well-known argument\r
+     * types.\r
+     */\r
+    public static Object setElem(Scriptable obj, int index, Object value)\r
+    {\r
+        Scriptable m = obj;\r
+        do {\r
+            if (m.has(index, obj)) {\r
+                m.put(index, obj, value);\r
+                return value;\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        obj.put(index, obj, value);\r
+        return value;\r
+    }\r
+\r
+    /**\r
+     * The delete operator\r
+     *\r
+     * See ECMA 11.4.1\r
+     *\r
+     * In ECMA 0.19, the description of the delete operator (11.4.1)\r
+     * assumes that the [[Delete]] method returns a value. However,\r
+     * the definition of the [[Delete]] operator (8.6.2.5) does not\r
+     * define a return value. Here we assume that the [[Delete]]\r
+     * method doesn't return a value.\r
+     */\r
+    public static Object delete(Object obj, Object id) {\r
+        String s = getStringId(id);\r
+        boolean result = s != null\r
+            ? ScriptableObject.deleteProperty((Scriptable) obj, s)\r
+            : ScriptableObject.deleteProperty((Scriptable) obj, getIntId(id));\r
+        return result ? Boolean.TRUE : Boolean.FALSE;\r
+    }\r
+\r
+    /**\r
+     * Looks up a name in the scope chain and returns its value.\r
+     */\r
+    public static Object name(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND)\r
+                    return result;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id.toString()),\r
+                        scopeChain);\r
+    }\r
+\r
+    /**\r
+     * Returns the object in the scope chain that has a given property.\r
+     *\r
+     * The order of evaluation of an assignment expression involves\r
+     * evaluating the lhs to a reference, evaluating the rhs, and then\r
+     * modifying the reference with the rhs value. This method is used\r
+     * to 'bind' the given name to an object containing that property\r
+     * so that the side effects of evaluating the rhs do not affect\r
+     * which property is modified.\r
+     * Typically used in conjunction with setName.\r
+     *\r
+     * See ECMA 10.1.4\r
+     */\r
+    public static Scriptable bind(Scriptable scope, String id) {\r
+        Scriptable obj = scope;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                if (m.has(id, obj))\r
+                    return obj;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        return null;\r
+    }\r
+\r
+    public static Scriptable getBase(Scriptable scope, String id) {\r
+        Scriptable obj = scope;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                if (m.get(id, obj) != Scriptable.NOT_FOUND)\r
+                    return obj;\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError(\r
+                    Context.getContext(), "ReferenceError",\r
+                    ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scope);\r
+    }\r
+\r
+    public static Scriptable getThis(Scriptable base) {\r
+        while (base instanceof NativeWith)\r
+            base = base.getPrototype();\r
+        if (base instanceof NativeCall)\r
+            base = ScriptableObject.getTopLevelScope(base);\r
+        return base;\r
+    }\r
+\r
+    public static Object setName(Scriptable bound, Object value,\r
+                                 Scriptable scope, String id)\r
+    {\r
+        if (bound == null) {\r
+            // "newname = 7;", where 'newname' has not yet\r
+            // been defined, creates a new property in the\r
+            // global object. Find the global object by\r
+            // walking up the scope chain.\r
+            Scriptable next = scope;\r
+            do {\r
+                bound = next;\r
+                next = bound.getParentScope();\r
+            } while (next != null);\r
+\r
+            bound.put(id, bound, value);\r
+            /*\r
+            This code is causing immense performance problems in\r
+            scripts that assign to the variables as a way of creating them.\r
+            XXX need strict mode\r
+            String message = getMessage1("msg.assn.create", id);\r
+            Context.reportWarning(message);\r
+            */\r
+            return value;\r
+        }\r
+        return setProp(bound, id, value, scope);\r
+    }\r
+\r
+    public static Enumeration initEnum(Object value, Scriptable scope) {\r
+        Scriptable m = toObject(scope, value);\r
+        return new IdEnumeration(m);\r
+    }\r
+\r
+    public static Object nextEnum(Enumeration enum) {\r
+        // OPT this could be more efficient; should junk the Enumeration\r
+        // interface\r
+        if (!enum.hasMoreElements())\r
+            return null;\r
+        return enum.nextElement();\r
+    }\r
+\r
+    // Form used by class files generated by 1.4R3 and earlier.\r
+    public static Object call(Context cx, Object fun, Object thisArg,\r
+                              Object[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Scriptable scope = null;\r
+        if (fun instanceof Scriptable) \r
+            scope = ((Scriptable) fun).getParentScope();\r
+        return call(cx, fun, thisArg, args, scope);\r
+    }\r
+    \r
+    public static Object call(Context cx, Object fun, Object thisArg,\r
+                              Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Function function;\r
+        try {\r
+            function = (Function) fun;\r
+        }\r
+        catch (ClassCastException e) {\r
+            throw NativeGlobal.typeError1\r
+                ("msg.isnt.function", toString(fun), scope);\r
+        }\r
+\r
+        Scriptable thisObj;\r
+        if (thisArg instanceof Scriptable || thisArg == null) {\r
+            thisObj = (Scriptable) thisArg;\r
+        } else {\r
+            thisObj = ScriptRuntime.toObject(scope, thisArg);\r
+        }\r
+        if (function == null) throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        return function.call(cx, scope, thisObj, args);\r
+    }\r
+\r
+    private static Object callOrNewSpecial(Context cx, Scriptable scope,\r
+                                           Object fun, Object jsThis, \r
+                                           Object thisArg,\r
+                                           Object[] args, boolean isCall,\r
+                                           String filename, int lineNumber)\r
+        throws JavaScriptException\r
+    {\r
+        if (fun instanceof IdFunction) {\r
+            IdFunction f = (IdFunction)fun;\r
+            String name = f.getFunctionName();\r
+            if (name.length() == 4) {\r
+                if (name.equals("eval")) {\r
+                    if (f.master.getClass() == NativeGlobal.class) {\r
+                        return NativeGlobal.evalSpecial(cx, scope, \r
+                                                        thisArg, args,\r
+                                                        filename, lineNumber);\r
+                    }\r
+                }\r
+                else if (name.equals("With")) {\r
+                    if (f.master.getClass() == NativeWith.class) {\r
+                        return NativeWith.newWithSpecial(cx, args, f, !isCall);\r
+                    }\r
+                }\r
+                else if (name.equals("exec")) {\r
+                    if (f.master.getClass() == NativeScript.class) {\r
+                        return ((NativeScript)jsThis).\r
+                            exec(cx, ScriptableObject.getTopLevelScope(scope));\r
+                    }\r
+                    else {\r
+                        RegExpProxy proxy = cx.getRegExpProxy();\r
+                        if (proxy != null && proxy.isRegExp(jsThis)) {\r
+                            return call(cx, fun, jsThis, args, scope);\r
+                        }\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        else    // could've been <java>.XXX.exec() that was re-directed here\r
+            if (fun instanceof NativeJavaMethod)\r
+                return call(cx, fun, jsThis, args, scope);\r
+\r
+        if (isCall)\r
+            return call(cx, fun, thisArg, args, scope);\r
+        return newObject(cx, fun, args, scope);\r
+    }\r
+\r
+    public static Object callSpecial(Context cx, Object fun,\r
+                                     Object thisArg, Object[] args,\r
+                                     Scriptable enclosingThisArg,\r
+                                     Scriptable scope, String filename,\r
+                                     int lineNumber)\r
+        throws JavaScriptException\r
+    {\r
+        return callOrNewSpecial(cx, scope, fun, thisArg,\r
+                                enclosingThisArg, args, true,\r
+                                filename, lineNumber);\r
+    }\r
+\r
+    /**\r
+     * Operator new.\r
+     *\r
+     * See ECMA 11.2.2\r
+     */\r
+    public static Scriptable newObject(Context cx, Object fun,\r
+                                       Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        Function f;\r
+        try {\r
+            f = (Function) fun;\r
+            if (f != null) {\r
+                return f.construct(cx, scope, args);\r
+           }\r
+            // else fall through to error\r
+        } catch (ClassCastException e) {\r
+            // fall through to error\r
+        }\r
+        throw NativeGlobal.typeError1\r
+            ("msg.isnt.function", toString(fun), scope);\r
+    }\r
+\r
+    public static Scriptable newObjectSpecial(Context cx, Object fun,\r
+                                              Object[] args, Scriptable scope)\r
+        throws JavaScriptException\r
+    {\r
+        return (Scriptable) callOrNewSpecial(cx, scope, fun, null, null, args,\r
+                                             false, null, -1);\r
+    }\r
+\r
+    /**\r
+     * The typeof operator\r
+     */\r
+    public static String typeof(Object value) {\r
+        if (value == Undefined.instance)\r
+            return "undefined";\r
+        if (value == null)\r
+            return "object";\r
+        if (value instanceof Scriptable)\r
+            return (value instanceof Function) ? "function" : "object";\r
+        if (value instanceof String)\r
+            return "string";\r
+        if (value instanceof Number)\r
+            return "number";\r
+        if (value instanceof Boolean)\r
+            return "boolean";\r
+        throw errorWithClassName("msg.invalid.type", value);\r
+    }\r
+\r
+    /**\r
+     * The typeof operator that correctly handles the undefined case\r
+     */\r
+    public static String typeofName(Scriptable scope, String id) {\r
+        Object val = bind(scope, id);\r
+        if (val == null)\r
+            return "undefined";\r
+        return typeof(getProp(val, id, scope));\r
+    }\r
+\r
+    // neg:\r
+    // implement the '-' operator inline in the caller\r
+    // as "-toNumber(val)"\r
+\r
+    // not:\r
+    // implement the '!' operator inline in the caller\r
+    // as "!toBoolean(val)"\r
+\r
+    // bitnot:\r
+    // implement the '~' operator inline in the caller\r
+    // as "~toInt32(val)"\r
+\r
+    public static Object add(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(null);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(null);\r
+        if (!(val1 instanceof String) && !(val2 instanceof String))\r
+            if ((val1 instanceof Number) && (val2 instanceof Number))\r
+                return new Double(((Number)val1).doubleValue() +\r
+                                  ((Number)val2).doubleValue());\r
+            else\r
+                return new Double(toNumber(val1) + toNumber(val2));\r
+        return toString(val1) + toString(val2);\r
+    }\r
+\r
+    public static Object postIncrement(Object value) {\r
+        if (value instanceof Number)\r
+            value = new Double(((Number)value).doubleValue() + 1.0);\r
+        else\r
+            value = new Double(toNumber(value) + 1.0);\r
+        return value;\r
+    }\r
+\r
+    public static Object postIncrement(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND) {\r
+                    Object newValue = result;\r
+                    if (newValue instanceof Number) {\r
+                        newValue = new Double(\r
+                                    ((Number)newValue).doubleValue() + 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return result;\r
+                    }\r
+                    else {\r
+                        newValue = new Double(toNumber(newValue) + 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return new Double(toNumber(result));\r
+                    }\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scopeChain);\r
+    }\r
+\r
+    public static Object postIncrement(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND) {\r
+                Object newValue = result;\r
+                if (newValue instanceof Number) {\r
+                    newValue = new Double(\r
+                                ((Number)newValue).doubleValue() + 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return result;\r
+                }\r
+                else {\r
+                    newValue = new Double(toNumber(newValue) + 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return new Double(toNumber(result));\r
+                }\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object postIncrementElem(Object obj,\r
+                                            Object index, Scriptable scope) {\r
+        Object oldValue = getElem(obj, index, scope);\r
+        if (oldValue == Undefined.instance)\r
+            return Undefined.instance;\r
+        double resultValue = toNumber(oldValue);\r
+        Double newValue = new Double(resultValue + 1.0);\r
+        setElem(obj, index, newValue, scope);\r
+        return new Double(resultValue);\r
+    }\r
+\r
+    public static Object postDecrementElem(Object obj,\r
+                                            Object index, Scriptable scope) {\r
+        Object oldValue = getElem(obj, index, scope);\r
+        if (oldValue == Undefined.instance)\r
+            return Undefined.instance;\r
+        double resultValue = toNumber(oldValue);\r
+        Double newValue = new Double(resultValue - 1.0);\r
+        setElem(obj, index, newValue, scope);\r
+        return new Double(resultValue);\r
+    }\r
+\r
+    public static Object postDecrement(Object value) {\r
+        if (value instanceof Number)\r
+            value = new Double(((Number)value).doubleValue() - 1.0);\r
+        else\r
+            value = new Double(toNumber(value) - 1.0);\r
+        return value;\r
+    }\r
+\r
+    public static Object postDecrement(Scriptable scopeChain, String id) {\r
+        Scriptable obj = scopeChain;\r
+        Object prop;\r
+        while (obj != null) {\r
+            Scriptable m = obj;\r
+            do {\r
+                Object result = m.get(id, obj);\r
+                if (result != Scriptable.NOT_FOUND) {\r
+                    Object newValue = result;\r
+                    if (newValue instanceof Number) {\r
+                        newValue = new Double(\r
+                                    ((Number)newValue).doubleValue() - 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return result;\r
+                    }\r
+                    else {\r
+                        newValue = new Double(toNumber(newValue) - 1.0);\r
+                        m.put(id, obj, newValue);\r
+                        return new Double(toNumber(result));\r
+                    }\r
+                }\r
+                m = m.getPrototype();\r
+            } while (m != null);\r
+            obj = obj.getParentScope();\r
+        }\r
+        throw NativeGlobal.constructError\r
+            (Context.getContext(), "ReferenceError",\r
+             ScriptRuntime.getMessage1("msg.is.not.defined", id),\r
+                    scopeChain);\r
+    }\r
+\r
+    public static Object postDecrement(Object obj, String id, Scriptable scope) {\r
+        Scriptable start;\r
+        if (obj instanceof Scriptable) {\r
+            start = (Scriptable) obj;\r
+        } else {\r
+            start = toObject(scope, obj);\r
+        }\r
+        if (start == null) {\r
+            throw NativeGlobal.typeError0("msg.null.to.object", scope);\r
+        }\r
+        Scriptable m = start;\r
+        do {\r
+            Object result = m.get(id, start);\r
+            if (result != Scriptable.NOT_FOUND) {\r
+                Object newValue = result;\r
+                if (newValue instanceof Number) {\r
+                    newValue = new Double(\r
+                                ((Number)newValue).doubleValue() - 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return result;\r
+                }\r
+                else {\r
+                    newValue = new Double(toNumber(newValue) - 1.0);\r
+                    m.put(id, start, newValue);\r
+                    return new Double(toNumber(result));\r
+                }\r
+            }\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return Undefined.instance;\r
+    }\r
+\r
+    public static Object toPrimitive(Object val) {\r
+        if (val == null || !(val instanceof Scriptable)) {\r
+            return val;\r
+        }\r
+        Object result = ((Scriptable) val).getDefaultValue(null);\r
+        if (result != null && result instanceof Scriptable)\r
+            throw NativeGlobal.typeError0("msg.bad.default.value", val);\r
+        return result;\r
+    }\r
+\r
+    private static Class getTypeOfValue(Object obj) {\r
+        if (obj == null)\r
+            return ScriptableClass;\r
+        if (obj == Undefined.instance)\r
+            return UndefinedClass;\r
+        if (obj instanceof Scriptable)\r
+            return ScriptableClass;\r
+        if (obj instanceof Number)\r
+            return NumberClass;\r
+        return obj.getClass();\r
+    }\r
+\r
+    /**\r
+     * Equality\r
+     *\r
+     * See ECMA 11.9\r
+     */\r
+    public static boolean eq(Object x, Object y) {\r
+        Object xCopy = x;                                       // !!! JIT bug in Cafe 2.1\r
+        Object yCopy = y;                                       // need local copies, otherwise their values get blown below\r
+        for (;;) {\r
+            Class typeX = getTypeOfValue(x);\r
+            Class typeY = getTypeOfValue(y);\r
+            if (typeX == typeY) {\r
+                if (typeX == UndefinedClass)\r
+                    return true;\r
+                if (typeX == NumberClass)\r
+                    return ((Number) x).doubleValue() ==\r
+                           ((Number) y).doubleValue();\r
+                if (typeX == StringClass || typeX == BooleanClass)\r
+                    return xCopy.equals(yCopy);                                 // !!! JIT bug in Cafe 2.1\r
+                if (typeX == ScriptableClass) {\r
+                    if (x == y)\r
+                        return true;\r
+                    if (x instanceof Wrapper &&\r
+                        y instanceof Wrapper)\r
+                    {\r
+                        return ((Wrapper) x).unwrap() ==\r
+                               ((Wrapper) y).unwrap();\r
+                    }\r
+                    return false;\r
+                }\r
+                throw new RuntimeException(); // shouldn't get here\r
+            }\r
+            if (x == null && y == Undefined.instance)\r
+                return true;\r
+            if (x == Undefined.instance && y == null)\r
+                return true;\r
+            if (typeX == NumberClass &&\r
+                typeY == StringClass)\r
+            {\r
+                return ((Number) x).doubleValue() == toNumber(y);\r
+            }\r
+            if (typeX == StringClass &&\r
+                typeY == NumberClass)\r
+            {\r
+                return toNumber(x) == ((Number) y).doubleValue();\r
+            }\r
+            if (typeX == BooleanClass) {\r
+                x = new Double(toNumber(x));\r
+                xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if (typeY == BooleanClass) {\r
+                y = new Double(toNumber(y));\r
+                yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if ((typeX == StringClass ||\r
+                 typeX == NumberClass) &&\r
+                typeY == ScriptableClass && y != null)\r
+            {\r
+                y = toPrimitive(y);\r
+                yCopy = y;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            if (typeX == ScriptableClass && x != null &&\r
+                (typeY == StringClass ||\r
+                 typeY == NumberClass))\r
+            {\r
+                x = toPrimitive(x);\r
+                xCopy = x;                                 // !!! JIT bug in Cafe 2.1\r
+                continue;\r
+            }\r
+            return false;\r
+        }\r
+    }\r
+\r
+    public static Boolean eqB(Object x, Object y) {\r
+        if (eq(x,y))\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static Boolean neB(Object x, Object y) {\r
+        if (eq(x,y))\r
+            return Boolean.FALSE;\r
+        else\r
+            return Boolean.TRUE;\r
+    }\r
+\r
+    public static boolean shallowEq(Object x, Object y) {\r
+        Class type = getTypeOfValue(x);\r
+        if (type != getTypeOfValue(y))\r
+            return false;\r
+        if (type == StringClass || type == BooleanClass)\r
+            return x.equals(y);\r
+        if (type == NumberClass)\r
+            return ((Number) x).doubleValue() ==\r
+                   ((Number) y).doubleValue();\r
+        if (type == ScriptableClass) {\r
+            if (x == y)\r
+                return true;\r
+            if (x instanceof Wrapper && y instanceof Wrapper)\r
+                return ((Wrapper) x).unwrap() ==\r
+                       ((Wrapper) y).unwrap();\r
+            return false;\r
+        }\r
+        if (type == UndefinedClass)\r
+            return true;\r
+        return false;\r
+    }\r
+\r
+    public static Boolean seqB(Object x, Object y) {\r
+        if (shallowEq(x,y))\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static Boolean sneB(Object x, Object y) {\r
+        if (shallowEq(x,y))\r
+            return Boolean.FALSE;\r
+        else\r
+            return Boolean.TRUE;\r
+    }\r
+\r
+    /**\r
+     * The instanceof operator.\r
+     *\r
+     * @return a instanceof b\r
+     */\r
+    public static boolean instanceOf(Scriptable scope, Object a, Object b) {\r
+        // Check RHS is an object\r
+        if (! (b instanceof Scriptable)) {\r
+            throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
+        }\r
+\r
+        // for primitive values on LHS, return false\r
+        // XXX we may want to change this so that\r
+        // 5 instanceof Number == true\r
+        if (! (a instanceof Scriptable))\r
+            return false;\r
+\r
+        return ((Scriptable)b).hasInstance((Scriptable)a);\r
+    }\r
+\r
+    /**\r
+     * Delegates to\r
+     *\r
+     * @return true iff rhs appears in lhs' proto chain\r
+     */\r
+    protected static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {\r
+        Scriptable proto = lhs.getPrototype();\r
+\r
+        while (proto != null) {\r
+            if (proto.equals(rhs)) return true;\r
+            proto = proto.getPrototype();\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * The in operator.\r
+     *\r
+     * This is a new JS 1.3 language feature.  The in operator mirrors\r
+     * the operation of the for .. in construct, and tests whether the\r
+     * rhs has the property given by the lhs.  It is different from the \r
+     * for .. in construct in that:\r
+     * <BR> - it doesn't perform ToObject on the right hand side\r
+     * <BR> - it returns true for DontEnum properties.\r
+     * @param a the left hand operand\r
+     * @param b the right hand operand\r
+     *\r
+     * @return true if property name or element number a is a property of b\r
+     */\r
+    public static boolean in(Object a, Object b, Scriptable scope) {\r
+        if (!(b instanceof Scriptable)) {\r
+            throw NativeGlobal.typeError0("msg.instanceof.not.object", scope);\r
+        }\r
+        String s = getStringId(a);\r
+        return s != null\r
+            ? ScriptableObject.hasProperty((Scriptable) b, s)\r
+            : ScriptableObject.hasProperty((Scriptable) b, getIntId(a));\r
+    }\r
+\r
+    public static Boolean cmp_LTB(Object val1, Object val2) {\r
+        if (cmp_LT(val1, val2) == 1)\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static int cmp_LT(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
+        if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
+            double d1 = toNumber(val1);\r
+            if (d1 != d1)\r
+                return 0;\r
+            double d2 = toNumber(val2);\r
+            if (d2 != d2)\r
+                return 0;\r
+            return d1 < d2 ? 1 : 0;\r
+        }\r
+        return toString(val1).compareTo(toString(val2)) < 0 ? 1 : 0;\r
+    }\r
+\r
+    public static Boolean cmp_LEB(Object val1, Object val2) {\r
+        if (cmp_LE(val1, val2) == 1)\r
+            return Boolean.TRUE;\r
+        else\r
+            return Boolean.FALSE;\r
+    }\r
+\r
+    public static int cmp_LE(Object val1, Object val2) {\r
+        if (val1 instanceof Scriptable)\r
+            val1 = ((Scriptable) val1).getDefaultValue(NumberClass);\r
+        if (val2 instanceof Scriptable)\r
+            val2 = ((Scriptable) val2).getDefaultValue(NumberClass);\r
+        if (!(val1 instanceof String) || !(val2 instanceof String)) {\r
+            double d1 = toNumber(val1);\r
+            if (d1 != d1)\r
+                return 0;\r
+            double d2 = toNumber(val2);\r
+            if (d2 != d2)\r
+                return 0;\r
+            return d1 <= d2 ? 1 : 0;\r
+        }\r
+        return toString(val1).compareTo(toString(val2)) <= 0 ? 1 : 0;\r
+    }\r
+\r
+    // lt:\r
+    // implement the '<' operator inline in the caller\r
+    // as "compare(val1, val2) == 1"\r
+\r
+    // le:\r
+    // implement the '<=' operator inline in the caller\r
+    // as "compare(val2, val1) == 0"\r
+\r
+    // gt:\r
+    // implement the '>' operator inline in the caller\r
+    // as "compare(val2, val1) == 1"\r
+\r
+    // ge:\r
+    // implement the '>=' operator inline in the caller\r
+    // as "compare(val1, val2) == 0"\r
+\r
+    // ------------------\r
+    // Statements\r
+    // ------------------\r
+\r
+    private static final String GLOBAL_CLASS = \r
+        "org.mozilla.javascript.tools.shell.Global";\r
+\r
+    private static ScriptableObject getGlobal(Context cx) {\r
+        try {\r
+            Class globalClass = loadClassName(GLOBAL_CLASS);\r
+            Class[] parm = { Context.class };\r
+            Constructor globalClassCtor = globalClass.getConstructor(parm);\r
+            Object[] arg = { cx };\r
+            return (ScriptableObject) globalClassCtor.newInstance(arg);\r
+        } catch (ClassNotFoundException e) {\r
+            // fall through...\r
+        } catch (NoSuchMethodException e) {\r
+            // fall through...\r
+        } catch (InvocationTargetException e) {\r
+            // fall through...\r
+        } catch (IllegalAccessException e) {\r
+            // fall through...\r
+        } catch (InstantiationException e) {\r
+            // fall through...\r
+        }\r
+        return new ImporterTopLevel(cx);\r
+    }\r
+\r
+    public static void main(String scriptClassName, String[] args)\r
+        throws JavaScriptException\r
+    {\r
+        Context cx = Context.enter();\r
+        ScriptableObject global = getGlobal(cx);\r
+\r
+        // get the command line arguments and define "arguments" \r
+        // array in the top-level object\r
+        Scriptable argsObj = cx.newArray(global, args);\r
+        global.defineProperty("arguments", argsObj,\r
+                              ScriptableObject.DONTENUM);\r
+        \r
+        try {\r
+            Class cl = loadClassName(scriptClassName);\r
+            Script script = (Script) cl.newInstance();\r
+            script.exec(cx, global);\r
+            return;\r
+        }\r
+        catch (ClassNotFoundException e) {\r
+        }\r
+        catch (InstantiationException e) {\r
+        }\r
+        catch (IllegalAccessException e) {\r
+        }\r
+        finally {\r
+            Context.exit();\r
+        }\r
+        throw new RuntimeException("Error creating script object");\r
+    }\r
+\r
+    public static Scriptable initScript(Context cx, Scriptable scope,\r
+                                        NativeFunction funObj,\r
+                                        Scriptable thisObj,\r
+                                        boolean fromEvalCode)\r
+    {\r
+        String[] argNames = funObj.argNames;\r
+        if (argNames != null) {\r
+            ScriptableObject so;\r
+            try {\r
+                /* Global var definitions are supposed to be DONTDELETE\r
+                 * so we try to create them that way by hoping that the\r
+                 * scope is a ScriptableObject which provides access to\r
+                 * setting the attributes.\r
+                 */\r
+                so = (ScriptableObject) scope;\r
+            } catch (ClassCastException x) {\r
+                // oh well, we tried.\r
+                so = null;\r
+            }\r
+\r
+            Scriptable varScope = scope;\r
+            if (fromEvalCode) {\r
+                // When executing an eval() inside a with statement,\r
+                // define any variables resulting from var statements\r
+                // in the first non-with scope. See bug 38590.\r
+                varScope = scope;\r
+                while (varScope instanceof NativeWith)\r
+                    varScope = varScope.getParentScope();\r
+            }\r
+            for (int i = argNames.length; i-- != 0;) {\r
+                String name = argNames[i];\r
+                // Don't overwrite existing def if already defined in object\r
+                // or prototypes of object.\r
+                if (!hasProp(scope, name)) {\r
+                    if (so != null && !fromEvalCode)\r
+                        so.defineProperty(name, Undefined.instance,\r
+                                          ScriptableObject.PERMANENT);\r
+                    else \r
+                        varScope.put(name, varScope, Undefined.instance);\r
+                }\r
+            }\r
+        }\r
+\r
+        return scope;\r
+    }\r
+\r
+    public static Scriptable runScript(Script script) {\r
+        Context cx = Context.enter();\r
+        ScriptableObject global = getGlobal(cx);\r
+        try {\r
+            script.exec(cx, global);\r
+        } catch (JavaScriptException e) {\r
+            throw new Error(e.toString());\r
+        } finally {\r
+            Context.exit();\r
+        }\r
+        return global;\r
+    }\r
+\r
+    public static Scriptable initVarObj(Context cx, Scriptable scope,\r
+                                        NativeFunction funObj,\r
+                                        Scriptable thisObj, Object[] args)\r
+    {\r
+        NativeCall result = new NativeCall(cx, scope, funObj, thisObj, args);\r
+        String[] argNames = funObj.argNames;\r
+        if (argNames != null) {\r
+            for (int i = funObj.argCount; i != argNames.length; i++) {\r
+                String name = argNames[i];\r
+                result.put(name, result, Undefined.instance);\r
+            }\r
+        }\r
+        return result;\r
+    }\r
+\r
+    public static void popActivation(Context cx) {\r
+        NativeCall current = cx.currentActivation;\r
+        if (current != null) {\r
+            cx.currentActivation = current.caller;\r
+            current.caller = null;\r
+        }\r
+    }\r
+\r
+    public static Scriptable newScope() {\r
+        return new NativeObject();\r
+    }\r
+\r
+    public static Scriptable enterWith(Object value, Scriptable scope) {\r
+        return new NativeWith(scope, toObject(scope, value));\r
+    }\r
+\r
+    public static Scriptable leaveWith(Scriptable scope) {\r
+        return scope.getParentScope();\r
+    }\r
+\r
+    public static NativeFunction initFunction(NativeFunction fn,\r
+                                              Scriptable scope,\r
+                                              String fnName,\r
+                                              Context cx,\r
+                                              boolean doSetName)\r
+    {\r
+        fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
+        fn.setParentScope(scope);\r
+        if (doSetName)\r
+            setName(scope, fn, scope, fnName);\r
+        return fn;\r
+    }\r
+\r
+    public static NativeFunction createFunctionObject(Scriptable scope,\r
+                                                      Class functionClass,\r
+                                                      Context cx,\r
+                                                      boolean setName)\r
+    {\r
+        Constructor[] ctors = functionClass.getConstructors();\r
+\r
+        NativeFunction result = null;\r
+        Object[] initArgs = { scope, cx };\r
+        try {\r
+            result = (NativeFunction) ctors[0].newInstance(initArgs);\r
+        }\r
+        catch (InstantiationException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (IllegalAccessException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (IllegalArgumentException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+        catch (InvocationTargetException e) {\r
+            throw WrappedException.wrapException(e);\r
+        }\r
+\r
+        result.setPrototype(ScriptableObject.getClassPrototype(scope, "Function"));\r
+        result.setParentScope(scope);\r
+\r
+        String fnName = result.getFunctionName();\r
+        if (setName && fnName != null && fnName.length() != 0 && \r
+            !fnName.equals("anonymous"))\r
+        {\r
+            setProp(scope, fnName, result, scope);\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    static void checkDeprecated(Context cx, String name) {\r
+        int version = cx.getLanguageVersion();\r
+        if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {\r
+            String msg = getMessage1("msg.deprec.ctor", name);\r
+            if (version == Context.VERSION_DEFAULT)\r
+                Context.reportWarning(msg);\r
+            else\r
+                throw Context.reportRuntimeError(msg);\r
+        }\r
+    }\r
+\r
+    public static String getMessage0(String messageId) {\r
+        return Context.getMessage0(messageId);\r
+    }\r
+\r
+    public static String getMessage1(String messageId, Object arg1) {\r
+        return Context.getMessage1(messageId, arg1);\r
+    }\r
+\r
+    public static String getMessage2\r
+        (String messageId, Object arg1, Object arg2) \r
+    {\r
+        return Context.getMessage2(messageId, arg1, arg2);\r
+    }\r
+\r
+    public static String getMessage(String messageId, Object[] arguments) {\r
+        return Context.getMessage(messageId, arguments);\r
+    }\r
+\r
+    public static RegExpProxy getRegExpProxy(Context cx) {\r
+        return cx.getRegExpProxy();\r
+    }\r
+\r
+    public static NativeCall getCurrentActivation(Context cx) {\r
+        return cx.currentActivation;\r
+    }\r
+\r
+    public static void setCurrentActivation(Context cx,\r
+                                            NativeCall activation)\r
+    {\r
+        cx.currentActivation = activation;\r
+    }\r
+\r
+    public static Class loadClassName(String className) \r
+        throws ClassNotFoundException\r
+    {\r
+        /*\r
+        try {\r
+            ClassLoader cl = DefiningClassLoader.getContextClassLoader();\r
+            if (cl != null)\r
+                return cl.loadClass(className);\r
+        } catch (SecurityException e) {\r
+            // fall through...\r
+        } catch (ClassNotFoundException e) {\r
+            // Rather than just letting the exception propagate\r
+            // we'll try Class.forName as well. The results could be\r
+            // different if this class was loaded on a different\r
+            // thread than the current thread.\r
+            // So fall through...\r
+        }\r
+        */\r
+        return Class.forName(className);                \r
+    }\r
+\r
+    static boolean hasProp(Scriptable start, String name) {\r
+        Scriptable m = start;\r
+        do {\r
+            if (m.has(name, start))\r
+                return true;\r
+            m = m.getPrototype();\r
+        } while (m != null);\r
+        return false;\r
+    }\r
+\r
+    private static RuntimeException errorWithClassName(String msg, Object val)\r
+    {\r
+        return Context.reportRuntimeError1(msg, val.getClass().getName());\r
+    }\r
+\r
+    public static final Object[] emptyArgs = new Object[0];\r
+\r
+}\r
+\r
+\r
+/**\r
+ * This is the enumeration needed by the for..in statement.\r
+ *\r
+ * See ECMA 12.6.3.\r
+ *\r
+ * IdEnumeration maintains a Hashtable to make sure a given\r
+ * id is enumerated only once across multiple objects in a\r
+ * prototype chain.\r
+ *\r
+ * XXX - ECMA delete doesn't hide properties in the prototype,\r
+ * but js/ref does. This means that the js/ref for..in can\r
+ * avoid maintaining a hash table and instead perform lookups\r
+ * to see if a given property has already been enumerated.\r
+ *\r
+ */\r
+class IdEnumeration implements Enumeration {\r
+    IdEnumeration(Scriptable m) {\r
+        used = new Hashtable(27);\r
+        changeObject(m);\r
+        next = getNext();\r
+    }\r
+\r
+    public boolean hasMoreElements() {\r
+        return next != null;\r
+    }\r
+\r
+    public Object nextElement() {\r
+        Object result = next;\r
+\r
+        // only key used; 'next' as value for convenience\r
+        used.put(next, next);\r
+\r
+        next = getNext();\r
+        return result;\r
+    }\r
+\r
+    private void changeObject(Scriptable m) {\r
+        obj = m;\r
+        if (obj != null) {\r
+            array = m.getIds();\r
+            if (array.length == 0)\r
+                changeObject(obj.getPrototype());\r
+        }\r
+        index = 0;\r
+    }\r
+\r
+    private Object getNext() {\r
+        if (obj == null)\r
+            return null;\r
+        Object result;\r
+        for (;;) {\r
+            if (index == array.length) {\r
+                changeObject(obj.getPrototype());\r
+                if (obj == null)\r
+                    return null;\r
+            }\r
+            result = array[index++];\r
+            if (result instanceof String) {\r
+                if (!obj.has((String) result, obj))\r
+                    continue;   // must have been deleted\r
+            } else {\r
+                if (!obj.has(((Number) result).intValue(), obj))\r
+                    continue;   // must have been deleted\r
+            }\r
+            if (!used.containsKey(result)) {\r
+                break;\r
+            }\r
+        }\r
+        return ScriptRuntime.toString(result);\r
+    }\r
+\r
+    private Object next;\r
+    private Scriptable obj;\r
+    private int index;\r
+    private Object[] array;\r
+    private Hashtable used;\r
+}\r