--- /dev/null
+/* -*- 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