aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java')
-rw-r--r--trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java3830
1 files changed, 3830 insertions, 0 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java
new file mode 100644
index 0000000..f879581
--- /dev/null
+++ b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ScriptRuntime.java
@@ -0,0 +1,3830 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Patrick Beard
+ * Norris Boyd
+ * Igor Bukanov
+ * Ethan Hugg
+ * Bob Jervis
+ * Roger Lawrence
+ * Terry Lucas
+ * Frank Mitchell
+ * Milen Nankov
+ * Hannes Wallnoefer
+ * Andrew Wason
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+import java.lang.reflect.*;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.mozilla.javascript.xml.XMLObject;
+import org.mozilla.javascript.xml.XMLLib;
+
+/**
+ * This is the class that implements the runtime.
+ *
+ * @author Norris Boyd
+ */
+
+public class ScriptRuntime {
+
+ /**
+ * No instances should be created.
+ */
+ protected ScriptRuntime() {
+ }
+
+ private static class NoSuchMethodShim implements Callable {
+ String methodName;
+ Callable noSuchMethodMethod;
+
+ NoSuchMethodShim(Callable noSuchMethodMethod, String methodName)
+ {
+ this.noSuchMethodMethod = noSuchMethodMethod;
+ this.methodName = methodName;
+ }
+ /**
+ * Perform the call.
+ *
+ * @param cx the current Context for this thread
+ * @param scope the scope to use to resolve properties.
+ * @param thisObj the JavaScript <code>this</code> object
+ * @param args the array of arguments
+ * @return the result of the call
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ Object[] nestedArgs = new Object[2];
+
+ nestedArgs[0] = methodName;
+ nestedArgs[1] = newArrayLiteral(args, null, cx, scope);
+ return noSuchMethodMethod.call(cx, scope, thisObj, nestedArgs);
+ }
+
+ }
+ /*
+ * There's such a huge space (and some time) waste for the Foo.class
+ * syntax: the compiler sticks in a test of a static field in the
+ * enclosing class for null and the code for creating the class value.
+ * It has to do this since the reference has to get pushed off until
+ * execution time (i.e. can't force an early load), but for the
+ * 'standard' classes - especially those in java.lang, we can trust
+ * that they won't cause problems by being loaded early.
+ */
+
+ public final static Class
+ BooleanClass = Kit.classOrNull("java.lang.Boolean"),
+ ByteClass = Kit.classOrNull("java.lang.Byte"),
+ CharacterClass = Kit.classOrNull("java.lang.Character"),
+ ClassClass = Kit.classOrNull("java.lang.Class"),
+ DoubleClass = Kit.classOrNull("java.lang.Double"),
+ FloatClass = Kit.classOrNull("java.lang.Float"),
+ IntegerClass = Kit.classOrNull("java.lang.Integer"),
+ LongClass = Kit.classOrNull("java.lang.Long"),
+ NumberClass = Kit.classOrNull("java.lang.Number"),
+ ObjectClass = Kit.classOrNull("java.lang.Object"),
+ ShortClass = Kit.classOrNull("java.lang.Short"),
+ StringClass = Kit.classOrNull("java.lang.String"),
+ DateClass = Kit.classOrNull("java.util.Date");
+
+ public final static Class
+ ContextClass
+ = Kit.classOrNull("org.mozilla.javascript.Context"),
+ ContextFactoryClass
+ = Kit.classOrNull("org.mozilla.javascript.ContextFactory"),
+ FunctionClass
+ = Kit.classOrNull("org.mozilla.javascript.Function"),
+ ScriptableClass
+ = Kit.classOrNull("org.mozilla.javascript.Scriptable"),
+ ScriptableObjectClass
+ = Kit.classOrNull("org.mozilla.javascript.ScriptableObject");
+
+ private static final String[] lazilyNames = {
+ "RegExp", "org.mozilla.javascript.regexp.NativeRegExp",
+ "Packages", "org.mozilla.javascript.NativeJavaTopPackage",
+ "java", "org.mozilla.javascript.NativeJavaTopPackage",
+ "javax", "org.mozilla.javascript.NativeJavaTopPackage",
+ "org", "org.mozilla.javascript.NativeJavaTopPackage",
+ "com", "org.mozilla.javascript.NativeJavaTopPackage",
+ "edu", "org.mozilla.javascript.NativeJavaTopPackage",
+ "net", "org.mozilla.javascript.NativeJavaTopPackage",
+ "getClass", "org.mozilla.javascript.NativeJavaTopPackage",
+ "JavaAdapter", "org.mozilla.javascript.JavaAdapter",
+ "JavaImporter", "org.mozilla.javascript.ImporterTopLevel",
+ "Continuation", "org.mozilla.javascript.continuations.Continuation",
+ // TODO Grotesque hack using literal string (xml) just to minimize
+ // changes for now
+ "XML", "(xml)",
+ "XMLList", "(xml)",
+ "Namespace", "(xml)",
+ "QName", "(xml)",
+ };
+
+ private static final Object LIBRARY_SCOPE_KEY = new Object();
+
+ public static boolean isRhinoRuntimeType(Class cl)
+ {
+ if (cl.isPrimitive()) {
+ return (cl != Character.TYPE);
+ } else {
+ return (cl == StringClass || cl == BooleanClass
+ || NumberClass.isAssignableFrom(cl)
+ || ScriptableClass.isAssignableFrom(cl));
+ }
+ }
+
+ public static ScriptableObject initStandardObjects(Context cx,
+ ScriptableObject scope,
+ boolean sealed)
+ {
+ if (scope == null) {
+ scope = new NativeObject();
+ }
+ scope.associateValue(LIBRARY_SCOPE_KEY, scope);
+ (new ClassCache()).associate(scope);
+
+ BaseFunction.init(scope, sealed);
+ NativeObject.init(scope, sealed);
+
+ Scriptable objectProto = ScriptableObject.getObjectPrototype(scope);
+
+ // Function.prototype.__proto__ should be Object.prototype
+ Scriptable functionProto = ScriptableObject.getFunctionPrototype(scope);
+ functionProto.setPrototype(objectProto);
+
+ // Set the prototype of the object passed in if need be
+ if (scope.getPrototype() == null)
+ scope.setPrototype(objectProto);
+
+ // must precede NativeGlobal since it's needed therein
+ NativeError.init(scope, sealed);
+ NativeGlobal.init(cx, scope, sealed);
+
+ NativeArray.init(scope, sealed);
+ if (cx.getOptimizationLevel() > 0) {
+ // When optimizing, attempt to fulfill all requests for new Array(N)
+ // with a higher threshold before switching to a sparse
+ // representation
+ NativeArray.setMaximumInitialCapacity(200000);
+ }
+ NativeString.init(scope, sealed);
+ NativeBoolean.init(scope, sealed);
+ NativeNumber.init(scope, sealed);
+ NativeDate.init(scope, sealed);
+ NativeMath.init(scope, sealed);
+
+ NativeWith.init(scope, sealed);
+ NativeCall.init(scope, sealed);
+ NativeScript.init(scope, sealed);
+
+ NativeIterator.init(scope, sealed); // Also initializes NativeGenerator
+
+ boolean withXml = cx.hasFeature(Context.FEATURE_E4X) &&
+ cx.getE4xImplementationFactory() != null;
+
+ for (int i = 0; i != lazilyNames.length; i += 2) {
+ String topProperty = lazilyNames[i];
+ String className = lazilyNames[i + 1];
+ if (!withXml && className.equals("(xml)")) {
+ continue;
+ } else if (withXml && className.equals("(xml)")) {
+ className = cx.getE4xImplementationFactory().
+ getImplementationClassName();
+ }
+ new LazilyLoadedCtor(scope, topProperty, className, sealed);
+ }
+
+ return scope;
+ }
+
+ public static ScriptableObject getLibraryScopeOrNull(Scriptable scope)
+ {
+ ScriptableObject libScope;
+ libScope = (ScriptableObject)ScriptableObject.
+ getTopScopeValue(scope, LIBRARY_SCOPE_KEY);
+ return libScope;
+ }
+
+ // It is public so NativeRegExp can access it.
+ public static boolean isJSLineTerminator(int c)
+ {
+ // Optimization for faster check for eol character:
+ // they do not have 0xDFD0 bits set
+ if ((c & 0xDFD0) != 0) {
+ return false;
+ }
+ return c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029;
+ }
+
+ public static Boolean wrapBoolean(boolean b)
+ {
+ return b ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public static Integer wrapInt(int i)
+ {
+ return new Integer(i);
+ }
+
+ public static Number wrapNumber(double x)
+ {
+ if (x != x) {
+ return ScriptRuntime.NaNobj;
+ }
+ return new Double(x);
+ }
+
+ /**
+ * Convert the value to a boolean.
+ *
+ * See ECMA 9.2.
+ */
+ public static boolean toBoolean(Object val)
+ {
+ for (;;) {
+ if (val instanceof Boolean)
+ return ((Boolean) val).booleanValue();
+ if (val == null || val == Undefined.instance)
+ return false;
+ if (val instanceof String)
+ return ((String) val).length() != 0;
+ if (val instanceof Number) {
+ double d = ((Number) val).doubleValue();
+ return (d == d && d != 0.0);
+ }
+ if (val instanceof Scriptable) {
+ if (val instanceof ScriptableObject &&
+ ((ScriptableObject) val).avoidObjectDetection())
+ {
+ return false;
+ }
+ if (Context.getContext().isVersionECMA1()) {
+ // pure ECMA
+ return true;
+ }
+ // ECMA extension
+ val = ((Scriptable) val).getDefaultValue(BooleanClass);
+ if (val instanceof Scriptable)
+ throw errorWithClassName("msg.primitive.expected", val);
+ continue;
+ }
+ warnAboutNonJSObject(val);
+ return true;
+ }
+ }
+
+ /**
+ * Convert the value to a number.
+ *
+ * See ECMA 9.3.
+ */
+ public static double toNumber(Object val)
+ {
+ for (;;) {
+ if (val instanceof Number)
+ return ((Number) val).doubleValue();
+ if (val == null)
+ return +0.0;
+ if (val == Undefined.instance)
+ return NaN;
+ if (val instanceof String)
+ return toNumber((String) val);
+ if (val instanceof Boolean)
+ return ((Boolean) val).booleanValue() ? 1 : +0.0;
+ if (val instanceof Scriptable) {
+ val = ((Scriptable) val).getDefaultValue(NumberClass);
+ if (val instanceof Scriptable)
+ throw errorWithClassName("msg.primitive.expected", val);
+ continue;
+ }
+ warnAboutNonJSObject(val);
+ return NaN;
+ }
+ }
+
+ public static double toNumber(Object[] args, int index) {
+ return (index < args.length) ? toNumber(args[index]) : NaN;
+ }
+
+ // Can not use Double.NaN defined as 0.0d / 0.0 as under the Microsoft VM,
+ // versions 2.01 and 3.0P1, that causes some uses (returns at least) of
+ // Double.NaN to be converted to 1.0.
+ // So we use ScriptRuntime.NaN instead of Double.NaN.
+ public static final double
+ NaN = Double.longBitsToDouble(0x7ff8000000000000L);
+
+ // A similar problem exists for negative zero.
+ public static final double
+ negativeZero = Double.longBitsToDouble(0x8000000000000000L);
+
+ public static final Double NaNobj = new Double(NaN);
+
+ /*
+ * Helper function for toNumber, parseInt, and TokenStream.getToken.
+ */
+ static double stringToNumber(String s, int start, int radix) {
+ char digitMax = '9';
+ char lowerCaseBound = 'a';
+ char upperCaseBound = 'A';
+ int len = s.length();
+ if (radix < 10) {
+ digitMax = (char) ('0' + radix - 1);
+ }
+ if (radix > 10) {
+ lowerCaseBound = (char) ('a' + radix - 10);
+ upperCaseBound = (char) ('A' + radix - 10);
+ }
+ int end;
+ double sum = 0.0;
+ for (end=start; end < len; end++) {
+ char c = s.charAt(end);
+ int newDigit;
+ if ('0' <= c && c <= digitMax)
+ newDigit = c - '0';
+ else if ('a' <= c && c < lowerCaseBound)
+ newDigit = c - 'a' + 10;
+ else if ('A' <= c && c < upperCaseBound)
+ newDigit = c - 'A' + 10;
+ else
+ break;
+ sum = sum*radix + newDigit;
+ }
+ if (start == end) {
+ return NaN;
+ }
+ if (sum >= 9007199254740992.0) {
+ if (radix == 10) {
+ /* If we're accumulating a decimal number and the number
+ * is >= 2^53, then the result from the repeated multiply-add
+ * above may be inaccurate. Call Java to get the correct
+ * answer.
+ */
+ try {
+ return Double.valueOf(s.substring(start, end)).doubleValue();
+ } catch (NumberFormatException nfe) {
+ return NaN;
+ }
+ } else if (radix == 2 || radix == 4 || radix == 8 ||
+ radix == 16 || radix == 32)
+ {
+ /* The number may also be inaccurate for one of these bases.
+ * This happens if the addition in value*radix + digit causes
+ * a round-down to an even least significant mantissa bit
+ * when the first dropped bit is a one. If any of the
+ * following digits in the number (which haven't been added
+ * in yet) are nonzero then the correct action would have
+ * been to round up instead of down. An example of this
+ * occurs when reading the number 0x1000000000000081, which
+ * rounds to 0x1000000000000000 instead of 0x1000000000000100.
+ */
+ int bitShiftInChar = 1;
+ int digit = 0;
+
+ final int SKIP_LEADING_ZEROS = 0;
+ final int FIRST_EXACT_53_BITS = 1;
+ final int AFTER_BIT_53 = 2;
+ final int ZEROS_AFTER_54 = 3;
+ final int MIXED_AFTER_54 = 4;
+
+ int state = SKIP_LEADING_ZEROS;
+ int exactBitsLimit = 53;
+ double factor = 0.0;
+ boolean bit53 = false;
+ // bit54 is the 54th bit (the first dropped from the mantissa)
+ boolean bit54 = false;
+
+ for (;;) {
+ if (bitShiftInChar == 1) {
+ if (start == end)
+ break;
+ digit = s.charAt(start++);
+ if ('0' <= digit && digit <= '9')
+ digit -= '0';
+ else if ('a' <= digit && digit <= 'z')
+ digit -= 'a' - 10;
+ else
+ digit -= 'A' - 10;
+ bitShiftInChar = radix;
+ }
+ bitShiftInChar >>= 1;
+ boolean bit = (digit & bitShiftInChar) != 0;
+
+ switch (state) {
+ case SKIP_LEADING_ZEROS:
+ if (bit) {
+ --exactBitsLimit;
+ sum = 1.0;
+ state = FIRST_EXACT_53_BITS;
+ }
+ break;
+ case FIRST_EXACT_53_BITS:
+ sum *= 2.0;
+ if (bit)
+ sum += 1.0;
+ --exactBitsLimit;
+ if (exactBitsLimit == 0) {
+ bit53 = bit;
+ state = AFTER_BIT_53;
+ }
+ break;
+ case AFTER_BIT_53:
+ bit54 = bit;
+ factor = 2.0;
+ state = ZEROS_AFTER_54;
+ break;
+ case ZEROS_AFTER_54:
+ if (bit) {
+ state = MIXED_AFTER_54;
+ }
+ // fallthrough
+ case MIXED_AFTER_54:
+ factor *= 2;
+ break;
+ }
+ }
+ switch (state) {
+ case SKIP_LEADING_ZEROS:
+ sum = 0.0;
+ break;
+ case FIRST_EXACT_53_BITS:
+ case AFTER_BIT_53:
+ // do nothing
+ break;
+ case ZEROS_AFTER_54:
+ // x1.1 -> x1 + 1 (round up)
+ // x0.1 -> x0 (round down)
+ if (bit54 & bit53)
+ sum += 1.0;
+ sum *= factor;
+ break;
+ case MIXED_AFTER_54:
+ // x.100...1.. -> x + 1 (round up)
+ // x.0anything -> x (round down)
+ if (bit54)
+ sum += 1.0;
+ sum *= factor;
+ break;
+ }
+ }
+ /* We don't worry about inaccurate numbers for any other base. */
+ }
+ return sum;
+ }
+
+
+ /**
+ * ToNumber applied to the String type
+ *
+ * See ECMA 9.3.1
+ */
+ public static double toNumber(String s) {
+ int len = s.length();
+ int start = 0;
+ char startChar;
+ for (;;) {
+ if (start == len) {
+ // Empty or contains only whitespace
+ return +0.0;
+ }
+ startChar = s.charAt(start);
+ if (!Character.isWhitespace(startChar))
+ break;
+ start++;
+ }
+
+ if (startChar == '0') {
+ if (start + 2 < len) {
+ int c1 = s.charAt(start + 1);
+ if (c1 == 'x' || c1 == 'X') {
+ // A hexadecimal number
+ return stringToNumber(s, start + 2, 16);
+ }
+ }
+ } else if (startChar == '+' || startChar == '-') {
+ if (start + 3 < len && s.charAt(start + 1) == '0') {
+ int c2 = s.charAt(start + 2);
+ if (c2 == 'x' || c2 == 'X') {
+ // A hexadecimal number with sign
+ double val = stringToNumber(s, start + 3, 16);
+ return startChar == '-' ? -val : val;
+ }
+ }
+ }
+
+ int end = len - 1;
+ char endChar;
+ while (Character.isWhitespace(endChar = s.charAt(end)))
+ end--;
+ if (endChar == 'y') {
+ // check for "Infinity"
+ if (startChar == '+' || startChar == '-')
+ start++;
+ if (start + 7 == end && s.regionMatches(start, "Infinity", 0, 8))
+ return startChar == '-'
+ ? Double.NEGATIVE_INFINITY
+ : Double.POSITIVE_INFINITY;
+ return NaN;
+ }
+ // A non-hexadecimal, non-infinity number:
+ // just try a normal floating point conversion
+ String sub = s.substring(start, end+1);
+ if (MSJVM_BUG_WORKAROUNDS) {
+ // The MS JVM will accept non-conformant strings
+ // rather than throwing a NumberFormatException
+ // as it should.
+ for (int i=sub.length()-1; i >= 0; i--) {
+ char c = sub.charAt(i);
+ if (('0' <= c && c <= '9') || c == '.' ||
+ c == 'e' || c == 'E' ||
+ c == '+' || c == '-')
+ continue;
+ return NaN;
+ }
+ }
+ try {
+ return Double.valueOf(sub).doubleValue();
+ } catch (NumberFormatException ex) {
+ return NaN;
+ }
+ }
+
+ /**
+ * Helper function for builtin objects that use the varargs form.
+ * ECMA function formal arguments are undefined if not supplied;
+ * this function pads the argument array out to the expected
+ * length, if necessary.
+ */
+ public static Object[] padArguments(Object[] args, int count) {
+ if (count < args.length)
+ return args;
+
+ int i;
+ Object[] result = new Object[count];
+ for (i = 0; i < args.length; i++) {
+ result[i] = args[i];
+ }
+
+ for (; i < count; i++) {
+ result[i] = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /* Work around Microsoft Java VM bugs. */
+ private final static boolean MSJVM_BUG_WORKAROUNDS = true;
+
+ public static String escapeString(String s)
+ {
+ return escapeString(s, '"');
+ }
+
+ /**
+ * For escaping strings printed by object and array literals; not quite
+ * the same as 'escape.'
+ */
+ public static String escapeString(String s, char escapeQuote)
+ {
+ if (!(escapeQuote == '"' || escapeQuote == '\'')) Kit.codeBug();
+ StringBuffer sb = null;
+
+ for(int i = 0, L = s.length(); i != L; ++i) {
+ int c = s.charAt(i);
+
+ if (' ' <= c && c <= '~' && c != escapeQuote && c != '\\') {
+ // an ordinary print character (like C isprint()) and not "
+ // or \ .
+ if (sb != null) {
+ sb.append((char)c);
+ }
+ continue;
+ }
+ if (sb == null) {
+ sb = new StringBuffer(L + 3);
+ sb.append(s);
+ sb.setLength(i);
+ }
+
+ int escape = -1;
+ switch (c) {
+ case '\b': escape = 'b'; break;
+ case '\f': escape = 'f'; break;
+ case '\n': escape = 'n'; break;
+ case '\r': escape = 'r'; break;
+ case '\t': escape = 't'; break;
+ case 0xb: escape = 'v'; break; // Java lacks \v.
+ case ' ': escape = ' '; break;
+ case '\\': escape = '\\'; break;
+ }
+ if (escape >= 0) {
+ // an \escaped sort of character
+ sb.append('\\');
+ sb.append((char)escape);
+ } else if (c == escapeQuote) {
+ sb.append('\\');
+ sb.append(escapeQuote);
+ } else {
+ int hexSize;
+ if (c < 256) {
+ // 2-digit hex
+ sb.append("\\x");
+ hexSize = 2;
+ } else {
+ // Unicode.
+ sb.append("\\u");
+ hexSize = 4;
+ }
+ // append hexadecimal form of c left-padded with 0
+ for (int shift = (hexSize - 1) * 4; shift >= 0; shift -= 4) {
+ int digit = 0xf & (c >> shift);
+ int hc = (digit < 10) ? '0' + digit : 'a' - 10 + digit;
+ sb.append((char)hc);
+ }
+ }
+ }
+ return (sb == null) ? s : sb.toString();
+ }
+
+ static boolean isValidIdentifierName(String s)
+ {
+ int L = s.length();
+ if (L == 0)
+ return false;
+ if (!Character.isJavaIdentifierStart(s.charAt(0)))
+ return false;
+ for (int i = 1; i != L; ++i) {
+ if (!Character.isJavaIdentifierPart(s.charAt(i)))
+ return false;
+ }
+ return !TokenStream.isKeyword(s);
+ }
+
+ /**
+ * Convert the value to a string.
+ *
+ * See ECMA 9.8.
+ */
+ public static String toString(Object val) {
+ for (;;) {
+ if (val == null) {
+ return "null";
+ }
+ if (val == Undefined.instance) {
+ return "undefined";
+ }
+ if (val instanceof String) {
+ return (String)val;
+ }
+ if (val instanceof Number) {
+ // XXX should we just teach NativeNumber.stringValue()
+ // about Numbers?
+ return numberToString(((Number)val).doubleValue(), 10);
+ }
+ if (val instanceof Scriptable) {
+ val = ((Scriptable) val).getDefaultValue(StringClass);
+ if (val instanceof Scriptable) {
+ throw errorWithClassName("msg.primitive.expected", val);
+ }
+ continue;
+ }
+ return val.toString();
+ }
+ }
+
+ static String defaultObjectToString(Scriptable obj)
+ {
+ return "[object " + obj.getClassName() + ']';
+ }
+
+ public static String toString(Object[] args, int index)
+ {
+ return (index < args.length) ? toString(args[index]) : "undefined";
+ }
+
+ /**
+ * Optimized version of toString(Object) for numbers.
+ */
+ public static String toString(double val) {
+ return numberToString(val, 10);
+ }
+
+ public static String numberToString(double d, int base) {
+ if (d != d)
+ return "NaN";
+ if (d == Double.POSITIVE_INFINITY)
+ return "Infinity";
+ if (d == Double.NEGATIVE_INFINITY)
+ return "-Infinity";
+ if (d == 0.0)
+ return "0";
+
+ if ((base < 2) || (base > 36)) {
+ throw Context.reportRuntimeError1(
+ "msg.bad.radix", Integer.toString(base));
+ }
+
+ if (base != 10) {
+ return DToA.JS_dtobasestr(base, d);
+ } else {
+ StringBuffer result = new StringBuffer();
+ DToA.JS_dtostr(result, DToA.DTOSTR_STANDARD, 0, d);
+ return result.toString();
+ }
+
+ }
+
+ static String uneval(Context cx, Scriptable scope, Object value)
+ {
+ if (value == null) {
+ return "null";
+ }
+ if (value == Undefined.instance) {
+ return "undefined";
+ }
+ if (value instanceof String) {
+ String escaped = escapeString((String)value);
+ StringBuffer sb = new StringBuffer(escaped.length() + 2);
+ sb.append('\"');
+ sb.append(escaped);
+ sb.append('\"');
+ return sb.toString();
+ }
+ if (value instanceof Number) {
+ double d = ((Number)value).doubleValue();
+ if (d == 0 && 1 / d < 0) {
+ return "-0";
+ }
+ return toString(d);
+ }
+ if (value instanceof Boolean) {
+ return toString(value);
+ }
+ if (value instanceof Scriptable) {
+ Scriptable obj = (Scriptable)value;
+ // Wrapped Java objects won't have "toSource" and will report
+ // errors for get()s of nonexistent name, so use has() first
+ if (ScriptableObject.hasProperty(obj, "toSource")) {
+ Object v = ScriptableObject.getProperty(obj, "toSource");
+ if (v instanceof Function) {
+ Function f = (Function)v;
+ return toString(f.call(cx, scope, obj, emptyArgs));
+ }
+ }
+ return toString(value);
+ }
+ warnAboutNonJSObject(value);
+ return value.toString();
+ }
+
+ static String defaultObjectToSource(Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ boolean toplevel, iterating;
+ if (cx.iterating == null) {
+ toplevel = true;
+ iterating = false;
+ cx.iterating = new ObjToIntMap(31);
+ } else {
+ toplevel = false;
+ iterating = cx.iterating.has(thisObj);
+ }
+
+ StringBuffer result = new StringBuffer(128);
+ if (toplevel) {
+ result.append("(");
+ }
+ result.append('{');
+
+ // Make sure cx.iterating is set to null when done
+ // so we don't leak memory
+ try {
+ if (!iterating) {
+ cx.iterating.intern(thisObj); // stop recursion.
+ Object[] ids = thisObj.getIds();
+ for (int i=0; i < ids.length; i++) {
+ Object id = ids[i];
+ Object value;
+ if (id instanceof Integer) {
+ int intId = ((Integer)id).intValue();
+ value = thisObj.get(intId, thisObj);
+ if (value == Scriptable.NOT_FOUND)
+ continue; // a property has been removed
+ if (i > 0)
+ result.append(", ");
+ result.append(intId);
+ } else {
+ String strId = (String)id;
+ value = thisObj.get(strId, thisObj);
+ if (value == Scriptable.NOT_FOUND)
+ continue; // a property has been removed
+ if (i > 0)
+ result.append(", ");
+ if (ScriptRuntime.isValidIdentifierName(strId)) {
+ result.append(strId);
+ } else {
+ result.append('\'');
+ result.append(
+ ScriptRuntime.escapeString(strId, '\''));
+ result.append('\'');
+ }
+ }
+ result.append(':');
+ result.append(ScriptRuntime.uneval(cx, scope, value));
+ }
+ }
+ } finally {
+ if (toplevel) {
+ cx.iterating = null;
+ }
+ }
+
+ result.append('}');
+ if (toplevel) {
+ result.append(')');
+ }
+ return result.toString();
+ }
+
+ public static Scriptable toObject(Scriptable scope, Object val)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable)val;
+ }
+ return toObject(Context.getContext(), scope, val);
+ }
+
+ public static Scriptable toObjectOrNull(Context cx, Object obj)
+ {
+ if (obj instanceof Scriptable) {
+ return (Scriptable)obj;
+ } else if (obj != null && obj != Undefined.instance) {
+ return toObject(cx, getTopCallScope(cx), obj);
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use {@link #toObject(Scriptable, Object)} instead.
+ */
+ public static Scriptable toObject(Scriptable scope, Object val,
+ Class staticClass)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable)val;
+ }
+ return toObject(Context.getContext(), scope, val);
+ }
+
+ /**
+ * Convert the value to an object.
+ *
+ * See ECMA 9.9.
+ */
+ public static Scriptable toObject(Context cx, Scriptable scope, Object val)
+ {
+ if (val instanceof Scriptable) {
+ return (Scriptable) val;
+ }
+ if (val == null) {
+ throw typeError0("msg.null.to.object");
+ }
+ if (val == Undefined.instance) {
+ throw typeError0("msg.undef.to.object");
+ }
+ String className = val instanceof String ? "String" :
+ val instanceof Number ? "Number" :
+ val instanceof Boolean ? "Boolean" :
+ null;
+ if (className != null) {
+ Object[] args = { val };
+ scope = ScriptableObject.getTopLevelScope(scope);
+ return newObject(cx, scope, className, args);
+ }
+
+ // Extension: Wrap as a LiveConnect object.
+ Object wrapped = cx.getWrapFactory().wrap(cx, scope, val, null);
+ if (wrapped instanceof Scriptable)
+ return (Scriptable) wrapped;
+ throw errorWithClassName("msg.invalid.type", val);
+ }
+
+ /**
+ * @deprecated Use {@link #toObject(Context, Scriptable, Object)} instead.
+ */
+ public static Scriptable toObject(Context cx, Scriptable scope, Object val,
+ Class staticClass)
+ {
+ return toObject(cx, scope, val);
+ }
+
+ /**
+ * @deprecated The method is only present for compatibility.
+ */
+ public static Object call(Context cx, Object fun, Object thisArg,
+ Object[] args, Scriptable scope)
+ {
+ if (!(fun instanceof Function)) {
+ throw notFunctionError(toString(fun));
+ }
+ Function function = (Function)fun;
+ Scriptable thisObj = toObjectOrNull(cx, thisArg);
+ if (thisObj == null) {
+ throw undefCallError(thisObj, "function");
+ }
+ return function.call(cx, scope, thisObj, args);
+ }
+
+ public static Scriptable newObject(Context cx, Scriptable scope,
+ String constructorName, Object[] args)
+ {
+ scope = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ Function ctor = getExistingCtor(cx, scope, constructorName);
+ if (args == null) { args = ScriptRuntime.emptyArgs; }
+ return ctor.construct(cx, scope, args);
+ }
+
+ /**
+ *
+ * See ECMA 9.4.
+ */
+ public static double toInteger(Object val) {
+ return toInteger(toNumber(val));
+ }
+
+ // convenience method
+ public static double toInteger(double d) {
+ // if it's NaN
+ if (d != d)
+ return +0.0;
+
+ if (d == 0.0 ||
+ d == Double.POSITIVE_INFINITY ||
+ d == Double.NEGATIVE_INFINITY)
+ return d;
+
+ if (d > 0.0)
+ return Math.floor(d);
+ else
+ return Math.ceil(d);
+ }
+
+ public static double toInteger(Object[] args, int index) {
+ return (index < args.length) ? toInteger(args[index]) : +0.0;
+ }
+
+ /**
+ *
+ * See ECMA 9.5.
+ */
+ public static int toInt32(Object val)
+ {
+ // short circuit for common integer values
+ if (val instanceof Integer)
+ return ((Integer)val).intValue();
+
+ return toInt32(toNumber(val));
+ }
+
+ public static int toInt32(Object[] args, int index) {
+ return (index < args.length) ? toInt32(args[index]) : 0;
+ }
+
+ public static int toInt32(double d) {
+ int id = (int)d;
+ if (id == d) {
+ // This covers -0.0 as well
+ return id;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ double two32 = 4294967296.0;
+ d = Math.IEEEremainder(d, two32);
+ // (double)(long)d == d should hold here
+
+ long l = (long)d;
+ // returning (int)d does not work as d can be outside int range
+ // but the result must always be 32 lower bits of l
+ return (int)l;
+ }
+
+ /**
+ * See ECMA 9.6.
+ * @return long value representing 32 bits unsigned integer
+ */
+ public static long toUint32(double d) {
+ long l = (long)d;
+ if (l == d) {
+ // This covers -0.0 as well
+ return l & 0xffffffffL;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ // 0x100000000 gives me a numeric overflow...
+ double two32 = 4294967296.0;
+ l = (long)Math.IEEEremainder(d, two32);
+
+ return l & 0xffffffffL;
+ }
+
+ public static long toUint32(Object val) {
+ return toUint32(toNumber(val));
+ }
+
+ /**
+ *
+ * See ECMA 9.7.
+ */
+ public static char toUint16(Object val) {
+ double d = toNumber(val);
+
+ int i = (int)d;
+ if (i == d) {
+ return (char)i;
+ }
+
+ if (d != d
+ || d == Double.POSITIVE_INFINITY
+ || d == Double.NEGATIVE_INFINITY)
+ {
+ return 0;
+ }
+
+ d = (d >= 0) ? Math.floor(d) : Math.ceil(d);
+
+ int int16 = 0x10000;
+ i = (int)Math.IEEEremainder(d, int16);
+
+ return (char)i;
+ }
+
+ // XXX: this is until setDefaultNamespace will learn how to store NS
+ // properly and separates namespace form Scriptable.get etc.
+ private static final String DEFAULT_NS_TAG = "__default_namespace__";
+
+ public static Object setDefaultNamespace(Object namespace, Context cx)
+ {
+ Scriptable scope = cx.currentActivationCall;
+ if (scope == null) {
+ scope = getTopCallScope(cx);
+ }
+
+ XMLLib xmlLib = currentXMLLib(cx);
+ Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);
+
+ // XXX : this should be in separated namesapce from Scriptable.get/put
+ if (!scope.has(DEFAULT_NS_TAG, scope)) {
+ // XXX: this is racy of cause
+ ScriptableObject.defineProperty(scope, DEFAULT_NS_TAG, ns,
+ ScriptableObject.PERMANENT
+ | ScriptableObject.DONTENUM);
+ } else {
+ scope.put(DEFAULT_NS_TAG, scope, ns);
+ }
+
+ return Undefined.instance;
+ }
+
+ public static Object searchDefaultNamespace(Context cx)
+ {
+ Scriptable scope = cx.currentActivationCall;
+ if (scope == null) {
+ scope = getTopCallScope(cx);
+ }
+ Object nsObject;
+ for (;;) {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ nsObject = ScriptableObject.getProperty(scope, DEFAULT_NS_TAG);
+ if (nsObject == Scriptable.NOT_FOUND) {
+ return null;
+ }
+ break;
+ }
+ nsObject = scope.get(DEFAULT_NS_TAG, scope);
+ if (nsObject != Scriptable.NOT_FOUND) {
+ break;
+ }
+ scope = parent;
+ }
+ return nsObject;
+ }
+
+ public static Object getTopLevelProp(Scriptable scope, String id) {
+ scope = ScriptableObject.getTopLevelScope(scope);
+ return ScriptableObject.getProperty(scope, id);
+ }
+
+ static Function getExistingCtor(Context cx, Scriptable scope,
+ String constructorName)
+ {
+ Object ctorVal = ScriptableObject.getProperty(scope, constructorName);
+ if (ctorVal instanceof Function) {
+ return (Function)ctorVal;
+ }
+ if (ctorVal == Scriptable.NOT_FOUND) {
+ throw Context.reportRuntimeError1(
+ "msg.ctor.not.found", constructorName);
+ } else {
+ throw Context.reportRuntimeError1(
+ "msg.not.ctor", constructorName);
+ }
+ }
+
+ /**
+ * Return -1L if str is not an index or the index value as lower 32
+ * bits of the result.
+ */
+ private static long indexFromString(String str)
+ {
+ // The length of the decimal string representation of
+ // Integer.MAX_VALUE, 2147483647
+ final int MAX_VALUE_LENGTH = 10;
+
+ int len = str.length();
+ if (len > 0) {
+ int i = 0;
+ boolean negate = false;
+ int c = str.charAt(0);
+ if (c == '-') {
+ if (len > 1) {
+ c = str.charAt(1);
+ i = 1;
+ negate = true;
+ }
+ }
+ c -= '0';
+ if (0 <= c && c <= 9
+ && len <= (negate ? MAX_VALUE_LENGTH + 1 : MAX_VALUE_LENGTH))
+ {
+ // Use negative numbers to accumulate index to handle
+ // Integer.MIN_VALUE that is greater by 1 in absolute value
+ // then Integer.MAX_VALUE
+ int index = -c;
+ int oldIndex = 0;
+ i++;
+ if (index != 0) {
+ // Note that 00, 01, 000 etc. are not indexes
+ while (i != len && 0 <= (c = str.charAt(i) - '0') && c <= 9)
+ {
+ oldIndex = index;
+ index = 10 * index - c;
+ i++;
+ }
+ }
+ // Make sure all characters were consumed and that it couldn't
+ // have overflowed.
+ if (i == len &&
+ (oldIndex > (Integer.MIN_VALUE / 10) ||
+ (oldIndex == (Integer.MIN_VALUE / 10) &&
+ c <= (negate ? -(Integer.MIN_VALUE % 10)
+ : (Integer.MAX_VALUE % 10)))))
+ {
+ return 0xFFFFFFFFL & (negate ? index : -index);
+ }
+ }
+ }
+ return -1L;
+ }
+
+ /**
+ * If str is a decimal presentation of Uint32 value, return it as long.
+ * Othewise return -1L;
+ */
+ public static long testUint32String(String str)
+ {
+ // The length of the decimal string representation of
+ // UINT32_MAX_VALUE, 4294967296
+ final int MAX_VALUE_LENGTH = 10;
+
+ int len = str.length();
+ if (1 <= len && len <= MAX_VALUE_LENGTH) {
+ int c = str.charAt(0);
+ c -= '0';
+ if (c == 0) {
+ // Note that 00,01 etc. are not valid Uint32 presentations
+ return (len == 1) ? 0L : -1L;
+ }
+ if (1 <= c && c <= 9) {
+ long v = c;
+ for (int i = 1; i != len; ++i) {
+ c = str.charAt(i) - '0';
+ if (!(0 <= c && c <= 9)) {
+ return -1;
+ }
+ v = 10 * v + c;
+ }
+ // Check for overflow
+ if ((v >>> 32) == 0) {
+ return v;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * If s represents index, then return index value wrapped as Integer
+ * and othewise return s.
+ */
+ static Object getIndexObject(String s)
+ {
+ long indexTest = indexFromString(s);
+ if (indexTest >= 0) {
+ return new Integer((int)indexTest);
+ }
+ return s;
+ }
+
+ /**
+ * If d is exact int value, return its value wrapped as Integer
+ * and othewise return d converted to String.
+ */
+ static Object getIndexObject(double d)
+ {
+ int i = (int)d;
+ if (i == d) {
+ return new Integer(i);
+ }
+ return toString(d);
+ }
+
+ /**
+ * If toString(id) is a decimal presentation of int32 value, then id
+ * is index. In this case return null and make the index available
+ * as ScriptRuntime.lastIndexResult(cx). Otherwise return toString(id).
+ */
+ static String toStringIdOrIndex(Context cx, Object id)
+ {
+ if (id instanceof Number) {
+ double d = ((Number)id).doubleValue();
+ int index = (int)d;
+ if (index == d) {
+ storeIndexResult(cx, index);
+ return null;
+ }
+ return toString(id);
+ } else {
+ String s;
+ if (id instanceof String) {
+ s = (String)id;
+ } else {
+ s = toString(id);
+ }
+ long indexTest = indexFromString(s);
+ if (indexTest >= 0) {
+ storeIndexResult(cx, (int)indexTest);
+ return null;
+ }
+ return s;
+ }
+ }
+
+ /**
+ * Call obj.[[Get]](id)
+ */
+ public static Object getObjectElem(Object obj, Object elem, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, elem);
+ }
+ return getObjectElem(sobj, elem, cx);
+ }
+
+ public static Object getObjectElem(Scriptable obj, Object elem,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, elem);
+ }
+
+ Object result;
+
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.getProperty(obj, index);
+ } else {
+ result = ScriptableObject.getProperty(obj, s);
+ }
+
+ if (result == Scriptable.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /**
+ * Version of getObjectElem when elem is a valid JS identifier name.
+ */
+ public static Object getObjectProp(Object obj, String property,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, property);
+ }
+ return getObjectProp(sobj, property, cx);
+ }
+
+ public static Object getObjectProp(Scriptable obj, String property,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ // TODO: Change XMLObject to just use Scriptable interface
+ // to avoid paying cost of instanceof check on *every property
+ // lookup* !
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, property);
+ }
+
+ Object result = ScriptableObject.getProperty(obj, property);
+ if (result == Scriptable.NOT_FOUND) {
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) {
+ Context.reportWarning(ScriptRuntime.getMessage1(
+ "msg.ref.undefined.prop", property));
+ }
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ public static Object getObjectPropNoWarn(Object obj, String property,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, property);
+ }
+ if (obj instanceof XMLObject) {
+ // TODO: fix as mentioned in note in method above
+ getObjectProp(sobj, property, cx);
+ }
+ Object result = ScriptableObject.getProperty(sobj, property);
+ if (result == Scriptable.NOT_FOUND) {
+ return Undefined.instance;
+ }
+ return result;
+ }
+
+ /*
+ * A cheaper and less general version of the above for well-known argument
+ * types.
+ */
+ public static Object getObjectIndex(Object obj, double dblIndex,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefReadError(obj, toString(dblIndex));
+ }
+
+ int index = (int)dblIndex;
+ if (index == dblIndex) {
+ return getObjectIndex(sobj, index, cx);
+ } else {
+ String s = toString(dblIndex);
+ return getObjectProp(sobj, s, cx);
+ }
+ }
+
+ public static Object getObjectIndex(Scriptable obj, int index,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.ecmaGet(cx, new Integer(index));
+ }
+
+ Object result = ScriptableObject.getProperty(obj, index);
+ if (result == Scriptable.NOT_FOUND) {
+ result = Undefined.instance;
+ }
+
+ return result;
+ }
+
+ /*
+ * Call obj.[[Put]](id, value)
+ */
+ public static Object setObjectElem(Object obj, Object elem, Object value,
+ Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, elem, value);
+ }
+ return setObjectElem(sobj, elem, value, cx);
+ }
+
+ public static Object setObjectElem(Scriptable obj, Object elem,
+ Object value, Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, elem, value);
+ return value;
+ }
+
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ ScriptableObject.putProperty(obj, index, value);
+ } else {
+ ScriptableObject.putProperty(obj, s, value);
+ }
+
+ return value;
+ }
+
+ /**
+ * Version of setObjectElem when elem is a valid JS identifier name.
+ */
+ public static Object setObjectProp(Object obj, String property,
+ Object value, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, property, value);
+ }
+ return setObjectProp(sobj, property, value, cx);
+ }
+
+ public static Object setObjectProp(Scriptable obj, String property,
+ Object value, Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, property, value);
+ } else {
+ ScriptableObject.putProperty(obj, property, value);
+ }
+ return value;
+ }
+
+ /*
+ * A cheaper and less general version of the above for well-known argument
+ * types.
+ */
+ public static Object setObjectIndex(Object obj, double dblIndex,
+ Object value, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw undefWriteError(obj, String.valueOf(dblIndex), value);
+ }
+
+ int index = (int)dblIndex;
+ if (index == dblIndex) {
+ return setObjectIndex(sobj, index, value, cx);
+ } else {
+ String s = toString(dblIndex);
+ return setObjectProp(sobj, s, value, cx);
+ }
+ }
+
+ public static Object setObjectIndex(Scriptable obj, int index, Object value,
+ Context cx)
+ {
+ if (obj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)obj;
+ xmlObject.ecmaPut(cx, new Integer(index), value);
+ } else {
+ ScriptableObject.putProperty(obj, index, value);
+ }
+ return value;
+ }
+
+ public static boolean deleteObjectElem(Scriptable target, Object elem,
+ Context cx)
+ {
+ boolean result;
+ if (target instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)target;
+ result = xmlObject.ecmaDelete(cx, elem);
+ } else {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.deleteProperty(target, index);
+ } else {
+ result = ScriptableObject.deleteProperty(target, s);
+ }
+ }
+ return result;
+ }
+
+ public static boolean hasObjectElem(Scriptable target, Object elem,
+ Context cx)
+ {
+ boolean result;
+
+ if (target instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)target;
+ result = xmlObject.ecmaHas(cx, elem);
+ } else {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = ScriptableObject.hasProperty(target, index);
+ } else {
+ result = ScriptableObject.hasProperty(target, s);
+ }
+ }
+
+ return result;
+ }
+
+ public static Object refGet(Ref ref, Context cx)
+ {
+ return ref.get(cx);
+ }
+
+ public static Object refSet(Ref ref, Object value, Context cx)
+ {
+ return ref.set(cx, value);
+ }
+
+ public static Object refDel(Ref ref, Context cx)
+ {
+ return wrapBoolean(ref.delete(cx));
+ }
+
+ static boolean isSpecialProperty(String s)
+ {
+ return s.equals("__proto__") || s.equals("__parent__");
+ }
+
+ public static Ref specialRef(Object obj, String specialProperty,
+ Context cx)
+ {
+ return SpecialRef.createSpecial(cx, obj, specialProperty);
+ }
+
+ /**
+ * The delete operator
+ *
+ * See ECMA 11.4.1
+ *
+ * In ECMA 0.19, the description of the delete operator (11.4.1)
+ * assumes that the [[Delete]] method returns a value. However,
+ * the definition of the [[Delete]] operator (8.6.2.5) does not
+ * define a return value. Here we assume that the [[Delete]]
+ * method doesn't return a value.
+ */
+ public static Object delete(Object obj, Object id, Context cx)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ String idStr = (id == null) ? "null" : id.toString();
+ throw typeError2("msg.undef.prop.delete", toString(obj), idStr);
+ }
+ boolean result = deleteObjectElem(sobj, id, cx);
+ return wrapBoolean(result);
+ }
+
+ /**
+ * Looks up a name in the scope chain and returns its value.
+ */
+ public static Object name(Context cx, Scriptable scope, String name)
+ {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ Object result = topScopeName(cx, scope, name);
+ if (result == Scriptable.NOT_FOUND) {
+ throw notFoundError(scope, name);
+ }
+ return result;
+ }
+
+ return nameOrFunction(cx, scope, parent, name, false);
+ }
+
+ private static Object nameOrFunction(Context cx, Scriptable scope,
+ Scriptable parentScope, String name,
+ boolean asFunctionCall)
+ {
+ Object result;
+ Scriptable thisObj = scope; // It is used only if asFunctionCall==true.
+
+ XMLObject firstXMLObject = null;
+ for (;;) {
+ if (scope instanceof NativeWith) {
+ Scriptable withObj = scope.getPrototype();
+ if (withObj instanceof XMLObject) {
+ XMLObject xmlObj = (XMLObject)withObj;
+ if (xmlObj.ecmaHas(cx, name)) {
+ // function this should be the target object of with
+ thisObj = xmlObj;
+ result = xmlObj.ecmaGet(cx, name);
+ break;
+ }
+ if (firstXMLObject == null) {
+ firstXMLObject = xmlObj;
+ }
+ } else {
+ result = ScriptableObject.getProperty(withObj, name);
+ if (result != Scriptable.NOT_FOUND) {
+ // function this should be the target object of with
+ thisObj = withObj;
+ break;
+ }
+ }
+ } else if (scope instanceof NativeCall) {
+ // NativeCall does not prototype chain and Scriptable.get
+ // can be called directly.
+ result = scope.get(name, scope);
+ if (result != Scriptable.NOT_FOUND) {
+ if (asFunctionCall) {
+ // ECMA 262 requires that this for nested funtions
+ // should be top scope
+ thisObj = ScriptableObject.
+ getTopLevelScope(parentScope);
+ }
+ break;
+ }
+ } else {
+ // Can happen if Rhino embedding decided that nested
+ // scopes are useful for what ever reasons.
+ result = ScriptableObject.getProperty(scope, name);
+ if (result != Scriptable.NOT_FOUND) {
+ thisObj = scope;
+ break;
+ }
+ }
+ scope = parentScope;
+ parentScope = parentScope.getParentScope();
+ if (parentScope == null) {
+ result = topScopeName(cx, scope, name);
+ if (result == Scriptable.NOT_FOUND) {
+ if (firstXMLObject == null || asFunctionCall) {
+ throw notFoundError(scope, name);
+ }
+ // The name was not found, but we did find an XML
+ // object in the scope chain and we are looking for name,
+ // not function. The result should be an empty XMLList
+ // in name context.
+ result = firstXMLObject.ecmaGet(cx, name);
+ }
+ // For top scope thisObj for functions is always scope itself.
+ thisObj = scope;
+ break;
+ }
+ }
+
+ if (asFunctionCall) {
+ if (!(result instanceof Callable)) {
+ throw notFunctionError(result, name);
+ }
+ storeScriptable(cx, thisObj);
+ }
+
+ return result;
+ }
+
+ private static Object topScopeName(Context cx, Scriptable scope,
+ String name)
+ {
+ if (cx.useDynamicScope) {
+ scope = checkDynamicScope(cx.topCallScope, scope);
+ }
+ return ScriptableObject.getProperty(scope, name);
+ }
+
+
+ /**
+ * Returns the object in the scope chain that has a given property.
+ *
+ * The order of evaluation of an assignment expression involves
+ * evaluating the lhs to a reference, evaluating the rhs, and then
+ * modifying the reference with the rhs value. This method is used
+ * to 'bind' the given name to an object containing that property
+ * so that the side effects of evaluating the rhs do not affect
+ * which property is modified.
+ * Typically used in conjunction with setName.
+ *
+ * See ECMA 10.1.4
+ */
+ public static Scriptable bind(Context cx, Scriptable scope, String id)
+ {
+ Scriptable firstXMLObject = null;
+ Scriptable parent = scope.getParentScope();
+ childScopesChecks: if (parent != null) {
+ // Check for possibly nested "with" scopes first
+ while (scope instanceof NativeWith) {
+ Scriptable withObj = scope.getPrototype();
+ if (withObj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)withObj;
+ if (xmlObject.ecmaHas(cx, id)) {
+ return xmlObject;
+ }
+ if (firstXMLObject == null) {
+ firstXMLObject = xmlObject;
+ }
+ } else {
+ if (ScriptableObject.hasProperty(withObj, id)) {
+ return withObj;
+ }
+ }
+ scope = parent;
+ parent = parent.getParentScope();
+ if (parent == null) {
+ break childScopesChecks;
+ }
+ }
+ for (;;) {
+ if (ScriptableObject.hasProperty(scope, id)) {
+ return scope;
+ }
+ scope = parent;
+ parent = parent.getParentScope();
+ if (parent == null) {
+ break childScopesChecks;
+ }
+ }
+ }
+ // scope here is top scope
+ if (cx.useDynamicScope) {
+ scope = checkDynamicScope(cx.topCallScope, scope);
+ }
+ if (ScriptableObject.hasProperty(scope, id)) {
+ return scope;
+ }
+ // Nothing was found, but since XML objects always bind
+ // return one if found
+ return firstXMLObject;
+ }
+
+ public static Object setName(Scriptable bound, Object value,
+ Context cx, Scriptable scope, String id)
+ {
+ if (bound != null) {
+ if (bound instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)bound;
+ xmlObject.ecmaPut(cx, id, value);
+ } else {
+ ScriptableObject.putProperty(bound, id, value);
+ }
+ } else {
+ // "newname = 7;", where 'newname' has not yet
+ // been defined, creates a new property in the
+ // top scope unless strict mode is specified.
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
+ cx.hasFeature(Context.FEATURE_STRICT_VARS))
+ {
+ Context.reportWarning(
+ ScriptRuntime.getMessage1("msg.assn.create.strict", id));
+ }
+ // Find the top scope by walking up the scope chain.
+ bound = ScriptableObject.getTopLevelScope(scope);
+ if (cx.useDynamicScope) {
+ bound = checkDynamicScope(cx.topCallScope, bound);
+ }
+ bound.put(id, bound, value);
+ }
+ return value;
+ }
+
+ public static Object setConst(Scriptable bound, Object value,
+ Context cx, String id)
+ {
+ if (bound instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)bound;
+ xmlObject.ecmaPut(cx, id, value);
+ } else {
+ ScriptableObject.putConstProperty(bound, id, value);
+ }
+ return value;
+ }
+
+ /**
+ * This is the enumeration needed by the for..in statement.
+ *
+ * See ECMA 12.6.3.
+ *
+ * IdEnumeration maintains a ObjToIntMap to make sure a given
+ * id is enumerated only once across multiple objects in a
+ * prototype chain.
+ *
+ * XXX - ECMA delete doesn't hide properties in the prototype,
+ * but js/ref does. This means that the js/ref for..in can
+ * avoid maintaining a hash table and instead perform lookups
+ * to see if a given property has already been enumerated.
+ *
+ */
+ private static class IdEnumeration implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ Scriptable obj;
+ Object[] ids;
+ int index;
+ ObjToIntMap used;
+ Object currentId;
+ int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES,
+ ENUM_INIT_ARRAY */
+
+ // if true, integer ids will be returned as numbers rather than strings
+ boolean enumNumbers;
+
+ Scriptable iterator;
+ }
+
+ public static Scriptable toIterator(Context cx, Scriptable scope,
+ Scriptable obj, boolean keyOnly)
+ {
+ /*APPJET 1.6*//*
+ if (ScriptableObject.hasProperty(obj,
+ NativeIterator.ITERATOR_PROPERTY_NAME))
+ {
+ Object v = ScriptableObject.getProperty(obj,
+ NativeIterator.ITERATOR_PROPERTY_NAME);
+ if (!(v instanceof Callable)) {
+ throw typeError0("msg.invalid.iterator");
+ }
+ Callable f = (Callable) v;
+ Object[] args = new Object[] { keyOnly ? Boolean.TRUE
+ : Boolean.FALSE };
+ v = f.call(cx, scope, obj, args);
+ if (!(v instanceof Scriptable)) {
+ throw typeError0("msg.iterator.primitive");
+ }
+ return (Scriptable) v;
+ }*/
+ return null;
+ }
+
+ // for backwards compatibility with generated class files
+ public static Object enumInit(Object value, Context cx, boolean enumValues)
+ {
+ return enumInit(value, cx, enumValues ? ENUMERATE_VALUES
+ : ENUMERATE_KEYS);
+ }
+
+ public static final int ENUMERATE_KEYS = 0;
+ public static final int ENUMERATE_VALUES = 1;
+ public static final int ENUMERATE_ARRAY = 2;
+ public static final int ENUMERATE_KEYS_NO_ITERATOR = 3;
+ public static final int ENUMERATE_VALUES_NO_ITERATOR = 4;
+ public static final int ENUMERATE_ARRAY_NO_ITERATOR = 5;
+
+ public static Object enumInit(Object value, Context cx, int enumType)
+ {
+ IdEnumeration x = new IdEnumeration();
+ x.obj = toObjectOrNull(cx, value);
+ if (x.obj == null) {
+ // null or undefined do not cause errors but rather lead to empty
+ // "for in" loop
+ return x;
+ }
+ x.enumType = enumType;
+ x.iterator = null;
+ if (enumType != ENUMERATE_KEYS_NO_ITERATOR &&
+ enumType != ENUMERATE_VALUES_NO_ITERATOR &&
+ enumType != ENUMERATE_ARRAY_NO_ITERATOR)
+ {
+ x.iterator = toIterator(cx, x.obj.getParentScope(), x.obj, true);
+ }
+ if (x.iterator == null) {
+ // enumInit should read all initial ids before returning
+ // or "for (a.i in a)" would wrongly enumerate i in a as well
+ enumChangeObject(x);
+ }
+
+ return x;
+ }
+
+ public static void setEnumNumbers(Object enumObj, boolean enumNumbers) {
+ ((IdEnumeration)enumObj).enumNumbers = enumNumbers;
+ }
+
+ public static Boolean enumNext(Object enumObj)
+ {
+ IdEnumeration x = (IdEnumeration)enumObj;
+ if (x.iterator != null) {
+ Object v = ScriptableObject.getProperty(x.iterator, "next");
+ if (!(v instanceof Callable))
+ return Boolean.FALSE;
+ Callable f = (Callable) v;
+ Context cx = Context.enter();
+ try {
+ x.currentId = f.call(cx, x.iterator.getParentScope(),
+ x.iterator, emptyArgs);
+ return Boolean.TRUE;
+ } catch (JavaScriptException e) {
+ if (e.getValue() instanceof NativeIterator.StopIteration) {
+ return Boolean.FALSE;
+ }
+ throw e;
+ } finally {
+ Context.exit();
+ }
+ }
+ for (;;) {
+ if (x.obj == null) {
+ return Boolean.FALSE;
+ }
+ if (x.index == x.ids.length) {
+ x.obj = x.obj.getPrototype();
+ enumChangeObject(x);
+ continue;
+ }
+ Object id = x.ids[x.index++];
+ if (x.used != null && x.used.has(id)) {
+ continue;
+ }
+ if (id instanceof String) {
+ String strId = (String)id;
+ if (!x.obj.has(strId, x.obj))
+ continue; // must have been deleted
+ x.currentId = strId;
+ } else {
+ int intId = ((Number)id).intValue();
+ if (!x.obj.has(intId, x.obj))
+ continue; // must have been deleted
+ x.currentId = x.enumNumbers ? (Object) (new Integer(intId))
+ : String.valueOf(intId);
+ }
+ return Boolean.TRUE;
+ }
+ }
+
+ public static Object enumId(Object enumObj, Context cx)
+ {
+ IdEnumeration x = (IdEnumeration)enumObj;
+ if (x.iterator != null) {
+ return x.currentId;
+ }
+ switch (x.enumType) {
+ case ENUMERATE_KEYS:
+ case ENUMERATE_KEYS_NO_ITERATOR:
+ return x.currentId;
+ case ENUMERATE_VALUES:
+ case ENUMERATE_VALUES_NO_ITERATOR:
+ return enumValue(enumObj, cx);
+ case ENUMERATE_ARRAY:
+ case ENUMERATE_ARRAY_NO_ITERATOR:
+ Object[] elements = { x.currentId, enumValue(enumObj, cx) };
+ return cx.newArray(x.obj.getParentScope(), elements);
+ default:
+ throw Kit.codeBug();
+ }
+ }
+
+ public static Object enumValue(Object enumObj, Context cx) {
+ IdEnumeration x = (IdEnumeration)enumObj;
+
+ Object result;
+
+ String s = toStringIdOrIndex(cx, x.currentId);
+ if (s == null) {
+ int index = lastIndexResult(cx);
+ result = x.obj.get(index, x.obj);
+ } else {
+ result = x.obj.get(s, x.obj);
+ }
+
+ return result;
+ }
+
+ private static void enumChangeObject(IdEnumeration x)
+ {
+ Object[] ids = null;
+ while (x.obj != null) {
+ ids = x.obj.getIds();
+ if (ids.length != 0) {
+ break;
+ }
+ x.obj = x.obj.getPrototype();
+ }
+ if (x.obj != null && x.ids != null) {
+ Object[] previous = x.ids;
+ int L = previous.length;
+ if (x.used == null) {
+ x.used = new ObjToIntMap(L);
+ }
+ for (int i = 0; i != L; ++i) {
+ x.used.intern(previous[i]);
+ }
+ }
+ x.ids = ids;
+ x.index = 0;
+ }
+
+ /**
+ * Prepare for calling name(...): return function corresponding to
+ * name and make current top scope available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getNameFunctionAndThis(String name,
+ Context cx,
+ Scriptable scope)
+ {
+ Scriptable parent = scope.getParentScope();
+ if (parent == null) {
+ Object result = topScopeName(cx, scope, name);
+ if (!(result instanceof Callable)) {
+ if (result == Scriptable.NOT_FOUND) {
+ throw notFoundError(scope, name);
+ } else {
+ throw notFunctionError(result, name);
+ }
+ }
+ // Top scope is not NativeWith or NativeCall => thisObj == scope
+ Scriptable thisObj = scope;
+ storeScriptable(cx, thisObj);
+ return (Callable)result;
+ }
+
+ // name will call storeScriptable(cx, thisObj);
+ return (Callable)nameOrFunction(cx, scope, parent, name, true);
+ }
+
+ /**
+ * Prepare for calling obj[id](...): return function corresponding to
+ * obj[id] and make obj properly converted to Scriptable available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getElemFunctionAndThis(Object obj,
+ Object elem,
+ Context cx)
+ {
+ String s = toStringIdOrIndex(cx, elem);
+ if (s != null) {
+ return getPropFunctionAndThis(obj, s, cx);
+ }
+ int index = lastIndexResult(cx);
+
+ Scriptable thisObj = toObjectOrNull(cx, obj);
+ if (thisObj == null) {
+ throw undefCallError(obj, String.valueOf(index));
+ }
+
+ Object value;
+ for (;;) {
+ // Ignore XML lookup as requred by ECMA 357, 11.2.2.1
+ value = ScriptableObject.getProperty(thisObj, index);
+ if (value != Scriptable.NOT_FOUND) {
+ break;
+ }
+ if (!(thisObj instanceof XMLObject)) {
+ break;
+ }
+ XMLObject xmlObject = (XMLObject)thisObj;
+ Scriptable extra = xmlObject.getExtraMethodSource(cx);
+ if (extra == null) {
+ break;
+ }
+ thisObj = extra;
+ }
+ if (!(value instanceof Callable)) {
+ throw notFunctionError(value, elem);
+ }
+
+ storeScriptable(cx, thisObj);
+ return (Callable)value;
+ }
+
+ /**
+ * Prepare for calling obj.property(...): return function corresponding to
+ * obj.property and make obj properly converted to Scriptable available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getPropFunctionAndThis(Object obj,
+ String property,
+ Context cx)
+ {
+ Scriptable thisObj = toObjectOrNull(cx, obj);
+ if (thisObj == null) {
+ throw undefCallError(obj, property);
+ }
+
+ Object value;
+ for (;;) {
+ // Ignore XML lookup as required by ECMA 357, 11.2.2.1
+ value = ScriptableObject.getProperty(thisObj, property);
+ if (value != Scriptable.NOT_FOUND) {
+ break;
+ }
+ if (!(thisObj instanceof XMLObject)) {
+ break;
+ }
+ XMLObject xmlObject = (XMLObject)thisObj;
+ Scriptable extra = xmlObject.getExtraMethodSource(cx);
+ if (extra == null) {
+ break;
+ }
+ thisObj = extra;
+ }
+
+ if (!(value instanceof Callable)) {
+ Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__");
+ if (noSuchMethod instanceof Callable)
+ value = new NoSuchMethodShim((Callable)noSuchMethod, property);
+ else
+ throw notFunctionError(thisObj, value, property);
+ }
+
+ storeScriptable(cx, thisObj);
+ return (Callable)value;
+ }
+
+ /**
+ * Prepare for calling <expression>(...): return function corresponding to
+ * <expression> and make parent scope of the function available
+ * as ScriptRuntime.lastStoredScriptable() for consumption as thisObj.
+ * The caller must call ScriptRuntime.lastStoredScriptable() immediately
+ * after calling this method.
+ */
+ public static Callable getValueFunctionAndThis(Object value, Context cx)
+ {
+ if (!(value instanceof Callable)) {
+ throw notFunctionError(value);
+ }
+
+ Callable f = (Callable)value;
+ Scriptable thisObj = null;
+ if (f instanceof Scriptable) {
+ thisObj = ((Scriptable)f).getParentScope();
+ }
+ if (thisObj == null) {
+ if (cx.topCallScope == null) throw new IllegalStateException();
+ thisObj = cx.topCallScope;
+ }
+ if (thisObj.getParentScope() != null) {
+ if (thisObj instanceof NativeWith) {
+ // functions defined inside with should have with target
+ // as their thisObj
+ } else if (thisObj instanceof NativeCall) {
+ // nested functions should have top scope as their thisObj
+ thisObj = ScriptableObject.getTopLevelScope(thisObj);
+ }
+ }
+ storeScriptable(cx, thisObj);
+ return f;
+ }
+
+ /**
+ * Perform function call in reference context. Should always
+ * return value that can be passed to
+ * {@link #refGet(Ref, Context)} or {@link #refSet(Ref, Object, Context)}
+ * arbitrary number of times.
+ * The args array reference should not be stored in any object that is
+ * can be GC-reachable after this method returns. If this is necessary,
+ * store args.clone(), not args array itself.
+ */
+ public static Ref callRef(Callable function, Scriptable thisObj,
+ Object[] args, Context cx)
+ {
+ if (function instanceof RefCallable) {
+ RefCallable rfunction = (RefCallable)function;
+ Ref ref = rfunction.refCall(cx, thisObj, args);
+ if (ref == null) {
+ throw new IllegalStateException(rfunction.getClass().getName()+".refCall() returned null");
+ }
+ return ref;
+ }
+ // No runtime support for now
+ String msg = getMessage1("msg.no.ref.from.function",
+ toString(function));
+ throw constructError("ReferenceError", msg);
+ }
+
+ /**
+ * Operator new.
+ *
+ * See ECMA 11.2.2
+ */
+ public static Scriptable newObject(Object fun, Context cx,
+ Scriptable scope, Object[] args)
+ {
+ if (!(fun instanceof Function)) {
+ throw notFunctionError(fun);
+ }
+ Function function = (Function)fun;
+ return function.construct(cx, scope, args);
+ }
+
+ public static Object callSpecial(Context cx, Callable fun,
+ Scriptable thisObj,
+ Object[] args, Scriptable scope,
+ Scriptable callerThis, int callType,
+ String filename, int lineNumber)
+ {
+ if (callType == Node.SPECIALCALL_EVAL) {
+ if (NativeGlobal.isEvalFunction(fun)) {
+ return evalSpecial(cx, scope, callerThis, args,
+ filename, lineNumber);
+ }
+ } else if (callType == Node.SPECIALCALL_WITH) {
+ if (NativeWith.isWithFunction(fun)) {
+ throw Context.reportRuntimeError1("msg.only.from.new",
+ "With");
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+
+ return fun.call(cx, scope, thisObj, args);
+ }
+
+ public static Object newSpecial(Context cx, Object fun,
+ Object[] args, Scriptable scope,
+ int callType)
+ {
+ if (callType == Node.SPECIALCALL_EVAL) {
+ if (NativeGlobal.isEvalFunction(fun)) {
+ throw typeError1("msg.not.ctor", "eval");
+ }
+ } else if (callType == Node.SPECIALCALL_WITH) {
+ if (NativeWith.isWithFunction(fun)) {
+ return NativeWith.newWithSpecial(cx, scope, args);
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+
+ return newObject(fun, cx, scope, args);
+ }
+
+ /**
+ * Function.prototype.apply and Function.prototype.call
+ *
+ * See Ecma 15.3.4.[34]
+ */
+ public static Object applyOrCall(boolean isApply,
+ Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ int L = args.length;
+ Callable function = getCallable(thisObj);
+
+ Scriptable callThis = null;
+ if (L != 0) {
+ callThis = toObjectOrNull(cx, args[0]);
+ }
+ if (callThis == null) {
+ // This covers the case of args[0] == (null|undefined) as well.
+ callThis = getTopCallScope(cx);
+ }
+
+ Object[] callArgs;
+ if (isApply) {
+ // Follow Ecma 15.3.4.3
+ callArgs = L <= 1 ? ScriptRuntime.emptyArgs :
+ getApplyArguments(cx, args[1]);
+ } else {
+ // Follow Ecma 15.3.4.4
+ if (L <= 1) {
+ callArgs = ScriptRuntime.emptyArgs;
+ } else {
+ callArgs = new Object[L - 1];
+ System.arraycopy(args, 1, callArgs, 0, L - 1);
+ }
+ }
+
+ return function.call(cx, scope, callThis, callArgs);
+ }
+
+ static Object[] getApplyArguments(Context cx, Object arg1)
+ {
+ if (arg1 == null || arg1 == Undefined.instance) {
+ return ScriptRuntime.emptyArgs;
+ } else if (arg1 instanceof NativeArray || arg1 instanceof Arguments) {
+ return cx.getElements((Scriptable) arg1);
+ } else {
+ throw ScriptRuntime.typeError0("msg.arg.isnt.array");
+ }
+ }
+
+ static Callable getCallable(Scriptable thisObj)
+ {
+ Callable function;
+ if (thisObj instanceof Callable) {
+ function = (Callable)thisObj;
+ } else {
+ Object value = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
+ if (!(value instanceof Callable)) {
+ throw ScriptRuntime.notFunctionError(value, thisObj);
+ }
+ function = (Callable)value;
+ }
+ return function;
+ }
+
+ /**
+ * The eval function property of the global object.
+ *
+ * See ECMA 15.1.2.1
+ */
+ public static Object evalSpecial(Context cx, Scriptable scope,
+ Object thisArg, Object[] args,
+ String filename, int lineNumber)
+ {
+ if (args.length < 1)
+ return Undefined.instance;
+ Object x = args[0];
+ if (!(x instanceof String)) {
+ if (cx.hasFeature(Context.FEATURE_STRICT_MODE) ||
+ cx.hasFeature(Context.FEATURE_STRICT_EVAL))
+ {
+ throw Context.reportRuntimeError0("msg.eval.nonstring.strict");
+ }
+ String message = ScriptRuntime.getMessage0("msg.eval.nonstring");
+ Context.reportWarning(message);
+ return x;
+ }
+ if (filename == null) {
+ int[] linep = new int[1];
+ filename = Context.getSourcePositionFromStack(linep);
+ if (filename != null) {
+ lineNumber = linep[0];
+ } else {
+ filename = "";
+ }
+ }
+ String sourceName = ScriptRuntime.
+ makeUrlForGeneratedScript(true, filename, lineNumber);
+
+ ErrorReporter reporter;
+ reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
+
+ Evaluator evaluator = Context.createInterpreter();
+ if (evaluator == null) {
+ throw new JavaScriptException("Interpreter not present",
+ filename, lineNumber);
+ }
+
+ // Compile with explicit interpreter instance to force interpreter
+ // mode.
+ Script script = cx.compileString((String)x, evaluator,
+ reporter, sourceName, 1, null);
+ evaluator.setEvalScriptFlag(script);
+ Callable c = (Callable)script;
+ return c.call(cx, scope, (Scriptable)thisArg, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * The typeof operator
+ */
+ public static String typeof(Object value)
+ {
+ if (value == null)
+ return "object";
+ if (value == Undefined.instance)
+ return "undefined";
+ if (value instanceof Scriptable)
+ {
+ if (value instanceof ScriptableObject &&
+ ((ScriptableObject)value).avoidObjectDetection())
+ {
+ return "undefined";
+ }
+ if (value instanceof XMLObject)
+ return "xml";
+ return (value instanceof Callable) ? "function" : "object";
+ }
+ if (value instanceof String)
+ return "string";
+ if (value instanceof Number)
+ return "number";
+ if (value instanceof Boolean)
+ return "boolean";
+ throw errorWithClassName("msg.invalid.type", value);
+ }
+
+ /**
+ * The typeof operator that correctly handles the undefined case
+ */
+ public static String typeofName(Scriptable scope, String id)
+ {
+ Context cx = Context.getContext();
+ Scriptable val = bind(cx, scope, id);
+ if (val == null)
+ return "undefined";
+ return typeof(getObjectProp(val, id, cx));
+ }
+
+ // neg:
+ // implement the '-' operator inline in the caller
+ // as "-toNumber(val)"
+
+ // not:
+ // implement the '!' operator inline in the caller
+ // as "!toBoolean(val)"
+
+ // bitnot:
+ // implement the '~' operator inline in the caller
+ // as "~toInt32(val)"
+
+ public static Object add(Object val1, Object val2, Context cx)
+ {
+ if(val1 instanceof Number && val2 instanceof Number) {
+ return wrapNumber(((Number)val1).doubleValue() +
+ ((Number)val2).doubleValue());
+ }
+ if (val1 instanceof XMLObject) {
+ Object test = ((XMLObject)val1).addValues(cx, true, val2);
+ if (test != Scriptable.NOT_FOUND) {
+ return test;
+ }
+ }
+ if (val2 instanceof XMLObject) {
+ Object test = ((XMLObject)val2).addValues(cx, false, val1);
+ if (test != Scriptable.NOT_FOUND) {
+ return test;
+ }
+ }
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(null);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(null);
+ if (!(val1 instanceof String) && !(val2 instanceof String))
+ if ((val1 instanceof Number) && (val2 instanceof Number))
+ return wrapNumber(((Number)val1).doubleValue() +
+ ((Number)val2).doubleValue());
+ else
+ return wrapNumber(toNumber(val1) + toNumber(val2));
+ return toString(val1).concat(toString(val2));
+ }
+
+ public static String add(String val1, Object val2) {
+ return val1.concat(toString(val2));
+ }
+
+ public static String add(Object val1, String val2) {
+ return toString(val1).concat(val2);
+ }
+
+ /**
+ * @deprecated The method is only present for compatibility.
+ */
+ public static Object nameIncrDecr(Scriptable scopeChain, String id,
+ int incrDecrMask)
+ {
+ return nameIncrDecr(scopeChain, id, Context.getContext(), incrDecrMask);
+ }
+
+ public static Object nameIncrDecr(Scriptable scopeChain, String id,
+ Context cx, int incrDecrMask)
+ {
+ Scriptable target;
+ Object value;
+ search: {
+ do {
+ if (cx.useDynamicScope && scopeChain.getParentScope() == null) {
+ scopeChain = checkDynamicScope(cx.topCallScope, scopeChain);
+ }
+ target = scopeChain;
+ do {
+ value = target.get(id, scopeChain);
+ if (value != Scriptable.NOT_FOUND) {
+ break search;
+ }
+ target = target.getPrototype();
+ } while (target != null);
+ scopeChain = scopeChain.getParentScope();
+ } while (scopeChain != null);
+ throw notFoundError(scopeChain, id);
+ }
+ return doScriptableIncrDecr(target, id, scopeChain, value,
+ incrDecrMask);
+ }
+
+ public static Object propIncrDecr(Object obj, String id,
+ Context cx, int incrDecrMask)
+ {
+ Scriptable start = toObjectOrNull(cx, obj);
+ if (start == null) {
+ throw undefReadError(obj, id);
+ }
+
+ Scriptable target = start;
+ Object value;
+ search: {
+ do {
+ value = target.get(id, start);
+ if (value != Scriptable.NOT_FOUND) {
+ break search;
+ }
+ target = target.getPrototype();
+ } while (target != null);
+ start.put(id, start, NaNobj);
+ return NaNobj;
+ }
+ return doScriptableIncrDecr(target, id, start, value,
+ incrDecrMask);
+ }
+
+ private static Object doScriptableIncrDecr(Scriptable target,
+ String id,
+ Scriptable protoChainStart,
+ Object value,
+ int incrDecrMask)
+ {
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ target.put(id, protoChainStart, result);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ public static Object elemIncrDecr(Object obj, Object index,
+ Context cx, int incrDecrMask)
+ {
+ Object value = getObjectElem(obj, index, cx);
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ setObjectElem(obj, index, result, cx);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask)
+ {
+ Object value = ref.get(cx);
+ boolean post = ((incrDecrMask & Node.POST_FLAG) != 0);
+ double number;
+ if (value instanceof Number) {
+ number = ((Number)value).doubleValue();
+ } else {
+ number = toNumber(value);
+ if (post) {
+ // convert result to number
+ value = wrapNumber(number);
+ }
+ }
+ if ((incrDecrMask & Node.DECR_FLAG) == 0) {
+ ++number;
+ } else {
+ --number;
+ }
+ Number result = wrapNumber(number);
+ ref.set(cx, result);
+ if (post) {
+ return value;
+ } else {
+ return result;
+ }
+ }
+
+ private static Object toPrimitive(Object val)
+ {
+ if (!(val instanceof Scriptable)) {
+ return val;
+ }
+ Scriptable s = (Scriptable)val;
+ Object result = s.getDefaultValue(null);
+ if (result instanceof Scriptable)
+ throw typeError0("msg.bad.default.value");
+ return result;
+ }
+
+ /**
+ * Equality
+ *
+ * See ECMA 11.9
+ */
+ public static boolean eq(Object x, Object y)
+ {
+ if (x == null || x == Undefined.instance) {
+ if (y == null || y == Undefined.instance) {
+ return true;
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ return false;
+ } else if (x instanceof Number) {
+ return eqNumber(((Number)x).doubleValue(), y);
+ } else if (x instanceof String) {
+ return eqString((String)x, y);
+ } else if (x instanceof Boolean) {
+ boolean b = ((Boolean)x).booleanValue();
+ if (y instanceof Boolean) {
+ return b == ((Boolean)y).booleanValue();
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ return eqNumber(b ? 1.0 : 0.0, y);
+ } else if (x instanceof Scriptable) {
+ if (y instanceof Scriptable) {
+ if (x == y) {
+ return true;
+ }
+ if (x instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)x).equivalentValues(y);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ if (x instanceof Wrapper && y instanceof Wrapper) {
+ // See bug 413838. Effectively an extension to ECMA for
+ // the LiveConnect case.
+ Object unwrappedX = ((Wrapper)x).unwrap();
+ Object unwrappedY = ((Wrapper)y).unwrap();
+ return unwrappedX == unwrappedY ||
+ (isPrimitive(unwrappedX) &&
+ isPrimitive(unwrappedY) &&
+ eq(unwrappedX, unwrappedY));
+ }
+ return false;
+ } else if (y instanceof Boolean) {
+ if (x instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)x).equivalentValues(y);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ double d = ((Boolean)y).booleanValue() ? 1.0 : 0.0;
+ return eqNumber(d, x);
+ } else if (y instanceof Number) {
+ return eqNumber(((Number)y).doubleValue(), x);
+ } else if (y instanceof String) {
+ return eqString((String)y, x);
+ }
+ // covers the case when y == Undefined.instance as well
+ return false;
+ } else {
+ warnAboutNonJSObject(x);
+ return x == y;
+ }
+ }
+
+ private static boolean isPrimitive(Object obj) {
+ return (obj instanceof Number) || (obj instanceof String) ||
+ (obj instanceof Boolean);
+ }
+
+ static boolean eqNumber(double x, Object y)
+ {
+ for (;;) {
+ if (y == null || y == Undefined.instance) {
+ return false;
+ } else if (y instanceof Number) {
+ return x == ((Number)y).doubleValue();
+ } else if (y instanceof String) {
+ return x == toNumber(y);
+ } else if (y instanceof Boolean) {
+ return x == (((Boolean)y).booleanValue() ? 1.0 : +0.0);
+ } else if (y instanceof Scriptable) {
+ if (y instanceof ScriptableObject) {
+ Object xval = wrapNumber(x);
+ Object test = ((ScriptableObject)y).equivalentValues(xval);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ y = toPrimitive(y);
+ } else {
+ warnAboutNonJSObject(y);
+ return false;
+ }
+ }
+ }
+
+ private static boolean eqString(String x, Object y)
+ {
+ for (;;) {
+ if (y == null || y == Undefined.instance) {
+ return false;
+ } else if (y instanceof String) {
+ return x.equals(y);
+ } else if (y instanceof Number) {
+ return toNumber(x) == ((Number)y).doubleValue();
+ } else if (y instanceof Boolean) {
+ return toNumber(x) == (((Boolean)y).booleanValue() ? 1.0 : 0.0);
+ } else if (y instanceof Scriptable) {
+ if (y instanceof ScriptableObject) {
+ Object test = ((ScriptableObject)y).equivalentValues(x);
+ if (test != Scriptable.NOT_FOUND) {
+ return ((Boolean)test).booleanValue();
+ }
+ }
+ y = toPrimitive(y);
+ continue;
+ } else {
+ warnAboutNonJSObject(y);
+ return false;
+ }
+ }
+ }
+ public static boolean shallowEq(Object x, Object y)
+ {
+ if (x == y) {
+ if (!(x instanceof Number)) {
+ return true;
+ }
+ // NaN check
+ double d = ((Number)x).doubleValue();
+ return d == d;
+ }
+ if (x == null || x == Undefined.instance) {
+ return false;
+ } else if (x instanceof Number) {
+ if (y instanceof Number) {
+ return ((Number)x).doubleValue() == ((Number)y).doubleValue();
+ }
+ } else if (x instanceof String) {
+ if (y instanceof String) {
+ return x.equals(y);
+ }
+ } else if (x instanceof Boolean) {
+ if (y instanceof Boolean) {
+ return x.equals(y);
+ }
+ } else if (x instanceof Scriptable) {
+ if (x instanceof Wrapper && y instanceof Wrapper) {
+ return ((Wrapper)x).unwrap() == ((Wrapper)y).unwrap();
+ }
+ } else {
+ warnAboutNonJSObject(x);
+ return x == y;
+ }
+ return false;
+ }
+
+ /**
+ * The instanceof operator.
+ *
+ * @return a instanceof b
+ */
+ public static boolean instanceOf(Object a, Object b, Context cx)
+ {
+ // Check RHS is an object
+ if (! (b instanceof Scriptable)) {
+ throw typeError0("msg.instanceof.not.object");
+ }
+
+ // for primitive values on LHS, return false
+ // XXX we may want to change this so that
+ // 5 instanceof Number == true
+ if (! (a instanceof Scriptable))
+ return false;
+
+ return ((Scriptable)b).hasInstance((Scriptable)a);
+ }
+
+ /**
+ * Delegates to
+ *
+ * @return true iff rhs appears in lhs' proto chain
+ */
+ public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
+ Scriptable proto = lhs.getPrototype();
+
+ while (proto != null) {
+ if (proto.equals(rhs)) return true;
+ proto = proto.getPrototype();
+ }
+
+ return false;
+ }
+
+ /**
+ * The in operator.
+ *
+ * This is a new JS 1.3 language feature. The in operator mirrors
+ * the operation of the for .. in construct, and tests whether the
+ * rhs has the property given by the lhs. It is different from the
+ * for .. in construct in that:
+ * <BR> - it doesn't perform ToObject on the right hand side
+ * <BR> - it returns true for DontEnum properties.
+ * @param a the left hand operand
+ * @param b the right hand operand
+ *
+ * @return true if property name or element number a is a property of b
+ */
+ public static boolean in(Object a, Object b, Context cx)
+ {
+ if (!(b instanceof Scriptable)) {
+ throw typeError0("msg.instanceof.not.object");
+ }
+
+ return hasObjectElem((Scriptable)b, a, cx);
+ }
+
+ public static boolean cmp_LT(Object val1, Object val2)
+ {
+ double d1, d2;
+ if (val1 instanceof Number && val2 instanceof Number) {
+ d1 = ((Number)val1).doubleValue();
+ d2 = ((Number)val2).doubleValue();
+ } else {
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
+ if (val1 instanceof String && val2 instanceof String) {
+ return ((String)val1).compareTo((String)val2) < 0;
+ }
+ d1 = toNumber(val1);
+ d2 = toNumber(val2);
+ }
+ return d1 < d2;
+ }
+
+ public static boolean cmp_LE(Object val1, Object val2)
+ {
+ double d1, d2;
+ if (val1 instanceof Number && val2 instanceof Number) {
+ d1 = ((Number)val1).doubleValue();
+ d2 = ((Number)val2).doubleValue();
+ } else {
+ if (val1 instanceof Scriptable)
+ val1 = ((Scriptable) val1).getDefaultValue(NumberClass);
+ if (val2 instanceof Scriptable)
+ val2 = ((Scriptable) val2).getDefaultValue(NumberClass);
+ if (val1 instanceof String && val2 instanceof String) {
+ return ((String)val1).compareTo((String)val2) <= 0;
+ }
+ d1 = toNumber(val1);
+ d2 = toNumber(val2);
+ }
+ return d1 <= d2;
+ }
+
+ // ------------------
+ // Statements
+ // ------------------
+
+ public static ScriptableObject getGlobal(Context cx) {
+ final String GLOBAL_CLASS = "org.mozilla.javascript.tools.shell.Global";
+ Class globalClass = Kit.classOrNull(GLOBAL_CLASS);
+ if (globalClass != null) {
+ try {
+ Class[] parm = { ScriptRuntime.ContextClass };
+ Constructor globalClassCtor = globalClass.getConstructor(parm);
+ Object[] arg = { cx };
+ return (ScriptableObject) globalClassCtor.newInstance(arg);
+ } catch (Exception e) {
+ // fall through...
+ }
+ }
+ return new ImporterTopLevel(cx);
+ }
+
+ public static boolean hasTopCall(Context cx)
+ {
+ return (cx.topCallScope != null);
+ }
+
+ public static Scriptable getTopCallScope(Context cx)
+ {
+ Scriptable scope = cx.topCallScope;
+ if (scope == null) {
+ throw new IllegalStateException();
+ }
+ return scope;
+ }
+
+ public static Object doTopCall(Callable callable,
+ Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (scope == null) throw new IllegalArgumentException();
+ if (cx.topCallScope != null) throw new IllegalStateException();
+
+ Object result;
+ cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
+ cx.useDynamicScope = cx.hasFeature(Context.FEATURE_DYNAMIC_SCOPE);
+ ContextFactory f = cx.getFactory();
+ try {
+ result = f.doTopCall(callable, cx, scope, thisObj, args);
+ } finally {
+ cx.topCallScope = null;
+ // Cleanup cached references
+ cx.cachedXMLLib = null;
+
+ if (cx.currentActivationCall != null) {
+ // Function should always call exitActivationFunction
+ // if it creates activation record
+ throw new IllegalStateException();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return <tt>possibleDynamicScope</tt> if <tt>staticTopScope</tt>
+ * is present on its prototype chain and return <tt>staticTopScope</tt>
+ * otherwise.
+ * Should only be called when <tt>staticTopScope</tt> is top scope.
+ */
+ static Scriptable checkDynamicScope(Scriptable possibleDynamicScope,
+ Scriptable staticTopScope)
+ {
+ // Return cx.topCallScope if scope
+ if (possibleDynamicScope == staticTopScope) {
+ return possibleDynamicScope;
+ }
+ Scriptable proto = possibleDynamicScope;
+ for (;;) {
+ proto = proto.getPrototype();
+ if (proto == staticTopScope) {
+ return possibleDynamicScope;
+ }
+ if (proto == null) {
+ return staticTopScope;
+ }
+ }
+ }
+
+ public static void addInstructionCount(Context cx, int instructionsToAdd)
+ {
+ cx.instructionCount += instructionsToAdd;
+ if (cx.instructionCount > cx.instructionThreshold)
+ {
+ cx.observeInstructionCount(cx.instructionCount);
+ cx.instructionCount = 0;
+ }
+ }
+
+ public static void initScript(NativeFunction funObj, Scriptable thisObj,
+ Context cx, Scriptable scope,
+ boolean evalScript)
+ {
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+
+ int varCount = funObj.getParamAndVarCount();
+ if (varCount != 0) {
+
+ Scriptable varScope = scope;
+ // Never define any variables from var statements inside with
+ // object. See bug 38590.
+ while (varScope instanceof NativeWith) {
+ varScope = varScope.getParentScope();
+ }
+
+ for (int i = varCount; i-- != 0;) {
+ String name = funObj.getParamOrVarName(i);
+ boolean isConst = funObj.getParamOrVarConst(i);
+ // Don't overwrite existing def if already defined in object
+ // or prototypes of object.
+ if (!ScriptableObject.hasProperty(scope, name)) {
+ if (!evalScript) {
+ // Global var definitions are supposed to be DONTDELETE
+ if (isConst)
+ ScriptableObject.defineConstProperty(varScope, name);
+ else
+ ScriptableObject.defineProperty(
+ varScope, name, Undefined.instance,
+ ScriptableObject.PERMANENT);
+ } else {
+ varScope.put(name, varScope, Undefined.instance);
+ }
+ } else {
+ ScriptableObject.redefineProperty(scope, name, isConst);
+ }
+ }
+ }
+ }
+
+ public static Scriptable createFunctionActivation(NativeFunction funObj,
+ Scriptable scope,
+ Object[] args)
+ {
+ return new NativeCall(funObj, scope, args);
+ }
+
+
+ public static void enterActivationFunction(Context cx,
+ Scriptable scope)
+ {
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+ NativeCall call = (NativeCall)scope;
+ call.parentActivationCall = cx.currentActivationCall;
+ cx.currentActivationCall = call;
+ }
+
+ public static void exitActivationFunction(Context cx)
+ {
+ NativeCall call = cx.currentActivationCall;
+ cx.currentActivationCall = call.parentActivationCall;
+ call.parentActivationCall = null;
+ }
+
+ static NativeCall findFunctionActivation(Context cx, Function f)
+ {
+ NativeCall call = cx.currentActivationCall;
+ while (call != null) {
+ if (call.function == f)
+ return call;
+ call = call.parentActivationCall;
+ }
+ return null;
+ }
+
+ public static Scriptable newCatchScope(Throwable t,
+ Scriptable lastCatchScope,
+ String exceptionName,
+ Context cx, Scriptable scope)
+ {
+ Object obj;
+ boolean cacheObj;
+
+ getObj:
+ if (t instanceof JavaScriptException) {
+ cacheObj = false;
+ obj = ((JavaScriptException)t).getValue();
+ } else {
+ cacheObj = true;
+
+ // Create wrapper object unless it was associated with
+ // the previous scope object
+
+ if (lastCatchScope != null) {
+ NativeObject last = (NativeObject)lastCatchScope;
+ obj = last.getAssociatedValue(t);
+ if (obj == null) Kit.codeBug();
+ break getObj;
+ }
+
+ RhinoException re;
+ String errorName;
+ String errorMsg;
+ Throwable javaException = null;
+
+ if (t instanceof EcmaError) {
+ EcmaError ee = (EcmaError)t;
+ re = ee;
+ errorName = ee.getName();
+ errorMsg = ee.getErrorMessage();
+ } else if (t instanceof WrappedException) {
+ WrappedException we = (WrappedException)t;
+ re = we;
+ javaException = we.getWrappedException();
+ errorName = "JavaException";
+ errorMsg = javaException.getClass().getName()
+ +": "+javaException.getMessage();
+ } else if (t instanceof EvaluatorException) {
+ // Pure evaluator exception, nor WrappedException instance
+ EvaluatorException ee = (EvaluatorException)t;
+ re = ee;
+ errorName = "InternalError";
+ errorMsg = ee.getMessage();
+ } else if (cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS)) {
+ // With FEATURE_ENHANCED_JAVA_ACCESS, scripts can catch
+ // all exception types
+ re = new WrappedException(t);
+ errorName = "JavaException";
+ errorMsg = t.toString();
+ } else {
+ // Script can catch only instances of JavaScriptException,
+ // EcmaError and EvaluatorException
+ throw Kit.codeBug();
+ }
+
+ String sourceUri = re.sourceName();
+ if (sourceUri == null) {
+ sourceUri = "";
+ }
+ int line = re.lineNumber();
+ Object args[];
+ if (line > 0) {
+ args = new Object[] { errorMsg, sourceUri, new Integer(line) };
+ } else {
+ args = new Object[] { errorMsg, sourceUri };
+ }
+
+ Scriptable errorObject = cx.newObject(scope, errorName, args);
+ ScriptableObject.putProperty(errorObject, "name", errorName);
+
+ if (javaException != null) {
+ Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException,
+ null);
+ ScriptableObject.defineProperty(
+ errorObject, "javaException", wrap,
+ ScriptableObject.PERMANENT | ScriptableObject.READONLY);
+ }
+ Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null);
+ ScriptableObject.defineProperty(
+ errorObject, "rhinoException", wrap,
+ ScriptableObject.PERMANENT | ScriptableObject.READONLY);
+
+ obj = errorObject;
+ }
+
+ NativeObject catchScopeObject = new NativeObject();
+ // See ECMA 12.4
+ catchScopeObject.defineProperty(
+ exceptionName, obj, ScriptableObject.PERMANENT);
+
+ // Add special Rhino object __exception__ defined in the catch
+ // scope that can be used to retrieve the Java exception associated
+ // with the JavaScript exception (to get stack trace info, etc.)
+ /*APPJET NOJAVA*/
+ /*catchScopeObject.defineProperty(
+ "__exception__", Context.javaToJS(t, scope),
+ ScriptableObject.PERMANENT|ScriptableObject.DONTENUM);*/
+
+ if (cacheObj) {
+ catchScopeObject.associateValue(t, obj);
+ }
+ return catchScopeObject;
+ }
+
+ public static Scriptable enterWith(Object obj, Context cx,
+ Scriptable scope)
+ {
+ Scriptable sobj = toObjectOrNull(cx, obj);
+ if (sobj == null) {
+ throw typeError1("msg.undef.with", toString(obj));
+ }
+ if (sobj instanceof XMLObject) {
+ XMLObject xmlObject = (XMLObject)sobj;
+ return xmlObject.enterWith(scope);
+ }
+ return new NativeWith(scope, sobj);
+ }
+
+ public static Scriptable leaveWith(Scriptable scope)
+ {
+ NativeWith nw = (NativeWith)scope;
+ return nw.getParentScope();
+ }
+
+ public static Scriptable enterDotQuery(Object value, Scriptable scope)
+ {
+ if (!(value instanceof XMLObject)) {
+ throw notXmlError(value);
+ }
+ XMLObject object = (XMLObject)value;
+ return object.enterDotQuery(scope);
+ }
+
+ public static Object updateDotQuery(boolean value, Scriptable scope)
+ {
+ // Return null to continue looping
+ NativeWith nw = (NativeWith)scope;
+ return nw.updateDotQuery(value);
+ }
+
+ public static Scriptable leaveDotQuery(Scriptable scope)
+ {
+ NativeWith nw = (NativeWith)scope;
+ return nw.getParentScope();
+ }
+
+ public static void setFunctionProtoAndParent(BaseFunction fn,
+ Scriptable scope)
+ {
+ fn.setParentScope(scope);
+ fn.setPrototype(ScriptableObject.getFunctionPrototype(scope));
+ }
+
+ public static void setObjectProtoAndParent(ScriptableObject object,
+ Scriptable scope)
+ {
+ // Compared with function it always sets the scope to top scope
+ scope = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ object.setParentScope(scope);
+ Scriptable proto
+ = ScriptableObject.getClassPrototype(scope, object.getClassName());
+ object.setPrototype(proto);
+ }
+
+ public static void initFunction(Context cx, Scriptable scope,
+ NativeFunction function, int type,
+ boolean fromEvalCode)
+ {
+ if (type == FunctionNode.FUNCTION_STATEMENT) {
+ String name = function.getFunctionName();
+ if (name != null && name.length() != 0) {
+ if (!fromEvalCode) {
+ // ECMA specifies that functions defined in global and
+ // function scope outside eval should have DONTDELETE set.
+ ScriptableObject.defineProperty
+ (scope, name, function, ScriptableObject.PERMANENT);
+ } else {
+ scope.put(name, scope, function);
+ }
+ }
+ } else if (type == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+ String name = function.getFunctionName();
+ if (name != null && name.length() != 0) {
+ // Always put function expression statements into initial
+ // activation object ignoring the with statement to follow
+ // SpiderMonkey
+ while (scope instanceof NativeWith) {
+ scope = scope.getParentScope();
+ }
+ scope.put(name, scope, function);
+ }
+ } else {
+ throw Kit.codeBug();
+ }
+ }
+
+ public static Scriptable newArrayLiteral(Object[] objects,
+ int[] skipIndices,
+ Context cx, Scriptable scope)
+ {
+ final int SKIP_DENSITY = 2;
+ int count = objects.length;
+ int skipCount = 0;
+ if (skipIndices != null) {
+ skipCount = skipIndices.length;
+ }
+ int length = count + skipCount;
+ if (length > 1 && skipCount * SKIP_DENSITY < length) {
+ // If not too sparse, create whole array for constructor
+ Object[] sparse;
+ if (skipCount == 0) {
+ sparse = objects;
+ } else {
+ sparse = new Object[length];
+ int skip = 0;
+ for (int i = 0, j = 0; i != length; ++i) {
+ if (skip != skipCount && skipIndices[skip] == i) {
+ sparse[i] = Scriptable.NOT_FOUND;
+ ++skip;
+ continue;
+ }
+ sparse[i] = objects[j];
+ ++j;
+ }
+ }
+ return cx.newObject(scope, "Array", sparse);
+ }
+
+ Scriptable arrayObj = cx.newObject(scope, "Array",
+ ScriptRuntime.emptyArgs);
+ int skip = 0;
+ for (int i = 0, j = 0; i != length; ++i) {
+ if (skip != skipCount && skipIndices[skip] == i) {
+ ++skip;
+ continue;
+ }
+ ScriptableObject.putProperty(arrayObj, i, objects[j]);
+ ++j;
+ }
+ return arrayObj;
+ }
+
+ /**
+ * This method is here for backward compat with existing compiled code. It
+ * is called when an object literal is compiled. The next instance will be
+ * the version called from new code.
+ * @deprecated This method only present for compatibility.
+ */
+ public static Scriptable newObjectLiteral(Object[] propertyIds,
+ Object[] propertyValues,
+ Context cx, Scriptable scope)
+ {
+ // This will initialize to all zeros, exactly what we need for old-style
+ // getterSetters values (no getters or setters in the list)
+ int [] getterSetters = new int[propertyIds.length];
+ return newObjectLiteral(propertyIds, propertyValues, getterSetters,
+ cx, scope);
+ }
+
+ public static Scriptable newObjectLiteral(Object[] propertyIds,
+ Object[] propertyValues,
+ int [] getterSetters,
+ Context cx, Scriptable scope)
+ {
+ Scriptable object = cx.newObject(scope);
+ for (int i = 0, end = propertyIds.length; i != end; ++i) {
+ Object id = propertyIds[i];
+ int getterSetter = getterSetters[i];
+ Object value = propertyValues[i];
+ if (id instanceof String) {
+ if (getterSetter == 0)
+ ScriptableObject.putProperty(object, (String)id, value);
+ else {
+ Callable fun;
+ String definer;
+ if (getterSetter < 0) // < 0 means get foo() ...
+ definer = "__defineGetter__";
+ else
+ definer = "__defineSetter__";
+ fun = getPropFunctionAndThis(object, definer, cx);
+ // Must consume the last scriptable object in cx
+ lastStoredScriptable(cx);
+ Object[] outArgs = new Object[2];
+ outArgs[0] = id;
+ outArgs[1] = value;
+ fun.call(cx, scope, object, outArgs);
+ }
+ } else {
+ int index = ((Integer)id).intValue();
+ ScriptableObject.putProperty(object, index, value);
+ }
+ }
+ return object;
+ }
+
+ public static boolean isArrayObject(Object obj)
+ {
+ return obj instanceof NativeArray || obj instanceof Arguments;
+ }
+
+ public static Object[] getArrayElements(Scriptable object)
+ {
+ Context cx = Context.getContext();
+ long longLen = NativeArray.getLengthProperty(cx, object);
+ if (longLen > Integer.MAX_VALUE) {
+ // arrays beyond MAX_INT is not in Java in any case
+ throw new IllegalArgumentException();
+ }
+ int len = (int) longLen;
+ if (len == 0) {
+ return ScriptRuntime.emptyArgs;
+ } else {
+ Object[] result = new Object[len];
+ for (int i=0; i < len; i++) {
+ Object elem = ScriptableObject.getProperty(object, i);
+ result[i] = (elem == Scriptable.NOT_FOUND) ? Undefined.instance
+ : elem;
+ }
+ return result;
+ }
+ }
+
+ static void checkDeprecated(Context cx, String name) {
+ int version = cx.getLanguageVersion();
+ if (version >= Context.VERSION_1_4 || version == Context.VERSION_DEFAULT) {
+ String msg = getMessage1("msg.deprec.ctor", name);
+ if (version == Context.VERSION_DEFAULT)
+ Context.reportWarning(msg);
+ else
+ throw Context.reportRuntimeError(msg);
+ }
+ }
+
+ public static String getMessage0(String messageId)
+ {
+ return getMessage(messageId, null);
+ }
+
+ public static String getMessage1(String messageId, Object arg1)
+ {
+ Object[] arguments = {arg1};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage2(
+ String messageId, Object arg1, Object arg2)
+ {
+ Object[] arguments = {arg1, arg2};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage3(
+ String messageId, Object arg1, Object arg2, Object arg3)
+ {
+ Object[] arguments = {arg1, arg2, arg3};
+ return getMessage(messageId, arguments);
+ }
+
+ public static String getMessage4(
+ String messageId, Object arg1, Object arg2, Object arg3, Object arg4)
+ {
+ Object[] arguments = {arg1, arg2, arg3, arg4};
+ return getMessage(messageId, arguments);
+ }
+
+ /* OPT there's a noticable delay for the first error! Maybe it'd
+ * make sense to use a ListResourceBundle instead of a properties
+ * file to avoid (synchronized) text parsing.
+ */
+ public static String getMessage(String messageId, Object[] arguments)
+ {
+ final String defaultResource
+ = "org.mozilla.javascript.resources.Messages";
+
+ Context cx = Context.getCurrentContext();
+ Locale locale = cx != null ? cx.getLocale() : Locale.getDefault();
+
+ // ResourceBundle does cacheing.
+ ResourceBundle rb = ResourceBundle.getBundle(defaultResource, locale);
+
+ String formatString;
+ try {
+ formatString = rb.getString(messageId);
+ } catch (java.util.MissingResourceException mre) {
+ throw new RuntimeException
+ ("no message resource found for message property "+ messageId);
+ }
+
+ /*
+ * It's OK to format the string, even if 'arguments' is null;
+ * we need to format it anyway, to make double ''s collapse to
+ * single 's.
+ */
+ MessageFormat formatter = new MessageFormat(formatString);
+ return formatter.format(arguments);
+ }
+
+ public static EcmaError constructError(String error, String message)
+ {
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ return constructError(error, message, filename, linep[0], null, 0);
+ }
+
+ public static EcmaError constructError(String error,
+ String message,
+ int lineNumberDelta)
+ {
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ if (linep[0] != 0) {
+ linep[0] += lineNumberDelta;
+ }
+ return constructError(error, message, filename, linep[0], null, 0);
+ }
+
+ public static EcmaError constructError(String error,
+ String message,
+ String sourceName,
+ int lineNumber,
+ String lineSource,
+ int columnNumber)
+ {
+ return new EcmaError(error, message, sourceName,
+ lineNumber, lineSource, columnNumber);
+ }
+
+ public static EcmaError typeError(String message)
+ {
+ return constructError("TypeError", message);
+ }
+
+ public static EcmaError typeError0(String messageId)
+ {
+ String msg = getMessage0(messageId);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError1(String messageId, String arg1)
+ {
+ String msg = getMessage1(messageId, arg1);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError2(String messageId, String arg1,
+ String arg2)
+ {
+ String msg = getMessage2(messageId, arg1, arg2);
+ return typeError(msg);
+ }
+
+ public static EcmaError typeError3(String messageId, String arg1,
+ String arg2, String arg3)
+ {
+ String msg = getMessage3(messageId, arg1, arg2, arg3);
+ return typeError(msg);
+ }
+
+ public static RuntimeException undefReadError(Object object, Object id)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ return typeError2("msg.undef.prop.read", toString(object), idStr);
+ }
+
+ public static RuntimeException undefCallError(Object object, Object id)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ return typeError2("msg.undef.method.call", toString(object), idStr);
+ }
+
+ public static RuntimeException undefWriteError(Object object,
+ Object id,
+ Object value)
+ {
+ String idStr = (id == null) ? "null" : id.toString();
+ String valueStr = (value instanceof Scriptable)
+ ? value.toString() : toString(value);
+ return typeError3("msg.undef.prop.write", toString(object), idStr,
+ valueStr);
+ }
+
+ public static RuntimeException notFoundError(Scriptable object,
+ String property)
+ {
+ // XXX: use object to improve the error message
+ String msg = getMessage1("msg.is.not.defined", property);
+ throw constructError("ReferenceError", msg);
+ }
+
+ public static RuntimeException notFunctionError(Object value)
+ {
+ return notFunctionError(value, value);
+ }
+
+ public static RuntimeException notFunctionError(Object value,
+ Object messageHelper)
+ {
+ // Use value for better error reporting
+ String msg = (messageHelper == null)
+ ? "null" : messageHelper.toString();
+ if (value == Scriptable.NOT_FOUND) {
+ return typeError1("msg.function.not.found", msg);
+ }
+ return typeError2("msg.isnt.function", msg, typeof(value));
+ }
+
+ public static RuntimeException notFunctionError(Object obj, Object value,
+ String propertyName)
+ {
+ // Use obj and value for better error reporting
+ String objString = toString(obj);
+ if (value == Scriptable.NOT_FOUND) {
+ return typeError2("msg.function.not.found.in", propertyName,
+ objString);
+ }
+ return typeError3("msg.isnt.function.in", propertyName, objString,
+ typeof(value));
+ }
+
+ private static RuntimeException notXmlError(Object value)
+ {
+ throw typeError1("msg.isnt.xml.object", toString(value));
+ }
+
+ private static void warnAboutNonJSObject(Object nonJSObject)
+ {
+ String message =
+"RHINO USAGE WARNING: Missed Context.javaToJS() conversion:\n"
++"Rhino runtime detected object "+nonJSObject+" of class "+nonJSObject.getClass().getName()+" where it expected String, Number, Boolean or Scriptable instance. Please check your code for missing Context.javaToJS() call.";
+ Context.reportWarning(message);
+ // Just to be sure that it would be noticed
+ System.err.println(message);
+ }
+
+ public static RegExpProxy getRegExpProxy(Context cx)
+ {
+ return cx.getRegExpProxy();
+ }
+
+ public static void setRegExpProxy(Context cx, RegExpProxy proxy)
+ {
+ if (proxy == null) throw new IllegalArgumentException();
+ cx.regExpProxy = proxy;
+ }
+
+ public static RegExpProxy checkRegExpProxy(Context cx)
+ {
+ RegExpProxy result = getRegExpProxy(cx);
+ if (result == null) {
+ throw Context.reportRuntimeError0("msg.no.regexp");
+ }
+ return result;
+ }
+
+ private static XMLLib currentXMLLib(Context cx)
+ {
+ // Scripts should be running to access this
+ if (cx.topCallScope == null)
+ throw new IllegalStateException();
+
+ XMLLib xmlLib = cx.cachedXMLLib;
+ if (xmlLib == null) {
+ xmlLib = XMLLib.extractFromScope(cx.topCallScope);
+ if (xmlLib == null)
+ throw new IllegalStateException();
+ cx.cachedXMLLib = xmlLib;
+ }
+
+ return xmlLib;
+ }
+
+ /**
+ * Escapes the reserved characters in a value of an attribute
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public static String escapeAttributeValue(Object value, Context cx)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.escapeAttributeValue(value);
+ }
+
+ /**
+ * Escapes the reserved characters in a value of a text node
+ *
+ * @param value Unescaped text
+ * @return The escaped text
+ */
+ public static String escapeTextValue(Object value, Context cx)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.escapeTextValue(value);
+ }
+
+ public static Ref memberRef(Object obj, Object elem,
+ Context cx, int memberTypeFlags)
+ {
+ if (!(obj instanceof XMLObject)) {
+ throw notXmlError(obj);
+ }
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.memberRef(cx, elem, memberTypeFlags);
+ }
+
+ public static Ref memberRef(Object obj, Object namespace, Object elem,
+ Context cx, int memberTypeFlags)
+ {
+ if (!(obj instanceof XMLObject)) {
+ throw notXmlError(obj);
+ }
+ XMLObject xmlObject = (XMLObject)obj;
+ return xmlObject.memberRef(cx, namespace, elem, memberTypeFlags);
+ }
+
+ public static Ref nameRef(Object name, Context cx,
+ Scriptable scope, int memberTypeFlags)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.nameRef(cx, name, scope, memberTypeFlags);
+ }
+
+ public static Ref nameRef(Object namespace, Object name, Context cx,
+ Scriptable scope, int memberTypeFlags)
+ {
+ XMLLib xmlLib = currentXMLLib(cx);
+ return xmlLib.nameRef(cx, namespace, name, scope, memberTypeFlags);
+ }
+
+ private static void storeIndexResult(Context cx, int index)
+ {
+ cx.scratchIndex = index;
+ }
+
+ static int lastIndexResult(Context cx)
+ {
+ return cx.scratchIndex;
+ }
+
+ public static void storeUint32Result(Context cx, long value)
+ {
+ if ((value >>> 32) != 0)
+ throw new IllegalArgumentException();
+ cx.scratchUint32 = value;
+ }
+
+ public static long lastUint32Result(Context cx)
+ {
+ long value = cx.scratchUint32;
+ if ((value >>> 32) != 0)
+ throw new IllegalStateException();
+ return value;
+ }
+
+ private static void storeScriptable(Context cx, Scriptable value)
+ {
+ // The previosly stored scratchScriptable should be consumed
+ if (cx.scratchScriptable != null)
+ throw new IllegalStateException();
+ cx.scratchScriptable = value;
+ }
+
+ public static Scriptable lastStoredScriptable(Context cx)
+ {
+ Scriptable result = cx.scratchScriptable;
+ cx.scratchScriptable = null;
+ return result;
+ }
+
+ static String makeUrlForGeneratedScript
+ (boolean isEval, String masterScriptUrl, int masterScriptLine)
+ {
+ if (isEval) {
+ return masterScriptUrl+'#'+masterScriptLine+"(eval)";
+ } else {
+ return masterScriptUrl+'#'+masterScriptLine+"(Function)";
+ }
+ }
+
+ static boolean isGeneratedScript(String sourceUrl) {
+ // ALERT: this may clash with a valid URL containing (eval) or
+ // (Function)
+ return sourceUrl.indexOf("(eval)") >= 0
+ || sourceUrl.indexOf("(Function)") >= 0;
+ }
+
+ private static RuntimeException errorWithClassName(String msg, Object val)
+ {
+ return Context.reportRuntimeError1(msg, val.getClass().getName());
+ }
+
+ public static final Object[] emptyArgs = new Object[0];
+ public static final String[] emptyStrings = new String[0];
+
+}