All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mozilla.javascript.ScriptRuntime Maven / Gradle / Ivy

Go to download

Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.

There is a newer version: 1.7.15
Show newest version
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import org.mozilla.javascript.ast.FunctionNode;
import org.mozilla.javascript.v8dtoa.DoubleConversion;
import org.mozilla.javascript.v8dtoa.FastDtoa;
import org.mozilla.javascript.xml.XMLLib;
import org.mozilla.javascript.xml.XMLObject;

/**
 * This is the class that implements the runtime.
 *
 * @author Norris Boyd
 */
public class ScriptRuntime {

    /** No instances should be created. */
    protected ScriptRuntime() {}

    /**
     * Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3
     *
     * @deprecated {@link #typeErrorThrower(Context)}
     */
    @Deprecated
    public static BaseFunction typeErrorThrower() {
        return typeErrorThrower(Context.getCurrentContext());
    }

    /** Returns representation of the [[ThrowTypeError]] object. See ECMA 5 spec, 13.2.3 */
    public static BaseFunction typeErrorThrower(Context cx) {
        if (cx.typeErrorThrower == null) {
            BaseFunction thrower =
                    new BaseFunction() {
                        private static final long serialVersionUID = -5891740962154902286L;

                        @Override
                        public Object call(
                                Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
                            throw typeErrorById("msg.op.not.allowed");
                        }

                        @Override
                        public int getLength() {
                            return 0;
                        }
                    };
            ScriptRuntime.setFunctionProtoAndParent(thrower, cx.topCallScope);
            thrower.preventExtensions();
            cx.typeErrorThrower = thrower;
        }
        return cx.typeErrorThrower;
    }

    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 this object
         * @param args the array of arguments
         * @return the result of the call
         */
        @Override
        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 static final 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"),
            BigIntegerClass = Kit.classOrNull("java.math.BigInteger");
    public static final Class ContextClass = Kit.classOrNull("org.mozilla.javascript.Context"),
            ContextFactoryClass = Kit.classOrNull("org.mozilla.javascript.ContextFactory"),
            FunctionClass = Kit.classOrNull("org.mozilla.javascript.Function"),
            ScriptableObjectClass = Kit.classOrNull("org.mozilla.javascript.ScriptableObject");
    public static final Class ScriptableClass = Scriptable.class;

    private static final Object LIBRARY_SCOPE_KEY = "LIBRARY_SCOPE";

    public static boolean isRhinoRuntimeType(Class cl) {
        if (cl.isPrimitive()) {
            return (cl != Character.TYPE);
        }
        return (cl == StringClass
                || cl == BooleanClass
                || NumberClass.isAssignableFrom(cl)
                || ScriptableClass.isAssignableFrom(cl));
    }

    public static ScriptableObject initSafeStandardObjects(
            Context cx, ScriptableObject scope, boolean sealed) {
        if (scope == null) {
            scope = new NativeObject();
        } else if (scope instanceof TopLevel) {
            ((TopLevel) scope).clearCache();
        }

        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.getClassPrototype(scope, "Function");
        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);
        NativeJSON.init(scope, sealed);

        NativeWith.init(scope, sealed);
        NativeCall.init(scope, sealed);
        NativeScript.init(scope, sealed);

        NativeIterator.init(cx, scope, sealed); // Also initializes NativeGenerator & ES6Generator

        NativeArrayIterator.init(scope, sealed);
        NativeStringIterator.init(scope, sealed);

        NativeJavaObject.init(scope, sealed);
        NativeJavaMap.init(scope, sealed);

        boolean withXml =
                cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null;

        // define lazy-loaded properties using their class name
        new LazilyLoadedCtor(
                scope, "RegExp", "org.mozilla.javascript.regexp.NativeRegExp", sealed, true);
        new LazilyLoadedCtor(
                scope, "Continuation", "org.mozilla.javascript.NativeContinuation", sealed, true);

        if (withXml) {
            String xmlImpl = cx.getE4xImplementationFactory().getImplementationClassName();
            new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true);
            new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true);
            new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true);
            new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true);
        }

        if (((cx.getLanguageVersion() >= Context.VERSION_1_8)
                        && cx.hasFeature(Context.FEATURE_V8_EXTENSIONS))
                || (cx.getLanguageVersion() >= Context.VERSION_ES6)) {
            new LazilyLoadedCtor(
                    scope,
                    "ArrayBuffer",
                    "org.mozilla.javascript.typedarrays.NativeArrayBuffer",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Int8Array",
                    "org.mozilla.javascript.typedarrays.NativeInt8Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Uint8Array",
                    "org.mozilla.javascript.typedarrays.NativeUint8Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Uint8ClampedArray",
                    "org.mozilla.javascript.typedarrays.NativeUint8ClampedArray",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Int16Array",
                    "org.mozilla.javascript.typedarrays.NativeInt16Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Uint16Array",
                    "org.mozilla.javascript.typedarrays.NativeUint16Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Int32Array",
                    "org.mozilla.javascript.typedarrays.NativeInt32Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Uint32Array",
                    "org.mozilla.javascript.typedarrays.NativeUint32Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Float32Array",
                    "org.mozilla.javascript.typedarrays.NativeFloat32Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "Float64Array",
                    "org.mozilla.javascript.typedarrays.NativeFloat64Array",
                    sealed,
                    true);
            new LazilyLoadedCtor(
                    scope,
                    "DataView",
                    "org.mozilla.javascript.typedarrays.NativeDataView",
                    sealed,
                    true);
        }

        if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
            NativeSymbol.init(cx, scope, sealed);
            NativeCollectionIterator.init(scope, NativeSet.ITERATOR_TAG, sealed);
            NativeCollectionIterator.init(scope, NativeMap.ITERATOR_TAG, sealed);
            NativeMap.init(cx, scope, sealed);
            NativePromise.init(cx, scope, sealed);
            NativeSet.init(cx, scope, sealed);
            NativeWeakMap.init(scope, sealed);
            NativeWeakSet.init(scope, sealed);
            NativeBigInt.init(scope, sealed);
        }

        if (scope instanceof TopLevel) {
            ((TopLevel) scope).cacheBuiltins(scope, sealed);
        }

        return scope;
    }

    public static ScriptableObject initStandardObjects(
            Context cx, ScriptableObject scope, boolean sealed) {
        ScriptableObject s = initSafeStandardObjects(cx, scope, sealed);

        new LazilyLoadedCtor(
                s, "Packages", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
        new LazilyLoadedCtor(
                s, "getClass", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
        new LazilyLoadedCtor(s, "JavaAdapter", "org.mozilla.javascript.JavaAdapter", sealed, true);
        new LazilyLoadedCtor(
                s, "JavaImporter", "org.mozilla.javascript.ImporterTopLevel", sealed, true);

        for (String packageName : getTopPackageNames()) {
            new LazilyLoadedCtor(
                    s, packageName, "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
        }

        return s;
    }

    static String[] getTopPackageNames() {
        // Include "android" top package if running on Android
        return "Dalvik".equals(System.getProperty("java.vm.name"))
                ? new String[] {"java", "javax", "org", "com", "edu", "net", "android"}
                : new String[] {"java", "javax", "org", "com", "edu", "net"};
    }

    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 isJSWhitespaceOrLineTerminator(int c) {
        return (isStrWhiteSpaceChar(c) || isJSLineTerminator(c));
    }

    /**
     * Indicates if the character is a Str whitespace char according to ECMA spec: StrWhiteSpaceChar
     * :::           
     */
    static boolean isStrWhiteSpaceChar(int c) {
        switch (c) {
            case ' ': // 
            case '\n': // 
            case '\r': // 
            case '\t': // 
            case '\u00A0': // 
            case '\u000C': // 
            case '\u000B': // 
            case '\u2028': // 
            case '\u2029': // 
            case '\uFEFF': // 
                return true;
            default:
                return Character.getType(c) == Character.SPACE_SEPARATOR;
        }
    }

    public static Boolean wrapBoolean(boolean b) {
        return Boolean.valueOf(b);
    }

    public static Integer wrapInt(int i) {
        return Integer.valueOf(i);
    }

    public static Number wrapNumber(double x) {
        if (Double.isNaN(x)) {
            return ScriptRuntime.NaNobj;
        }
        return Double.valueOf(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 || Undefined.isUndefined(val)) return false; if (val instanceof CharSequence) return ((CharSequence) val).length() != 0; if (val instanceof BigInteger) { return !((BigInteger) val).equals(BigInteger.ZERO); } if (val instanceof Number) { double d = ((Number) val).doubleValue(); return (!Double.isNaN(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) && !isSymbol(val)) 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 BigInteger) { throw typeErrorById("msg.cant.convert.to.number", "BigInt"); } if (val instanceof Number) return ((Number) val).doubleValue(); if (val == null) return +0.0; if (Undefined.isUndefined(val)) return NaN; if (val instanceof String) return toNumber((String) val); if (val instanceof CharSequence) return toNumber(val.toString()); if (val instanceof Boolean) return ((Boolean) val).booleanValue() ? 1 : +0.0; if (val instanceof Symbol) throw typeErrorById("msg.not.a.number"); if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(NumberClass); if ((val instanceof Scriptable) && !isSymbol(val)) 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; } public static final double NaN = Double.NaN; public static final Double NaNobj = Double.valueOf(NaN); // Preserve backward-compatibility with historical value of this. public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L); public static final Double zeroObj = Double.valueOf(0.0); public static final Double negativeZeroObj = Double.valueOf(-0.0); static double stringPrefixToNumber(String s, int start, int radix) { return stringToNumber(s, start, s.length() - 1, radix, true); } static double stringToNumber(String s, int start, int end, int radix) { return stringToNumber(s, start, end, radix, false); } /* * Helper function for toNumber, parseInt, and TokenStream.getToken. */ private static double stringToNumber( String source, int sourceStart, int sourceEnd, int radix, boolean isPrefix) { char digitMax = '9'; char lowerCaseBound = 'a'; char upperCaseBound = 'A'; 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 = sourceStart; end <= sourceEnd; end++) { char c = source.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 if (!isPrefix) return NaN; // isn't a prefix but found unexpected char else break; // unexpected char sum = sum * radix + newDigit; } if (sourceStart == end) { // stopped right at the beginning return NaN; } if (sum > NativeNumber.MAX_SAFE_INTEGER) { 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.parseDouble(source.substring(sourceStart, end)); } 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; int pos = sourceStart; for (; ; ) { if (bitShiftInChar == 1) { if (pos == end) break; digit = source.charAt(pos++); 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 the #sec-tonumber-applied-to-the-string-type section of ECMA */ public static double toNumber(String s) { final int len = s.length(); // Skip whitespace at the start int start = 0; char startChar; for (; ; ) { if (start == len) { // empty or contains only whitespace return +0.0; } startChar = s.charAt(start); if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) { // found first non-whitespace character break; } start++; } // Skip whitespace at the end int end = len - 1; char endChar; while (ScriptRuntime.isStrWhiteSpaceChar(endChar = s.charAt(end))) { end--; } // Do not break scripts relying on old non-compliant conversion // (see bug #368) // 1. makes ToNumber parse only a valid prefix in hex literals (similar to 'parseInt()') // ToNumber('0x10 something') => 16 // 2. allows plus and minus signs for hexadecimal numbers // ToNumber('-0x10') => -16 // 3. disables support for binary ('0b10') and octal ('0o13') literals // ToNumber('0b1') => NaN // ToNumber('0o5') => NaN final Context cx = Context.getCurrentContext(); final boolean oldParsingMode = cx == null || cx.getLanguageVersion() < Context.VERSION_ES6; // Handle non-base10 numbers if (startChar == '0') { if (start + 2 <= end) { final char radixC = s.charAt(start + 1); int radix = -1; if (radixC == 'x' || radixC == 'X') { radix = 16; } else if (!oldParsingMode && (radixC == 'o' || radixC == 'O')) { radix = 8; } else if (!oldParsingMode && (radixC == 'b' || radixC == 'B')) { radix = 2; } if (radix != -1) { if (oldParsingMode) { return stringPrefixToNumber(s, start + 2, radix); } return stringToNumber(s, start + 2, end, radix); } } } else if (oldParsingMode && (startChar == '+' || startChar == '-')) { // If in old parsing mode, check for a signed hexadecimal number if (start + 3 <= end && s.charAt(start + 1) == '0') { final char radixC = s.charAt(start + 2); if (radixC == 'x' || radixC == 'X') { double val = stringPrefixToNumber(s, start + 3, 16); return startChar == '-' ? -val : val; } } } 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 base10, non-infinity number: // just try a normal floating point conversion String sub = s.substring(start, end + 1); // Quick test to check string contains only valid characters because // Double.parseDouble() can be slow and accept input we want to reject 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.parseDouble(sub); } catch (NumberFormatException ex) { return NaN; } } /** Convert the value to a BigInt. */ public static BigInteger toBigInt(Object val) { for (; ; ) { if (val instanceof BigInteger) { return (BigInteger) val; } if (val instanceof BigDecimal) { return ((BigDecimal) val).toBigInteger(); } if (val instanceof Number) { if (val instanceof Long) { return BigInteger.valueOf(((Long) val)); } else { double d = ((Number) val).doubleValue(); if (Double.isNaN(d) || Double.isInfinite(d)) { throw rangeErrorById( "msg.cant.convert.to.bigint.isnt.integer", toString(val)); } BigDecimal bd = new BigDecimal(d, MathContext.UNLIMITED); try { return bd.toBigIntegerExact(); } catch (ArithmeticException e) { throw rangeErrorById( "msg.cant.convert.to.bigint.isnt.integer", toString(val)); } } } if (val == null || Undefined.isUndefined(val)) { throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); } if (val instanceof String) { return toBigInt((String) val); } if (val instanceof CharSequence) { return toBigInt(val.toString()); } if (val instanceof Boolean) { return ((Boolean) val).booleanValue() ? BigInteger.ONE : BigInteger.ZERO; } if (val instanceof Symbol) { throw typeErrorById("msg.cant.convert.to.bigint", toString(val)); } if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(BigIntegerClass); if ((val instanceof Scriptable) && !isSymbol(val)) { throw errorWithClassName("msg.primitive.expected", val); } continue; } warnAboutNonJSObject(val); return BigInteger.ZERO; } } /** ToBigInt applied to the String type */ public static BigInteger toBigInt(String s) { final int len = s.length(); // Skip whitespace at the start int start = 0; char startChar; for (; ; ) { if (start == len) { // empty or contains only whitespace return BigInteger.ZERO; } startChar = s.charAt(start); if (!ScriptRuntime.isStrWhiteSpaceChar(startChar)) { // found first non-whitespace character break; } start++; } // Skip whitespace at the end int end = len - 1; while (ScriptRuntime.isStrWhiteSpaceChar(s.charAt(end))) { end--; } // Handle non-base10 numbers if (startChar == '0') { if (start + 2 <= end) { final char radixC = s.charAt(start + 1); int radix = -1; if (radixC == 'x' || radixC == 'X') { radix = 16; } else if (radixC == 'o' || radixC == 'O') { radix = 8; } else if (radixC == 'b' || radixC == 'B') { radix = 2; } if (radix != -1) { try { return new BigInteger(s.substring(start + 2, end + 1), radix); } catch (NumberFormatException ex) { throw syntaxErrorById("msg.bigint.bad.form"); } } } } // A base10, non-infinity bigint: // just try a normal biginteger conversion String sub = s.substring(start, end + 1); for (int i = sub.length() - 1; i >= 0; i--) { char c = sub.charAt(i); if (i == 0 && (c == '+' || c == '-')) { continue; } if ('0' <= c && c <= '9') { continue; } throw syntaxErrorById("msg.bigint.bad.form"); } try { return new BigInteger(sub); } catch (NumberFormatException ex) { throw syntaxErrorById("msg.bigint.bad.form"); } } /** * Convert the value to a Numeric (Number or BigInt). * *

toNumber does not allow java.math.BigInteger. toNumeric allows java.math.BigInteger. * *

See ECMA 7.1.3 (v11.0). */ public static Number toNumeric(Object val) { if (val instanceof Number) { return (Number) val; } return toNumber(val); } public static int toIndex(Object val) { if (Undefined.isUndefined(val)) { return 0; } double integerIndex = toInteger(val); if (integerIndex < 0) { throw rangeError("index out of range"); } // ToLength double index = Math.min(integerIndex, NativeNumber.MAX_SAFE_INTEGER); if (integerIndex != index) { throw rangeError("index out of range"); } return (int) index; } /** * 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; Object[] result = new Object[count]; System.arraycopy(args, 0, result, 0, args.length); if (args.length < count) { Arrays.fill(result, args.length, count, Undefined.instance); } return result; } 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(); StringBuilder 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 StringBuilder(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, Context cx, boolean isStrict) { 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, cx.getLanguageVersion(), isStrict); } public static CharSequence toCharSequence(Object val) { if (val instanceof NativeString) { return ((NativeString) val).toCharSequence(); } return val instanceof CharSequence ? (CharSequence) val : toString(val); } /** * Convert the value to a string. * *

See ECMA 9.8. */ public static String toString(Object val) { for (; ; ) { if (val == null) { return "null"; } if (Undefined.isUndefined(val)) { return "undefined"; } if (val instanceof String) { return (String) val; } if (val instanceof CharSequence) { return val.toString(); } if (val instanceof BigInteger) { return val.toString(); } if (val instanceof Number) { // XXX should we just teach NativeNumber.stringValue() // about Numbers? return numberToString(((Number) val).doubleValue(), 10); } if (val instanceof Symbol) { throw typeErrorById("msg.not.a.string"); } if (val instanceof Scriptable) { val = ((Scriptable) val).getDefaultValue(StringClass); if ((val instanceof Scriptable) && !isSymbol(val)) { throw errorWithClassName("msg.primitive.expected", val); } continue; } return val.toString(); } } static String defaultObjectToString(Scriptable obj) { if (obj == null) return "[object Null]"; if (Undefined.isUndefined(obj)) return "[object Undefined]"; 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 ((base < 2) || (base > 36)) { throw Context.reportRuntimeErrorById("msg.bad.radix", Integer.toString(base)); } if (Double.isNaN(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 != 10) { return DToA.JS_dtobasestr(base, d); } // V8 FastDtoa can't convert all numbers, so try it first but // fall back to old DToA in case it fails String result = FastDtoa.numberToString(d); if (result != null) { return result; } StringBuilder buffer = new StringBuilder(); DToA.JS_dtostr(buffer, DToA.DTOSTR_STANDARD, 0, d); return buffer.toString(); } public static String bigIntToString(BigInteger n, int base) { if ((base < 2) || (base > 36)) { throw rangeErrorById("msg.bad.radix", Integer.toString(base)); } return n.toString(base); } static String uneval(Context cx, Scriptable scope, Object value) { if (value == null) { return "null"; } if (Undefined.isUndefined(value)) { return "undefined"; } if (value instanceof CharSequence) { String escaped = escapeString(value.toString()); StringBuilder sb = new StringBuilder(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); } StringBuilder result = new StringBuilder(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, cx, cx.isStrictMode())) { 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); } /** * Warning: This doesn't allow to resolve primitive prototype properly when * many top scopes are involved * * @deprecated Use {@link #toObjectOrNull(Context, Object, Scriptable)} instead */ @Deprecated public static Scriptable toObjectOrNull(Context cx, Object obj) { if (obj instanceof Scriptable) { return (Scriptable) obj; } else if (obj != null && !Undefined.isUndefined(obj)) { return toObject(cx, getTopCallScope(cx), obj); } return null; } /** @param scope the scope that should be used to resolve primitive prototype */ public static Scriptable toObjectOrNull(Context cx, Object obj, Scriptable scope) { if (obj instanceof Scriptable) { return (Scriptable) obj; } else if (obj != null && !Undefined.isUndefined(obj)) { return toObject(cx, scope, obj); } return null; } /** @deprecated Use {@link #toObject(Scriptable, Object)} instead. */ @Deprecated 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 == null) { throw typeErrorById("msg.null.to.object"); } if (Undefined.isUndefined(val)) { throw typeErrorById("msg.undef.to.object"); } if (isSymbol(val)) { NativeSymbol result = new NativeSymbol((NativeSymbol) val); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Symbol); return result; } if (val instanceof Scriptable) { return (Scriptable) val; } if (val instanceof CharSequence) { // FIXME we want to avoid toString() here, especially for concat() NativeString result = new NativeString((CharSequence) val); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.String); return result; } if (cx.getLanguageVersion() >= Context.VERSION_ES6 && val instanceof BigInteger) { NativeBigInt result = new NativeBigInt(((BigInteger) val)); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.BigInt); return result; } if (val instanceof Number) { NativeNumber result = new NativeNumber(((Number) val).doubleValue()); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Number); return result; } if (val instanceof Boolean) { NativeBoolean result = new NativeBoolean(((Boolean) val).booleanValue()); setBuiltinProtoAndParent(result, scope, TopLevel.Builtins.Boolean); return result; } // 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. */ @Deprecated 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. */ @Deprecated 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, scope); if (thisObj == null) { throw undefCallError(null, "function"); } return function.call(cx, scope, thisObj, args); } public static Scriptable newObject( Context cx, Scriptable scope, String constructorName, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = getExistingCtor(cx, scope, constructorName); if (args == null) { args = ScriptRuntime.emptyArgs; } return ctor.construct(cx, scope, args); } public static Scriptable newBuiltinObject( Context cx, Scriptable scope, TopLevel.Builtins type, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = TopLevel.getBuiltinCtor(cx, scope, type); if (args == null) { args = ScriptRuntime.emptyArgs; } return ctor.construct(cx, scope, args); } static Scriptable newNativeError( Context cx, Scriptable scope, TopLevel.NativeErrors type, Object[] args) { scope = ScriptableObject.getTopLevelScope(scope); Function ctor = TopLevel.getNativeErrorCtor(cx, scope, type); 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 (Double.isNaN(d)) return +0.0; if ((d == 0.0) || Double.isInfinite(d)) return d; if (d > 0.0) return Math.floor(d); return Math.ceil(d); } public static double toInteger(Object[] args, int index) { return (index < args.length) ? toInteger(args[index]) : +0.0; } public static long toLength(Object[] args, int index) { double len = toInteger(args, index); if (len <= +0.0) { return 0; } return (long) Math.min(len, NativeNumber.MAX_SAFE_INTEGER); } /** 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) { return DoubleConversion.doubleToInt32(d); } /** * See ECMA 9.6. * * @return long value representing 32 bits unsigned integer */ public static long toUint32(double d) { return DoubleConversion.doubleToInt32(d) & 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); return (char) DoubleConversion.doubleToInt32(d); } // 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.reportRuntimeErrorById("msg.ctor.not.found", constructorName); } throw Context.reportRuntimeErrorById("msg.not.ctor", constructorName); } /** * Return -1L if str is not an index, or the index value as lower 32 bits of the result. Note * that the result needs to be cast to an int in order to produce the actual index, which may be * negative. */ public 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); if (c == '0') return -1L; // "-0" is not an index 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 Integer.valueOf((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 Integer.valueOf(i); } return toString(d); } /** * Helper to return a string or an integer. Always use a null check on s.stringId to determine * if the result is string or integer. * * @see ScriptRuntime#toStringIdOrIndex(Context, Object) */ static final class StringIdOrIndex { final String stringId; final int index; StringIdOrIndex(String stringId) { this.stringId = stringId; this.index = -1; } StringIdOrIndex(int index) { this.stringId = null; this.index = index; } } /** * 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 StringIdOrIndex toStringIdOrIndex(Context cx, Object id) { if (id instanceof Number) { double d = ((Number) id).doubleValue(); int index = (int) d; if (index == d) { return new StringIdOrIndex(index); } return new StringIdOrIndex(toString(id)); } String s; if (id instanceof String) { s = (String) id; } else { s = toString(id); } long indexTest = indexFromString(s); if (indexTest >= 0) { return new StringIdOrIndex((int) indexTest); } return new StringIdOrIndex(s); } /** * Call obj.[[Get]](id) * * @deprecated Use {@link #getObjectElem(Object, Object, Context, Scriptable)} instead */ @Deprecated public static Object getObjectElem(Object obj, Object elem, Context cx) { return getObjectElem(obj, elem, cx, getTopCallScope(cx)); } /** Call obj.[[Get]](id) */ public static Object getObjectElem(Object obj, Object elem, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, elem); } return getObjectElem(sobj, elem, cx); } public static Object getObjectElem(Scriptable obj, Object elem, Context cx) { Object result; if (obj instanceof XMLObject) { result = ((XMLObject) obj).get(cx, elem); } else if (isSymbol(elem)) { result = ScriptableObject.getProperty(obj, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { int index = s.index; result = ScriptableObject.getProperty(obj, index); } else { result = ScriptableObject.getProperty(obj, s.stringId); } } if (result == Scriptable.NOT_FOUND) { result = Undefined.instance; } return result; } /** * Version of getObjectElem when elem is a valid JS identifier name. * * @deprecated Use {@link #getObjectProp(Object, String, Context, Scriptable)} instead */ @Deprecated public static Object getObjectProp(Object obj, String property, Context cx) { return getObjectProp(obj, property, cx, getTopCallScope(cx)); } /** * Version of getObjectElem when elem is a valid JS identifier name. * * @param scope the scope that should be used to resolve primitive prototype */ public static Object getObjectProp(Object obj, String property, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, property); } return getObjectProp(sobj, property, cx); } public static Object getObjectProp(Scriptable obj, String property, Context cx) { Object result = ScriptableObject.getProperty(obj, property); if (result == Scriptable.NOT_FOUND) { if (cx.hasFeature(Context.FEATURE_STRICT_MODE)) { Context.reportWarning( ScriptRuntime.getMessageById("msg.ref.undefined.prop", property)); } result = Undefined.instance; } return result; } /** @deprecated Use {@link #getObjectPropNoWarn(Object, String, Context, Scriptable)} instead */ @Deprecated public static Object getObjectPropNoWarn(Object obj, String property, Context cx) { return getObjectPropNoWarn(obj, property, cx, getTopCallScope(cx)); } public static Object getObjectPropNoWarn( Object obj, String property, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, property); } 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. * * @deprecated Use {@link #getObjectIndex(Object, double, Context, Scriptable)} instead */ @Deprecated public static Object getObjectIndex(Object obj, double dblIndex, Context cx) { return getObjectIndex(obj, dblIndex, cx, getTopCallScope(cx)); } /** 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 scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefReadError(obj, toString(dblIndex)); } int index = (int) dblIndex; if (index == dblIndex) { return getObjectIndex(sobj, index, cx); } String s = toString(dblIndex); return getObjectProp(sobj, s, cx); } public static Object getObjectIndex(Scriptable obj, int index, Context cx) { Object result = ScriptableObject.getProperty(obj, index); if (result == Scriptable.NOT_FOUND) { result = Undefined.instance; } return result; } /** * Call obj.[[Put]](id, value) * * @deprecated Use {@link #setObjectElem(Object, Object, Object, Context, Scriptable)} instead */ @Deprecated public static Object setObjectElem(Object obj, Object elem, Object value, Context cx) { return setObjectElem(obj, elem, value, cx, getTopCallScope(cx)); } /** Call obj.[[Put]](id, value) */ public static Object setObjectElem( Object obj, Object elem, Object value, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); 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) obj).put(cx, elem, value); } else if (isSymbol(elem)) { ScriptableObject.putProperty(obj, (Symbol) elem, value); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { ScriptableObject.putProperty(obj, s.index, value); } else { ScriptableObject.putProperty(obj, s.stringId, value); } } return value; } /** * Version of setObjectElem when elem is a valid JS identifier name. * * @deprecated Use {@link #setObjectProp(Object, String, Object, Context, Scriptable)} instead */ @Deprecated public static Object setObjectProp(Object obj, String property, Object value, Context cx) { return setObjectProp(obj, property, value, cx, getTopCallScope(cx)); } /** Version of setObjectElem when elem is a valid JS identifier name. */ public static Object setObjectProp( Object obj, String property, Object value, Context cx, Scriptable scope) { if (!(obj instanceof Scriptable) && cx.isStrictMode() && cx.getLanguageVersion() >= Context.VERSION_1_8) { throw undefWriteError(obj, property, value); } Scriptable sobj = toObjectOrNull(cx, obj, scope); 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) { ScriptableObject.putProperty(obj, property, value); return value; } /** * A cheaper and less general version of the above for well-known argument types. * * @deprecated Use {@link #setObjectIndex(Object, double, Object, Context, Scriptable)} instead */ @Deprecated public static Object setObjectIndex(Object obj, double dblIndex, Object value, Context cx) { return setObjectIndex(obj, dblIndex, value, cx, getTopCallScope(cx)); } /** 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 scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw undefWriteError(obj, String.valueOf(dblIndex), value); } int index = (int) dblIndex; if (index == dblIndex) { return setObjectIndex(sobj, index, value, cx); } String s = toString(dblIndex); return setObjectProp(sobj, s, value, cx); } public static Object setObjectIndex(Scriptable obj, int index, Object value, Context cx) { ScriptableObject.putProperty(obj, index, value); return value; } public static boolean deleteObjectElem(Scriptable target, Object elem, Context cx) { if (isSymbol(elem)) { SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(target); Symbol s = (Symbol) elem; so.delete(s); return !so.has(s, target); } StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { target.delete(s.index); return !target.has(s.index, target); } target.delete(s.stringId); return !target.has(s.stringId, target); } public static boolean hasObjectElem(Scriptable target, Object elem, Context cx) { boolean result; if (isSymbol(elem)) { result = ScriptableObject.hasProperty(target, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId == null) { result = ScriptableObject.hasProperty(target, s.index); } else { result = ScriptableObject.hasProperty(target, s.stringId); } } return result; } public static Object refGet(Ref ref, Context cx) { return ref.get(cx); } /** @deprecated Use {@link #refSet(Ref, Object, Context, Scriptable)} instead */ @Deprecated public static Object refSet(Ref ref, Object value, Context cx) { return refSet(ref, value, cx, getTopCallScope(cx)); } public static Object refSet(Ref ref, Object value, Context cx, Scriptable scope) { return ref.set(cx, scope, 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__"); } /** @deprecated Use {@link #specialRef(Object, String, Context, Scriptable)} instead */ @Deprecated public static Ref specialRef(Object obj, String specialProperty, Context cx) { return specialRef(obj, specialProperty, cx, getTopCallScope(cx)); } public static Ref specialRef(Object obj, String specialProperty, Context cx, Scriptable scope) { return SpecialRef.createSpecial(cx, scope, obj, specialProperty); } /** @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */ @Deprecated public static Object delete(Object obj, Object id, Context cx) { return delete(obj, id, cx, false); } /** * 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. * * @deprecated Use {@link #delete(Object, Object, Context, Scriptable, boolean)} instead */ @Deprecated public static Object delete(Object obj, Object id, Context cx, boolean isName) { return delete(obj, id, cx, getTopCallScope(cx), isName); } /** * 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 scope, boolean isName) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { if (isName) { return Boolean.TRUE; } throw undefDeleteError(obj, id); } 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.has(name, xmlObj)) { // function this should be the target object of with thisObj = xmlObj; result = xmlObj.get(name, xmlObj); 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.get(name, firstXMLObject); } // 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.has(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) { // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and it's better to optimize the common case 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.getMessageById("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 strictSetName( Scriptable bound, Object value, Context cx, Scriptable scope, String id) { if (bound != null) { // TODO: The LeftHandSide also may not be a reference to a // data property with the attribute value {[[Writable]]:false}, // to an accessor property with the attribute value // {[[Put]]:undefined}, nor to a non-existent property of an // object whose [[Extensible]] internal property has the value // false. In these cases a TypeError exception is thrown (11.13.1). // TODO: we used to special-case XMLObject here, but putProperty // seems to work for E4X and we should optimize the common case ScriptableObject.putProperty(bound, id, value); return value; } // See ES5 8.7.2 String msg = "Assignment to undefined \"" + id + "\" in strict mode"; throw constructError("ReferenceError", msg); } public static Object setConst(Scriptable bound, Object value, Context cx, String id) { if (bound instanceof XMLObject) { bound.put(id, bound, 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; ObjToIntMap used; Object currentId; int index; int enumType; /* one of ENUM_INIT_KEYS, ENUM_INIT_VALUES, ENUM_INIT_ARRAY, ENUMERATE_VALUES_IN_ORDER */ // 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) { if (ScriptableObject.hasProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME)) { Object v = ScriptableObject.getProperty(obj, NativeIterator.ITERATOR_PROPERTY_NAME); if (!(v instanceof Callable)) { throw typeErrorById("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 typeErrorById("msg.iterator.primitive"); } return (Scriptable) v; } return null; } /** * For backwards compatibility with generated class files * * @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead */ @Deprecated 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 final int ENUMERATE_VALUES_IN_ORDER = 6; /** @deprecated Use {@link #enumInit(Object, Context, Scriptable, int)} instead */ @Deprecated public static Object enumInit(Object value, Context cx, int enumType) { return enumInit(value, cx, getTopCallScope(cx), enumType); } public static Object enumInit(Object value, Context cx, Scriptable scope, int enumType) { IdEnumeration x = new IdEnumeration(); x.obj = toObjectOrNull(cx, value, scope); // "for of" loop if (enumType == ENUMERATE_VALUES_IN_ORDER) { x.enumType = enumType; x.iterator = null; return enumInitInOrder(cx, x); } 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, enumType == ScriptRuntime.ENUMERATE_KEYS); } 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; } private static Object enumInitInOrder(Context cx, IdEnumeration x) { if (!(x.obj instanceof SymbolScriptable) || !ScriptableObject.hasProperty(x.obj, SymbolKey.ITERATOR)) { throw typeErrorById("msg.not.iterable", toString(x.obj)); } Object iterator = ScriptableObject.getProperty(x.obj, SymbolKey.ITERATOR); if (!(iterator instanceof Callable)) { throw typeErrorById("msg.not.iterable", toString(x.obj)); } Callable f = (Callable) iterator; Scriptable scope = x.obj.getParentScope(); Object[] args = new Object[] {}; Object v = f.call(cx, scope, x.obj, args); if (!(v instanceof Scriptable)) { throw typeErrorById("msg.not.iterable", toString(x.obj)); } x.iterator = (Scriptable) v; 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) { if (x.enumType == ENUMERATE_VALUES_IN_ORDER) { return enumNextInOrder(x); } Object v = ScriptableObject.getProperty(x.iterator, "next"); if (!(v instanceof Callable)) return Boolean.FALSE; Callable f = (Callable) v; Context cx = Context.getContext(); 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; } } 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 Symbol) { continue; } else 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) (Integer.valueOf(intId)) : String.valueOf(intId); } return Boolean.TRUE; } } private static Boolean enumNextInOrder(IdEnumeration enumObj) { Object v = ScriptableObject.getProperty(enumObj.iterator, ES6Iterator.NEXT_METHOD); if (!(v instanceof Callable)) { throw notFunctionError(enumObj.iterator, ES6Iterator.NEXT_METHOD); } Callable f = (Callable) v; Context cx = Context.getContext(); Scriptable scope = enumObj.iterator.getParentScope(); Object r = f.call(cx, scope, enumObj.iterator, emptyArgs); Scriptable iteratorResult = toObject(cx, scope, r); Object done = ScriptableObject.getProperty(iteratorResult, ES6Iterator.DONE_PROPERTY); if (done != Scriptable.NOT_FOUND && toBoolean(done)) { return Boolean.FALSE; } enumObj.currentId = ScriptableObject.getProperty(iteratorResult, ES6Iterator.VALUE_PROPERTY); 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(ScriptableObject.getTopLevelScope(x.obj), elements); default: throw Kit.codeBug(); } } public static Object enumValue(Object enumObj, Context cx) { IdEnumeration x = (IdEnumeration) enumObj; Object result; if (isSymbol(x.currentId)) { SymbolScriptable so = ScriptableObject.ensureSymbolScriptable(x.obj); result = so.get((Symbol) x.currentId, x.obj); } else { StringIdOrIndex s = toStringIdOrIndex(cx, x.currentId); if (s.stringId == null) { result = x.obj.get(s.index, x.obj); } else { result = x.obj.get(s.stringId, 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; } /** * This is used to handle all the special cases that are required when invoking * Object.fromEntries or constructing a NativeMap or NativeWeakMap from an iterable. * * @param cx the current context * @param scope the current scope * @param arg1 the iterable object. * @param setter the setter to set the value * @return true, if arg1 was iterable. */ public static boolean loadFromIterable( Context cx, Scriptable scope, Object arg1, BiConsumer setter) { if ((arg1 == null) || Undefined.isUndefined(arg1)) return false; // Call the "[Symbol.iterator]" property as a function. final Object ito = ScriptRuntime.callIterator(arg1, cx, scope); if (Undefined.isUndefined(ito)) { // Per spec, ignore if the iterator is undefined return false; } // Finally, run through all the iterated values and add them! try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, ito)) { for (Object val : it) { Scriptable sVal = ScriptableObject.ensureScriptable(val); if (sVal instanceof Symbol) { throw ScriptRuntime.typeErrorById( "msg.arg.not.object", ScriptRuntime.typeof(sVal)); } Object finalKey = sVal.get(0, sVal); if (finalKey == Scriptable.NOT_FOUND) { finalKey = Undefined.instance; } Object finalVal = sVal.get(1, sVal); if (finalVal == Scriptable.NOT_FOUND) { finalVal = Undefined.instance; } setter.accept(finalKey, finalVal); } } return true; } /** * 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); } 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. * * @deprecated Use {@link #getElemFunctionAndThis(Object, Object, Context, Scriptable)} instead */ @Deprecated public static Callable getElemFunctionAndThis(Object obj, Object elem, Context cx) { return getElemFunctionAndThis(obj, elem, cx, getTopCallScope(cx)); } /** * 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, Scriptable scope) { Scriptable thisObj; Object value; if (isSymbol(elem)) { thisObj = toObjectOrNull(cx, obj, scope); if (thisObj == null) { throw undefCallError(obj, String.valueOf(elem)); } value = ScriptableObject.getProperty(thisObj, (Symbol) elem); } else { StringIdOrIndex s = toStringIdOrIndex(cx, elem); if (s.stringId != null) { return getPropFunctionAndThis(obj, s.stringId, cx, scope); } thisObj = toObjectOrNull(cx, obj, scope); if (thisObj == null) { throw undefCallError(obj, String.valueOf(elem)); } value = ScriptableObject.getProperty(thisObj, s.index); } 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. Warning: this doesn't allow to resolve primitive prototype * properly when many top scopes are involved. * * @deprecated Use {@link #getPropFunctionAndThis(Object, String, Context, Scriptable)} instead */ @Deprecated public static Callable getPropFunctionAndThis(Object obj, String property, Context cx) { return getPropFunctionAndThis(obj, property, cx, getTopCallScope(cx)); } /** * 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 scope) { Scriptable thisObj = toObjectOrNull(cx, obj, scope); return getPropFunctionAndThisHelper(obj, property, cx, thisObj); } private static Callable getPropFunctionAndThisHelper( Object obj, String property, Context cx, Scriptable thisObj) { if (thisObj == null) { throw undefCallError(obj, property); } Object value = ScriptableObject.getProperty(thisObj, property); if (!(value instanceof Callable)) { Object noSuchMethod = ScriptableObject.getProperty(thisObj, "__noSuchMethod__"); if (noSuchMethod instanceof Callable) value = new NoSuchMethodShim((Callable) noSuchMethod, property); } if (!(value instanceof Callable)) { 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; } /** * Given an object, get the "Symbol.iterator" element, throw a TypeError if it is not present, * then call the result, (throwing a TypeError if the result is not a function), and return that * result, whatever it is. */ public static Object callIterator(Object obj, Context cx, Scriptable scope) { final Callable getIterator = ScriptRuntime.getElemFunctionAndThis(obj, SymbolKey.ITERATOR, cx, scope); final Scriptable iterable = ScriptRuntime.lastStoredScriptable(cx); return getIterator.call(cx, scope, iterable, ScriptRuntime.emptyArgs); } /** * Given an iterator result, return true if and only if there is a "done" property that's true. */ public static boolean isIteratorDone(Context cx, Object result) { if (!(result instanceof Scriptable)) { return false; } final Object prop = getObjectProp((Scriptable) result, ES6Iterator.DONE_PROPERTY, cx); return toBoolean(prop); } /** * 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 = getMessageById("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 (thisObj.getParentScope() == null && NativeGlobal.isEvalFunction(fun)) { return evalSpecial(cx, scope, callerThis, args, filename, lineNumber); } } else if (callType == Node.SPECIALCALL_WITH) { if (NativeWith.isWithFunction(fun)) { throw Context.reportRuntimeErrorById("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 typeErrorById("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) { if (cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) { callThis = toObjectOrNull(cx, args[0], scope); } else { callThis = args[0] == Undefined.instance ? Undefined.SCRIPTABLE_UNDEFINED : toObjectOrNull(cx, args[0], scope); } } if (callThis == null && cx.hasFeature(Context.FEATURE_OLD_UNDEF_NULL_THIS)) { callThis = getTopCallScope( cx); // This covers the case of args[0] == (null|undefined) as well. } 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); } /** @return true if the passed in Scriptable looks like an array */ private static boolean isArrayLike(Scriptable obj) { return obj != null && (obj instanceof NativeArray || obj instanceof Arguments || ScriptableObject.hasProperty(obj, "length")); } static Object[] getApplyArguments(Context cx, Object arg1) { if (arg1 == null || Undefined.isUndefined(arg1)) { return ScriptRuntime.emptyArgs; } else if (arg1 instanceof Scriptable && isArrayLike((Scriptable) arg1)) { return cx.getElements((Scriptable) arg1); } else if (arg1 instanceof ScriptableObject) { return ScriptRuntime.emptyArgs; } else { throw ScriptRuntime.typeErrorById("msg.arg.isnt.array"); } } static Callable getCallable(Scriptable thisObj) { Callable function; if (thisObj instanceof Callable) { function = (Callable) thisObj; } else if (thisObj == null) { throw ScriptRuntime.notFunctionError(null, null); } 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 CharSequence)) { if (cx.hasFeature(Context.FEATURE_STRICT_MODE) || cx.hasFeature(Context.FEATURE_STRICT_EVAL)) { throw Context.reportRuntimeErrorById("msg.eval.nonstring.strict"); } String message = ScriptRuntime.getMessageById("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(x.toString(), 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 Delegator) return typeof(((Delegator) value).getDelegee()); if (value instanceof ScriptableObject) return ((ScriptableObject) value).getTypeOf(); if (value instanceof Scriptable) return (value instanceof Callable) ? "function" : "object"; if (value instanceof CharSequence) return "string"; if (value instanceof BigInteger) return "bigint"; 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)); } public static boolean isObject(Object value) { if (value == null) { return false; } if (Undefined.isUndefined(value)) { return false; } if (value instanceof ScriptableObject) { String type = ((ScriptableObject) value).getTypeOf(); return "object".equals(type) || "function".equals(type); } if (value instanceof Scriptable) { return (!(value instanceof Callable)); } return false; } // 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 BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).add((BigInteger) val2); } if ((val1 instanceof Number && val2 instanceof BigInteger) || (val1 instanceof BigInteger && val2 instanceof Number)) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } if (val1 instanceof Number && val2 instanceof Number) { return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue()); } if (val1 instanceof CharSequence && val2 instanceof CharSequence) { // If we let this happen later, then the "getDefaultValue" logic // undoes many optimizations return new ConsString((CharSequence) val1, (CharSequence) val2); } 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 Symbol) || (val2 instanceof Symbol)) { throw typeErrorById("msg.not.a.number"); } if (val1 instanceof Scriptable) val1 = ((Scriptable) val1).getDefaultValue(null); if (val2 instanceof Scriptable) val2 = ((Scriptable) val2).getDefaultValue(null); if (!(val1 instanceof CharSequence) && !(val2 instanceof CharSequence)) { Number num1 = val1 instanceof Number ? (Number) val1 : toNumeric(val1); Number num2 = val2 instanceof Number ? (Number) val2 : toNumeric(val2); if (num1 instanceof BigInteger && num2 instanceof BigInteger) { return ((BigInteger) num1).add((BigInteger) num2); } if (num1 instanceof BigInteger || num2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } return num1.doubleValue() + num2.doubleValue(); } return new ConsString(toCharSequence(val1), toCharSequence(val2)); } /** * https://262.ecma-international.org/11.0/#sec-addition-operator-plus 5. Let lprim be ? * ToPrimitive(lval). 7. If Type(lprim) is String or Type(rprim) is String, then a. Let lstr be * ? ToString(lprim). * *

Should call toPrimitive before toCharSequence * * @deprecated Use {@link #add(Object, Object, Context)} instead */ @Deprecated public static CharSequence add(CharSequence val1, Object val2) { return new ConsString(val1, toCharSequence(val2)); } /** * https://262.ecma-international.org/11.0/#sec-addition-operator-plus 6. Let rprim be ? * ToPrimitive(rval). 7. If Type(lprim) is String or Type(rprim) is String, then b. Let rstr be * ? ToString(rprim). * *

Should call toPrimitive before toCharSequence * * @deprecated Use {@link #add(Object, Object, Context)} instead */ @Deprecated public static CharSequence add(Object val1, CharSequence val2) { return new ConsString(toCharSequence(val1), val2); } public static Number subtract(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).subtract((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { return val1.doubleValue() - val2.doubleValue(); } } public static Number multiply(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).multiply((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { return val1.doubleValue() * val2.doubleValue(); } } public static Number divide(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { if (val2.equals(BigInteger.ZERO)) { throw ScriptRuntime.rangeErrorById("msg.division.zero"); } return ((BigInteger) val1).divide((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { return val1.doubleValue() / val2.doubleValue(); } } public static Number remainder(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { if (val2.equals(BigInteger.ZERO)) { throw ScriptRuntime.rangeErrorById("msg.division.zero"); } return ((BigInteger) val1).remainder((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { return val1.doubleValue() % val2.doubleValue(); } } public static Number exponentiate(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { if (((BigInteger) val2).signum() == -1) { throw ScriptRuntime.rangeErrorById("msg.bigint.negative.exponent"); } try { int intVal2 = ((BigInteger) val2).intValueExact(); return ((BigInteger) val1).pow(intVal2); } catch (ArithmeticException e) { // This is outside the scope of the ECMA262 specification. throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic"); } } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { return Math.pow(val1.doubleValue(), val2.doubleValue()); } } public static Number bitwiseAND(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).and((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { int result = toInt32(val1.doubleValue()) & toInt32(val2.doubleValue()); return Double.valueOf(result); } } public static Number bitwiseOR(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).or((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { int result = toInt32(val1.doubleValue()) | toInt32(val2.doubleValue()); return Double.valueOf(result); } } public static Number bitwiseXOR(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return ((BigInteger) val1).xor((BigInteger) val2); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { int result = toInt32(val1.doubleValue()) ^ toInt32(val2.doubleValue()); return Double.valueOf(result); } } public static Number leftShift(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { try { int intVal2 = ((BigInteger) val2).intValueExact(); return ((BigInteger) val1).shiftLeft(intVal2); } catch (ArithmeticException e) { // This is outside the scope of the ECMA262 specification. throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic"); } } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { int result = toInt32(val1.doubleValue()) << toInt32(val2.doubleValue()); return Double.valueOf(result); } } public static Number signedRightShift(Number val1, Number val2) { if (val1 instanceof BigInteger && val2 instanceof BigInteger) { try { int intVal2 = ((BigInteger) val2).intValueExact(); return ((BigInteger) val1).shiftRight(intVal2); } catch (ArithmeticException e) { // This is outside the scope of the ECMA262 specification. throw ScriptRuntime.rangeErrorById("msg.bigint.out.of.range.arithmetic"); } } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt"); } else { int result = toInt32(val1.doubleValue()) >> toInt32(val2.doubleValue()); return Double.valueOf(result); } } public static Number bitwiseNOT(Number val) { if (val instanceof BigInteger) { return ((BigInteger) val).not(); } else { int result = ~toInt32(val.doubleValue()); return Double.valueOf(result); } } /** * The method is only present for compatibility. * * @deprecated Use {@link #nameIncrDecr(Scriptable, String, Context, int)} instead */ @Deprecated 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 { if (target instanceof NativeWith && target.getPrototype() instanceof XMLObject) { break; } 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(null, id); } return doScriptableIncrDecr(target, id, scopeChain, value, incrDecrMask); } /** @deprecated Use {@link #propIncrDecr(Object, String, Context, Scriptable, int)} instead */ @Deprecated public static Object propIncrDecr(Object obj, String id, Context cx, int incrDecrMask) { return propIncrDecr(obj, id, cx, getTopCallScope(cx), incrDecrMask); } public static Object propIncrDecr( Object obj, String id, Context cx, Scriptable scope, int incrDecrMask) { Scriptable start = toObjectOrNull(cx, obj, scope); 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) { final boolean post = (incrDecrMask & Node.POST_FLAG) != 0; Number number; if (value instanceof Number) { number = (Number) value; } else { number = toNumeric(value); } Number result; if (number instanceof BigInteger) { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = ((BigInteger) number).add(BigInteger.ONE); } else { result = ((BigInteger) number).subtract(BigInteger.ONE); } } else { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = number.doubleValue() + 1.0; } else { result = number.doubleValue() - 1.0; } } target.put(id, protoChainStart, result); if (post) { return number; } return result; } /** @deprecated Use {@link #elemIncrDecr(Object, Object, Context, Scriptable, int)} instead */ @Deprecated public static Object elemIncrDecr(Object obj, Object index, Context cx, int incrDecrMask) { return elemIncrDecr(obj, index, cx, getTopCallScope(cx), incrDecrMask); } public static Object elemIncrDecr( Object obj, Object index, Context cx, Scriptable scope, int incrDecrMask) { Object value = getObjectElem(obj, index, cx, scope); final boolean post = (incrDecrMask & Node.POST_FLAG) != 0; Number number; if (value instanceof Number) { number = (Number) value; } else { number = toNumeric(value); } Number result; if (number instanceof BigInteger) { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = ((BigInteger) number).add(BigInteger.ONE); } else { result = ((BigInteger) number).subtract(BigInteger.ONE); } } else { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = number.doubleValue() + 1.0; } else { result = number.doubleValue() - 1.0; } } setObjectElem(obj, index, result, cx, scope); if (post) { return number; } return result; } /** @deprecated Use {@link #refIncrDecr(Ref, Context, Scriptable, int)} instead */ @Deprecated public static Object refIncrDecr(Ref ref, Context cx, int incrDecrMask) { return refIncrDecr(ref, cx, getTopCallScope(cx), incrDecrMask); } public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope, int incrDecrMask) { Object value = ref.get(cx); boolean post = ((incrDecrMask & Node.POST_FLAG) != 0); Number number; if (value instanceof Number) { number = (Number) value; } else { number = toNumeric(value); } Number result; if (number instanceof BigInteger) { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = ((BigInteger) number).add(BigInteger.ONE); } else { result = ((BigInteger) number).subtract(BigInteger.ONE); } } else { if ((incrDecrMask & Node.DECR_FLAG) == 0) { result = number.doubleValue() + 1.0; } else { result = number.doubleValue() - 1.0; } } ref.set(cx, scope, result); if (post) { return number; } return result; } public static Number negate(Number val) { if (val instanceof BigInteger) { return ((BigInteger) val).negate(); } return -val.doubleValue(); } public static Object toPrimitive(Object val) { return toPrimitive(val, null); } public static Object toPrimitive(Object val, Class typeHint) { if (!(val instanceof Scriptable)) { return val; } Scriptable s = (Scriptable) val; Object result = s.getDefaultValue(typeHint); if ((result instanceof Scriptable) && !isSymbol(result)) throw typeErrorById("msg.bad.default.value"); return result; } /** * Equality * *

See ECMA 11.9 */ public static boolean eq(Object x, Object y) { if (x == null || Undefined.isUndefined(x)) { if (y == null || Undefined.isUndefined(y)) { 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 BigInteger) { return eqBigInt((BigInteger) x, y); } else if (x instanceof Number) { return eqNumber(((Number) x).doubleValue(), y); } else if (x == y) { return true; } else if (x instanceof CharSequence) { return eqString((CharSequence) 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 (x instanceof Delegator) { x = ((Delegator) x).getDelegee(); if (y instanceof Delegator) { return eq(x, ((Delegator) y).getDelegee()); } if (x == y) { return true; } } if (y instanceof Delegator && ((Delegator) y).getDelegee() == x) { return true; } if (y instanceof Scriptable) { 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 BigInteger) { return eqBigInt((BigInteger) y, x); } else if (y instanceof Number) { return eqNumber(((Number) y).doubleValue(), x); } else if (y instanceof CharSequence) { return eqString((CharSequence) y, x); } // covers the case when y == Undefined.instance as well return false; } else { warnAboutNonJSObject(x); return x == y; } } /* * Implement "SameValue" as in ECMA 7.2.9. This is not the same as "eq" because it handles * signed zeroes and NaNs differently. */ public static boolean same(Object x, Object y) { if (!typeof(x).equals(typeof(y))) { return false; } if (x instanceof Number) { if (isNaN(x) && isNaN(y)) { return true; } return x.equals(y); } return eq(x, y); } /** Implement "SameValueZero" from ECMA 7.2.9 */ public static boolean sameZero(Object x, Object y) { if (!typeof(x).equals(typeof(y))) { return false; } if (x instanceof BigInteger) { return x.equals(y); } if (x instanceof Number) { if (isNaN(x) && isNaN(y)) { return true; } final double dx = ((Number) x).doubleValue(); if (y instanceof Number) { final double dy = ((Number) y).doubleValue(); if (((dx == negativeZero) && (dy == 0.0)) || ((dx == 0.0) && dy == negativeZero)) { return true; } } return eqNumber(dx, y); } return eq(x, y); } public static boolean isNaN(Object n) { if (n instanceof Double) { return ((Double) n).isNaN(); } if (n instanceof Float) { return ((Float) n).isNaN(); } return false; } public static boolean isPrimitive(Object obj) { return obj == null || Undefined.isUndefined(obj) || (obj instanceof Number) || (obj instanceof String) || (obj instanceof Boolean); } static boolean eqNumber(double x, Object y) { for (; ; ) { if (y == null || Undefined.isUndefined(y)) { return false; } else if (y instanceof BigInteger) { return eqBigInt((BigInteger) y, x); } else if (y instanceof Number) { return x == ((Number) y).doubleValue(); } else if (y instanceof CharSequence) { return x == toNumber(y); } else if (y instanceof Boolean) { return x == (((Boolean) y).booleanValue() ? 1.0 : +0.0); } else if (isSymbol(y)) { return false; } 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; } } } static boolean eqBigInt(BigInteger x, Object y) { for (; ; ) { if (y == null || Undefined.isUndefined(y)) { return false; } else if (y instanceof BigInteger) { return x.equals(y); } else if (y instanceof Number) { return eqBigInt(x, ((Number) y).doubleValue()); } else if (y instanceof CharSequence) { BigInteger biy; try { biy = toBigInt(y); } catch (EcmaError e) { return false; } return x.equals(biy); } else if (y instanceof Boolean) { BigInteger biy = ((Boolean) y).booleanValue() ? BigInteger.ONE : BigInteger.ZERO; return x.equals(biy); } else if (isSymbol(y)) { return false; } 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); } else { warnAboutNonJSObject(y); return false; } } } private static boolean eqBigInt(BigInteger x, double y) { if (Double.isNaN(y) || Double.isInfinite(y)) { return false; } double d = Math.ceil(y); if (d != y) { return false; } BigDecimal bdx = new BigDecimal(x); BigDecimal bdy = new BigDecimal(d, MathContext.UNLIMITED); return bdx.compareTo(bdy) == 0; } private static boolean eqString(CharSequence x, Object y) { for (; ; ) { if (y == null || Undefined.isUndefined(y)) { return false; } else if (y instanceof CharSequence) { CharSequence c = (CharSequence) y; return x.length() == c.length() && x.toString().equals(c.toString()); } else if (y instanceof BigInteger) { BigInteger bix; try { bix = toBigInt(x); } catch (EcmaError e) { return false; } return bix.equals(y); } else if (y instanceof Number) { return toNumber(x.toString()) == ((Number) y).doubleValue(); } else if (y instanceof Boolean) { return toNumber(x.toString()) == (((Boolean) y).booleanValue() ? 1.0 : 0.0); } else if (isSymbol(y)) { return false; } else if (y instanceof Scriptable) { if (y instanceof ScriptableObject) { Object test = ((ScriptableObject) y).equivalentValues(x.toString()); 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 !Double.isNaN(d); } if (x == null || x == Undefined.instance || x == Undefined.SCRIPTABLE_UNDEFINED) { if ((x == Undefined.instance && y == Undefined.SCRIPTABLE_UNDEFINED) || (x == Undefined.SCRIPTABLE_UNDEFINED && y == Undefined.instance)) return true; return false; } else if (x instanceof BigInteger) { if (y instanceof BigInteger) { return x.equals(y); } } else if (x instanceof Number && !(x instanceof BigInteger)) { if (y instanceof Number && !(y instanceof BigInteger)) { return ((Number) x).doubleValue() == ((Number) y).doubleValue(); } } else if (x instanceof CharSequence) { if (y instanceof CharSequence) { return x.toString().equals(y.toString()); } } 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(); } if (x instanceof Delegator) { x = ((Delegator) x).getDelegee(); if (y instanceof Delegator) { return shallowEq(x, ((Delegator) y).getDelegee()); } if (x == y) { return true; } } if (y instanceof Delegator && ((Delegator) y).getDelegee() == x) { return true; } } 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 typeErrorById("msg.instanceof.not.object"); } // for primitive values on LHS, return false 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:
* - it doesn't perform ToObject on the right hand side
* - 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 typeErrorById("msg.in.not.object"); } return hasObjectElem((Scriptable) b, a, cx); } public static boolean compare(Object val1, Object val2, int op) { assert op == Token.GE || op == Token.LE || op == Token.GT || op == Token.LT; if (val1 instanceof Number && val2 instanceof Number) { return compare((Number) val1, (Number) val2, op); } else { if ((val1 instanceof Symbol) || (val2 instanceof Symbol)) { throw typeErrorById("msg.compare.symbol"); } if (val1 instanceof Scriptable) { val1 = ((Scriptable) val1).getDefaultValue(NumberClass); } if (val2 instanceof Scriptable) { val2 = ((Scriptable) val2).getDefaultValue(NumberClass); } if (val1 instanceof CharSequence && val2 instanceof CharSequence) { return compareTo(val1.toString(), val2.toString(), op); } return compare(toNumeric(val1), toNumeric(val2), op); } } public static boolean compare(Number val1, Number val2, int op) { assert op == Token.GE || op == Token.LE || op == Token.GT || op == Token.LT; if (val1 instanceof BigInteger && val2 instanceof BigInteger) { return compareTo((BigInteger) val1, (BigInteger) val2, op); } else if (val1 instanceof BigInteger || val2 instanceof BigInteger) { BigDecimal bd1; if (val1 instanceof BigInteger) { bd1 = new BigDecimal((BigInteger) val1); } else { double d = val1.doubleValue(); if (Double.isNaN(d)) { return false; } else if (d == Double.POSITIVE_INFINITY) { return op == Token.GE || op == Token.GT; } else if (d == Double.NEGATIVE_INFINITY) { return op == Token.LE || op == Token.LT; } bd1 = new BigDecimal(d, MathContext.UNLIMITED); } BigDecimal bd2; if (val2 instanceof BigInteger) { bd2 = new BigDecimal((BigInteger) val2); } else { double d = val2.doubleValue(); if (Double.isNaN(d)) { return false; } else if (d == Double.POSITIVE_INFINITY) { return op == Token.LE || op == Token.LT; } else if (d == Double.NEGATIVE_INFINITY) { return op == Token.GE || op == Token.GT; } bd2 = new BigDecimal(d, MathContext.UNLIMITED); } return compareTo(bd1, bd2, op); } return compareTo(val1.doubleValue(), val2.doubleValue(), op); } private static boolean compareTo(Comparable val1, T val2, int op) { switch (op) { case Token.GE: return val1.compareTo(val2) >= 0; case Token.LE: return val1.compareTo(val2) <= 0; case Token.GT: return val1.compareTo(val2) > 0; case Token.LT: return val1.compareTo(val2) < 0; default: throw Kit.codeBug(); } } private static boolean compareTo(double d1, double d2, int op) { switch (op) { case Token.GE: return d1 >= d2; case Token.LE: return d1 <= d2; case Token.GT: return d1 > d2; case Token.LT: return d1 < d2; default: throw Kit.codeBug(); } } // ------------------ // 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 (RuntimeException e) { throw e; } 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; } /** * @deprecated Use {@link #doTopCall(Callable, Context, Scriptable, Scriptable, Object[], * boolean)} instead */ @Deprecated public static Object doTopCall( Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { return doTopCall(callable, cx, scope, thisObj, args, cx.isTopLevelStrict); } public static Object doTopCall( Callable callable, Context cx, Scriptable scope, Scriptable thisObj, Object[] args, boolean isTopLevelStrict) { 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); boolean previousTopLevelStrict = cx.isTopLevelStrict; cx.isTopLevelStrict = isTopLevelStrict; ContextFactory f = cx.getFactory(); try { result = f.doTopCall(callable, cx, scope, thisObj, args); } finally { cx.topCallScope = null; // Cleanup cached references cx.cachedXMLLib = null; cx.isTopLevelStrict = previousTopLevelStrict; if (cx.currentActivationCall != null) { // Function should always call exitActivationFunction // if it creates activation record throw new IllegalStateException(); } } return result; } /** * Return possibleDynamicScope if staticTopScope is present on its * prototype chain and return staticTopScope otherwise. Should only be called when * staticTopScope 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 (isConst) { ScriptableObject.defineConstProperty(varScope, name); } else if (!evalScript) { if (!(funObj instanceof InterpretedFunction) || ((InterpretedFunction) funObj).hasFunctionNamed(name)) { // Global var definitions are supposed to be DONTDELETE ScriptableObject.defineProperty( varScope, name, Undefined.instance, ScriptableObject.PERMANENT); } } else { varScope.put(name, varScope, Undefined.instance); } } else { ScriptableObject.redefineProperty(scope, name, isConst); } } } } /** * @deprecated Use {@link #createFunctionActivation(NativeFunction, Scriptable, Object[], * boolean)} instead */ @Deprecated public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args) { return createFunctionActivation(funObj, scope, args, false); } public static Scriptable createFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall(funObj, scope, args, false, isStrict); } public static Scriptable createArrowFunctionActivation( NativeFunction funObj, Scriptable scope, Object[] args, boolean isStrict) { return new NativeCall(funObj, scope, args, true, isStrict); } 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; call.defineAttributesForArguments(); } 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; TopLevel.NativeErrors type; String errorMsg; Throwable javaException = null; if (t instanceof EcmaError) { EcmaError ee = (EcmaError) t; re = ee; type = TopLevel.NativeErrors.valueOf(ee.getName()); errorMsg = ee.getErrorMessage(); } else if (t instanceof WrappedException) { WrappedException we = (WrappedException) t; re = we; javaException = we.getWrappedException(); if (!isVisible(cx, javaException)) { type = TopLevel.NativeErrors.InternalError; errorMsg = javaException.getMessage(); } else { type = TopLevel.NativeErrors.JavaException; errorMsg = javaException.getClass().getName() + ": " + javaException.getMessage(); } } else if (t instanceof EvaluatorException) { // Pure evaluator exception, nor WrappedException instance EvaluatorException ee = (EvaluatorException) t; re = ee; type = TopLevel.NativeErrors.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); type = TopLevel.NativeErrors.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, Integer.valueOf(line)}; } else { args = new Object[] {errorMsg, sourceUri}; } Scriptable errorObject = newNativeError(cx, scope, type, args); // set exception in Error objects to enable non-ECMA "stack" property if (errorObject instanceof NativeError) { ((NativeError) errorObject).setStackProvider(re); } if (javaException != null && isVisible(cx, javaException)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null); ScriptableObject.defineProperty( errorObject, "javaException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); } if (isVisible(cx, re)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); ScriptableObject.defineProperty( errorObject, "rhinoException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); } obj = errorObject; } NativeObject catchScopeObject = new NativeObject(); // See ECMA 12.4 catchScopeObject.defineProperty(exceptionName, obj, ScriptableObject.PERMANENT); if (isVisible(cx, t)) { // 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.) catchScopeObject.defineProperty( "__exception__", Context.javaToJS(t, scope), ScriptableObject.PERMANENT | ScriptableObject.DONTENUM); } if (cacheObj) { catchScopeObject.associateValue(t, obj); } return catchScopeObject; } public static Scriptable wrapException(Throwable t, Scriptable scope, Context cx) { 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, Integer.valueOf(line)}; } else { args = new Object[] {errorMsg, sourceUri}; } Scriptable errorObject = cx.newObject(scope, errorName, args); ScriptableObject.putProperty(errorObject, "name", errorName); // set exception in Error objects to enable non-ECMA "stack" property if (errorObject instanceof NativeError) { ((NativeError) errorObject).setStackProvider(re); } if (javaException != null && isVisible(cx, javaException)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, javaException, null); ScriptableObject.defineProperty( errorObject, "javaException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); } if (isVisible(cx, re)) { Object wrap = cx.getWrapFactory().wrap(cx, scope, re, null); ScriptableObject.defineProperty( errorObject, "rhinoException", wrap, ScriptableObject.PERMANENT | ScriptableObject.READONLY | ScriptableObject.DONTENUM); } return errorObject; } private static boolean isVisible(Context cx, Object obj) { ClassShutter shutter = cx.getClassShutter(); return shutter == null || shutter.visibleToScripts(obj.getClass().getName()); } public static Scriptable enterWith(Object obj, Context cx, Scriptable scope) { Scriptable sobj = toObjectOrNull(cx, obj, scope); if (sobj == null) { throw typeErrorById("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) { setFunctionProtoAndParent(fn, scope, false); } public static void setFunctionProtoAndParent( BaseFunction fn, Scriptable scope, boolean es6GeneratorFunction) { fn.setParentScope(scope); if (es6GeneratorFunction) { fn.setPrototype(ScriptableObject.getGeneratorFunctionPrototype(scope)); } else { 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.getTopLevelScope(scope); object.setParentScope(scope); Scriptable proto = ScriptableObject.getClassPrototype(scope, object.getClassName()); object.setPrototype(proto); } public static void setBuiltinProtoAndParent( ScriptableObject object, Scriptable scope, TopLevel.Builtins type) { scope = ScriptableObject.getTopLevelScope(scope); object.setParentScope(scope); object.setPrototype(TopLevel.getBuiltinPrototype(scope, type)); } 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.newArray(scope, sparse); } Scriptable array = cx.newArray(scope, length); int skip = 0; for (int i = 0, j = 0; i != length; ++i) { if (skip != skipCount && skipIndices[skip] == i) { ++skip; continue; } array.put(i, array, objects[j]); ++j; } return array; } /** * 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. * This method only present for compatibility. * * @deprecated Use {@link #newObjectLiteral(Object[], Object[], int[], Context, Scriptable)} * instead */ @Deprecated public static Scriptable newObjectLiteral( Object[] propertyIds, Object[] propertyValues, Context cx, Scriptable scope) { // Passing null for getterSetters means no getters or setters return newObjectLiteral(propertyIds, propertyValues, null, cx, scope); } public static Scriptable newObjectLiteral( Object[] propertyIds, Object[] propertyValues, int[] getterSetters, Context cx, Scriptable scope) { Scriptable object = cx.newObject(scope); int end = propertyIds == null ? 0 : propertyIds.length; for (int i = 0; i != end; ++i) { Object id = propertyIds[i]; // -1 for property getter, 1 for property setter, 0 for a regular value property int getterSetter = getterSetters == null ? 0 : getterSetters[i]; Object value = propertyValues[i]; if (getterSetter == 0) { if (id instanceof Symbol) { Symbol sym = (Symbol) id; SymbolScriptable so = (SymbolScriptable) object; so.put(sym, object, value); } else if (id instanceof Integer) { int index = ((Integer) id).intValue(); object.put(index, object, value); } else { String stringId = ScriptRuntime.toString(id); if (isSpecialProperty(stringId)) { Ref ref = specialRef(object, stringId, cx, scope); ref.set(cx, scope, value); } else { object.put(stringId, object, value); } } } else { ScriptableObject so = (ScriptableObject) object; Callable getterOrSetter = (Callable) value; boolean isSetter = getterSetter == 1; // XXX: Do we have to handle Symbol here. // This will be required, when conputedprops are supported. String key = id instanceof String ? (String) id : null; int index = key == null ? ((Integer) id).intValue() : 0; so.setGetterOrSetter(key, index, getterOrSetter, isSetter); } } 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; } 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 = getMessageById("msg.deprec.ctor", name); if (version == Context.VERSION_DEFAULT) Context.reportWarning(msg); else throw Context.reportRuntimeError(msg); } } /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage0(String messageId) { return getMessage(messageId, null); } /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage1(String messageId, Object arg1) { Object[] arguments = {arg1}; return getMessage(messageId, arguments); } /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage2(String messageId, Object arg1, Object arg2) { Object[] arguments = {arg1, arg2}; return getMessage(messageId, arguments); } /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage3(String messageId, Object arg1, Object arg2, Object arg3) { Object[] arguments = {arg1, arg2, arg3}; return getMessage(messageId, arguments); } /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage4( String messageId, Object arg1, Object arg2, Object arg3, Object arg4) { Object[] arguments = {arg1, arg2, arg3, arg4}; return getMessage(messageId, arguments); } /** * This is an interface defining a message provider. Create your own implementation to override * the default error message provider. * * @author Mike Harm */ public interface MessageProvider { /** * Returns a textual message identified by the given messageId, parameterized by the given * arguments. * * @param messageId the identifier of the message * @param arguments the arguments to fill into the message */ String getMessage(String messageId, Object[] arguments); } public static final MessageProvider messageProvider = new DefaultMessageProvider(); /** @deprecated Use {@link #getMessageById(String messageId, Object... args)} instead */ @Deprecated public static String getMessage(String messageId, Object[] arguments) { return messageProvider.getMessage(messageId, arguments); } public static String getMessageById(String messageId, Object... args) { return messageProvider.getMessage(messageId, args); } /* 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. */ private static class DefaultMessageProvider implements MessageProvider { @Override public 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 caching. 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 rangeError(String message) { return constructError("RangeError", message); } public static EcmaError rangeErrorById(String messageId, Object... args) { String msg = getMessageById(messageId, args); return rangeError(msg); } public static EcmaError typeError(String message) { return constructError("TypeError", message); } public static EcmaError typeErrorById(String messageId, Object... args) { String msg = getMessageById(messageId, args); return typeError(msg); } /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated public static EcmaError typeError0(String messageId) { String msg = getMessage0(messageId); return typeError(msg); } /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated public static EcmaError typeError1(String messageId, Object arg1) { String msg = getMessage1(messageId, arg1); return typeError(msg); } /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated public static EcmaError typeError2(String messageId, Object arg1, Object arg2) { String msg = getMessage2(messageId, arg1, arg2); return typeError(msg); } /** @deprecated Use {@link #typeErrorById(String messageId, Object... args)} instead */ @Deprecated 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) { return typeErrorById("msg.undef.prop.read", toString(object), toString(id)); } public static RuntimeException undefCallError(Object object, Object id) { return typeErrorById("msg.undef.method.call", toString(object), toString(id)); } public static RuntimeException undefWriteError(Object object, Object id, Object value) { return typeErrorById( "msg.undef.prop.write", toString(object), toString(id), toString(value)); } private static RuntimeException undefDeleteError(Object object, Object id) { throw typeErrorById("msg.undef.prop.delete", toString(object), toString(id)); } public static RuntimeException notFoundError(Scriptable object, String property) { // XXX: use object to improve the error message String msg = getMessageById("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 typeErrorById("msg.function.not.found", msg); } return typeErrorById("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 (obj instanceof NativeFunction) { // Omit function body in string representations of functions int paren = objString.indexOf(')'); int curly = objString.indexOf('{', paren); if (curly > -1) { objString = objString.substring(0, curly + 1) + "...}"; } } if (value == Scriptable.NOT_FOUND) { return typeErrorById("msg.function.not.found.in", propertyName, objString); } return typeErrorById("msg.isnt.function.in", propertyName, objString, typeof(value)); } private static RuntimeException notXmlError(Object value) { throw typeErrorById("msg.isnt.xml.object", toString(value)); } public static EcmaError syntaxError(String message) { return constructError("SyntaxError", message); } public static EcmaError syntaxErrorById(String messageId, Object... args) { String msg = getMessageById(messageId, args); return syntaxError(msg); } private static void warnAboutNonJSObject(Object nonJSObject) { final String omitParam = ScriptRuntime.getMessageById("params.omit.non.js.object.warning"); if (!"true".equals(omitParam)) { String message = ScriptRuntime.getMessageById( "msg.non.js.object.warning", nonJSObject, nonJSObject.getClass().getName()); 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.reportRuntimeErrorById("msg.no.regexp"); } return result; } public static Scriptable wrapRegExp(Context cx, Scriptable scope, Object compiled) { return cx.getRegExpProxy().wrapRegExp(cx, scope, compiled); } public static Scriptable getTemplateLiteralCallSite( Context cx, Scriptable scope, Object[] strings, int index) { Object callsite = strings[index]; if (callsite instanceof Scriptable) return (Scriptable) callsite; assert callsite instanceof String[]; String[] vals = (String[]) callsite; assert (vals.length & 1) == 0; ScriptableObject siteObj = (ScriptableObject) cx.newArray(scope, vals.length >>> 1); ScriptableObject rawObj = (ScriptableObject) cx.newArray(scope, vals.length >>> 1); siteObj.put("raw", siteObj, rawObj); siteObj.setAttributes("raw", ScriptableObject.DONTENUM); for (int i = 0, n = vals.length; i < n; i += 2) { int idx = i >>> 1; siteObj.put(idx, siteObj, (vals[i] == null ? Undefined.instance : vals[i])); rawObj.put(idx, rawObj, vals[i + 1]); } AbstractEcmaObjectOperations.setIntegrityLevel( cx, rawObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); AbstractEcmaObjectOperations.setIntegrityLevel( cx, siteObj, AbstractEcmaObjectOperations.INTEGRITY_LEVEL.FROZEN); strings[index] = siteObj; return siteObj; } 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); } 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 previously 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)"; } 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; } /** * Not all "NativeSymbol" instances are actually symbols. So account for that here rather than * just by using an "instanceof" check. */ static boolean isSymbol(Object obj) { return (((obj instanceof NativeSymbol) && ((NativeSymbol) obj).isSymbol())) || (obj instanceof SymbolKey); } private static RuntimeException errorWithClassName(String msg, Object val) { return Context.reportRuntimeErrorById(msg, val.getClass().getName()); } /** * Equivalent to executing "new Error(message, sourceFileName, sourceLineNo)" from JavaScript. * * @param cx the current context * @param scope the current scope * @param message the message * @return a JavaScriptException you should throw */ public static JavaScriptException throwError(Context cx, Scriptable scope, String message) { int[] linep = {0}; String filename = Context.getSourcePositionFromStack(linep); final Scriptable error = newBuiltinObject( cx, scope, TopLevel.Builtins.Error, new Object[] {message, filename, Integer.valueOf(linep[0])}); return new JavaScriptException(error, filename, linep[0]); } /** * Equivalent to executing "new $constructorName(message, sourceFileName, sourceLineNo)" from * JavaScript. * * @param cx the current context * @param scope the current scope * @param message the message * @return a JavaScriptException you should throw */ public static JavaScriptException throwCustomError( Context cx, Scriptable scope, String constructorName, String message) { int[] linep = {0}; String filename = Context.getSourcePositionFromStack(linep); final Scriptable error = cx.newObject( scope, constructorName, new Object[] {message, filename, Integer.valueOf(linep[0])}); return new JavaScriptException(error, filename, linep[0]); } public static final Object[] emptyArgs = new Object[0]; public static final String[] emptyStrings = new String[0]; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy