JSType.java revision 953:221a84ef44c0
11638Srgrimes/*
21638Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
31638Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41638Srgrimes *
51638Srgrimes * This code is free software; you can redistribute it and/or modify it
61638Srgrimes * under the terms of the GNU General Public License version 2 only, as
71638Srgrimes * published by the Free Software Foundation.  Oracle designates this
81638Srgrimes * particular file as subject to the "Classpath" exception as provided
91638Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101638Srgrimes *
111638Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121638Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131638Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141638Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151638Srgrimes * accompanied this code).
161638Srgrimes *
171638Srgrimes * You should have received a copy of the GNU General Public License version
181638Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191638Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201638Srgrimes *
211638Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221638Srgrimes * or visit www.oracle.com if you need additional information or have any
231638Srgrimes * questions.
241638Srgrimes */
251638Srgrimes
261638Srgrimespackage jdk.nashorn.internal.runtime;
271638Srgrimes
281638Srgrimesimport static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
291638Srgrimesimport static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
301638Srgrimesimport static jdk.nashorn.internal.lookup.Lookup.MH;
311638Srgrimesimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
321638Srgrimes
3320218Sacheimport java.lang.invoke.MethodHandle;
341638Srgrimesimport java.lang.invoke.MethodHandles;
351638Srgrimesimport java.lang.reflect.Array;
361638Srgrimesimport java.util.Arrays;
371638Srgrimesimport java.util.Collections;
381638Srgrimesimport java.util.Deque;
391638Srgrimesimport java.util.List;
401638Srgrimesimport jdk.internal.dynalink.beans.StaticClass;
411638Srgrimesimport jdk.nashorn.api.scripting.JSObject;
421638Srgrimesimport jdk.nashorn.internal.codegen.CompilerConstants.Call;
431638Srgrimesimport jdk.nashorn.internal.codegen.types.Type;
441638Srgrimesimport jdk.nashorn.internal.objects.Global;
451638Srgrimesimport jdk.nashorn.internal.parser.Lexer;
461638Srgrimesimport jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
471638Srgrimesimport jdk.nashorn.internal.runtime.linker.Bootstrap;
481638Srgrimes
491638Srgrimes/**
501638Srgrimes * Representation for ECMAScript types - this maps directly to the ECMA script standard
511638Srgrimes */
521638Srgrimespublic enum JSType {
531638Srgrimes    /** The undefined type */
541638Srgrimes    UNDEFINED("undefined"),
551638Srgrimes
561638Srgrimes    /** The null type */
571638Srgrimes    NULL("object"),
581638Srgrimes
591638Srgrimes    /** The boolean type */
601638Srgrimes    BOOLEAN("boolean"),
611638Srgrimes
621638Srgrimes    /** The number type */
631638Srgrimes    NUMBER("number"),
641638Srgrimes
651638Srgrimes    /** The string type */
661638Srgrimes    STRING("string"),
671638Srgrimes
681638Srgrimes    /** The object type */
691638Srgrimes    OBJECT("object"),
701638Srgrimes
711638Srgrimes    /** The function type */
721638Srgrimes    FUNCTION("function");
731638Srgrimes
741638Srgrimes    /** The type name as returned by ECMAScript "typeof" operator*/
751638Srgrimes    private final String typeName;
761638Srgrimes
771638Srgrimes    /** Max value for an uint32 in JavaScript */
781638Srgrimes    public static final long MAX_UINT = 0xFFFF_FFFFL;
791638Srgrimes
801638Srgrimes    private static final MethodHandles.Lookup JSTYPE_LOOKUP = MethodHandles.lookup();
811638Srgrimes
821638Srgrimes    /** JavaScript compliant conversion function from Object to boolean */
831638Srgrimes    public static final Call TO_BOOLEAN = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, Object.class);
841638Srgrimes
851638Srgrimes    /** JavaScript compliant conversion function from number to boolean */
861638Srgrimes    public static final Call TO_BOOLEAN_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, double.class);
871638Srgrimes
881638Srgrimes    /** JavaScript compliant conversion function from Object to integer */
891638Srgrimes    public static final Call TO_INTEGER = staticCall(JSTYPE_LOOKUP, JSType.class, "toInteger", int.class, Object.class);
901638Srgrimes
911638Srgrimes    /** JavaScript compliant conversion function from Object to long */
921638Srgrimes    public static final Call TO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, Object.class);
931638Srgrimes
941638Srgrimes    /** JavaScript compliant conversion function from double to long */
951638Srgrimes    public static final Call TO_LONG_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, double.class);
961638Srgrimes
971638Srgrimes    /** JavaScript compliant conversion function from Object to number */
981638Srgrimes    public static final Call TO_NUMBER = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumber", double.class, Object.class);
991638Srgrimes
1001638Srgrimes    /** JavaScript compliant conversion function from Object to number with type check */
1011638Srgrimes    public static final Call TO_NUMBER_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumberOptimistic", double.class, Object.class, int.class);
1021638Srgrimes
1031638Srgrimes    /** JavaScript compliant conversion function from Object to String */
1041638Srgrimes    public static final Call TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, Object.class);
1051638Srgrimes
1061638Srgrimes    /** JavaScript compliant conversion function from Object to int32 */
1071638Srgrimes    public static final Call TO_INT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, Object.class);
1081638Srgrimes
1091638Srgrimes    /** JavaScript compliant conversion function from Object to int32 */
1101638Srgrimes    public static final Call TO_INT32_L = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, long.class);
1111638Srgrimes
1121638Srgrimes    /** JavaScript compliant conversion function from Object to int32 with type check */
1131638Srgrimes    public static final Call TO_INT32_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32Optimistic", int.class, Object.class, int.class);
1141638Srgrimes
1151638Srgrimes    /** JavaScript compliant conversion function from double to int32 */
1161638Srgrimes    public static final Call TO_INT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, double.class);
1171638Srgrimes
1181638Srgrimes    /** JavaScript compliant conversion function from Object to uint32 */
1191638Srgrimes    public static final Call TO_UINT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, Object.class);
1201638Srgrimes
1211638Srgrimes    /** JavaScript compliant conversion function from Object to long with type check */
1221638Srgrimes    public static final Call TO_LONG_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toLongOptimistic", long.class, Object.class, int.class);
1231638Srgrimes
1241638Srgrimes    /** JavaScript compliant conversion function from number to uint32 */
1251638Srgrimes    public static final Call TO_UINT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, double.class);
1261638Srgrimes
1271638Srgrimes    /** JavaScript compliant conversion function from number to String */
1281638Srgrimes    public static final Call TO_STRING_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, double.class);
1291638Srgrimes
1301638Srgrimes    /** Combined call to toPrimitive followed by toString. */
1311638Srgrimes    public static final Call TO_PRIMITIVE_TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToString", String.class, Object.class);
1321638Srgrimes
1331638Srgrimes    /** Combined call to toPrimitive followed by toCharSequence. */
1341638Srgrimes    public static final Call TO_PRIMITIVE_TO_CHARSEQUENCE = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToCharSequence", CharSequence.class, Object.class);
1351638Srgrimes
1361638Srgrimes    /** Throw an unwarranted optimism exception */
1371638Srgrimes    public static final Call THROW_UNWARRANTED = staticCall(JSTYPE_LOOKUP, JSType.class, "throwUnwarrantedOptimismException", Object.class, Object.class, int.class);
1381638Srgrimes
1391638Srgrimes    /** Add exact wrapper for potentially overflowing integer operations */
1401638Srgrimes    public static final Call ADD_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", int.class, int.class, int.class, int.class);
1411638Srgrimes
1421638Srgrimes    /** Sub exact wrapper for potentially overflowing integer operations */
1431638Srgrimes    public static final Call SUB_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", int.class, int.class, int.class, int.class);
1441638Srgrimes
1451638Srgrimes    /** Multiply exact wrapper for potentially overflowing integer operations */
1461638Srgrimes    public static final Call MUL_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", int.class, int.class, int.class, int.class);
1471638Srgrimes
1481638Srgrimes    /** Div exact wrapper for potentially integer division that turns into float point */
1491638Srgrimes    public static final Call DIV_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
1501638Srgrimes
1511638Srgrimes    /** Mod exact wrapper for potentially integer remainders that turns into float point */
1521638Srgrimes    public static final Call REM_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
1531638Srgrimes
1541638Srgrimes    /** Decrement exact wrapper for potentially overflowing integer operations */
1551638Srgrimes    public static final Call DECREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact",   int.class, int.class, int.class);
1561638Srgrimes
1571638Srgrimes    /** Increment exact wrapper for potentially overflowing integer operations */
1581638Srgrimes    public static final Call INCREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact",   int.class, int.class, int.class);
1591638Srgrimes
1601638Srgrimes    /** Negate exact exact wrapper for potentially overflowing integer operations */
1611638Srgrimes    public static final Call NEGATE_EXACT         = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact", int.class, int.class, int.class);
1621638Srgrimes
1631638Srgrimes    /** Add exact wrapper for potentially overflowing long operations */
1641638Srgrimes    public static final Call ADD_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", long.class, long.class, long.class, int.class);
1651638Srgrimes
1661638Srgrimes    /** Sub exact wrapper for potentially overflowing long operations */
1671638Srgrimes    public static final Call SUB_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", long.class, long.class, long.class, int.class);
1681638Srgrimes
1691638Srgrimes    /** Multiply exact wrapper for potentially overflowing long operations */
1701638Srgrimes    public static final Call MUL_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", long.class, long.class, long.class, int.class);
1711638Srgrimes
1721638Srgrimes    /** Div exact wrapper for potentially integer division that turns into float point */
1731638Srgrimes    public static final Call DIV_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
1741638Srgrimes
1751638Srgrimes    /** Mod exact wrapper for potentially integer remainders that turns into float point */
1761638Srgrimes    public static final Call REM_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
1771638Srgrimes
1781638Srgrimes    /** Decrement exact wrapper for potentially overflowing long operations */
1791638Srgrimes    public static final Call DECREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact",  long.class, long.class, int.class);
1801638Srgrimes
1811638Srgrimes    /** Increment exact wrapper for potentially overflowing long operations */
1821638Srgrimes    public static final Call INCREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact",  long.class, long.class, int.class);
1831638Srgrimes
1841638Srgrimes    /** Negate exact exact wrapper for potentially overflowing long operations */
1851638Srgrimes    public static final Call NEGATE_EXACT_LONG    = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact",     long.class, long.class, int.class);
1861638Srgrimes
1871638Srgrimes    /** Method handle to convert a JS Object to a Java array. */
1881638Srgrimes    public static final Call TO_JAVA_ARRAY = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
1891638Srgrimes
1901638Srgrimes    /** Method handle to convert a JS Object to a Java List. */
1911638Srgrimes    public static final Call TO_JAVA_LIST = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaList", List.class, Object.class);
1921638Srgrimes
1931638Srgrimes    /** Method handle to convert a JS Object to a Java deque. */
1941638Srgrimes    public static final Call TO_JAVA_DEQUE = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaDeque", Deque.class, Object.class);
1951638Srgrimes
1961638Srgrimes    /** Method handle for void returns. */
1971638Srgrimes    public static final Call VOID_RETURN = staticCall(JSTYPE_LOOKUP, JSType.class, "voidReturn", void.class);
1981638Srgrimes
1991638Srgrimes
2001638Srgrimes    /**
2011638Srgrimes     * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide
2021638Srgrimes     *  in the dual--fields world
2031638Srgrimes     */
2041638Srgrimes    private static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
2051638Srgrimes            Arrays.asList(
2061638Srgrimes                Type.INT,
2071638Srgrimes                Type.LONG,
2081638Srgrimes                Type.NUMBER,
2091638Srgrimes                Type.OBJECT));
2101638Srgrimes
2111638Srgrimes    /** table index for undefined type - hard coded so it can be used in switches at compile time */
2121638Srgrimes    public static final int TYPE_UNDEFINED_INDEX = -1;
2131638Srgrimes    /** table index for integer type - hard coded so it can be used in switches at compile time */
2141638Srgrimes    public static final int TYPE_INT_INDEX    = 0; //getAccessorTypeIndex(int.class);
2151638Srgrimes    /** table index for long type - hard coded so it can be used in switches at compile time */
2161638Srgrimes    public static final int TYPE_LONG_INDEX   = 1; //getAccessorTypeIndex(long.class);
2171638Srgrimes    /** table index for double type - hard coded so it can be used in switches at compile time */
2181638Srgrimes    public static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
2191638Srgrimes    /** table index for object type - hard coded so it can be used in switches at compile time */
2201638Srgrimes    public static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
2211638Srgrimes
2221638Srgrimes    /** object conversion quickies with JS semantics - used for return value and parameter filter */
2231638Srgrimes    public static final List<MethodHandle> CONVERT_OBJECT = toUnmodifiableList(
2241638Srgrimes        JSType.TO_INT32.methodHandle(),
2251638Srgrimes        JSType.TO_UINT32.methodHandle(),
2261638Srgrimes        JSType.TO_NUMBER.methodHandle(),
2271638Srgrimes        null
2281638Srgrimes    );
2291638Srgrimes
2301638Srgrimes    /**
2311638Srgrimes     * object conversion quickies with JS semantics - used for return value and parameter filter, optimistic
2321638Srgrimes     * throws exception upon incompatible type (asking for a narrower one than the storage)
2331638Srgrimes     */
2341638Srgrimes    public static final List<MethodHandle> CONVERT_OBJECT_OPTIMISTIC = toUnmodifiableList(
2351638Srgrimes        JSType.TO_INT32_OPTIMISTIC.methodHandle(),
2361638Srgrimes        JSType.TO_LONG_OPTIMISTIC.methodHandle(),
2371638Srgrimes        JSType.TO_NUMBER_OPTIMISTIC.methodHandle(),
2381638Srgrimes        null
2391638Srgrimes    );
2401638Srgrimes
2411638Srgrimes    /** The value of Undefined cast to an int32 */
2421638Srgrimes    public static final int    UNDEFINED_INT    = 0;
2431638Srgrimes    /** The value of Undefined cast to a long */
2441638Srgrimes    public static final long   UNDEFINED_LONG   = 0L;
2451638Srgrimes    /** The value of Undefined cast to a double */
2461638Srgrimes    public static final double UNDEFINED_DOUBLE = Double.NaN;
2471638Srgrimes
2481638Srgrimes    /**
2491638Srgrimes     * Method handles for getters that return undefined coerced
2501638Srgrimes     * to the appropriate type
2511638Srgrimes     */
2521638Srgrimes    public static final List<MethodHandle> GET_UNDEFINED = toUnmodifiableList(
2531638Srgrimes        MH.constant(int.class, UNDEFINED_INT),
2541638Srgrimes        MH.constant(long.class, UNDEFINED_LONG),
2551638Srgrimes        MH.constant(double.class, UNDEFINED_DOUBLE),
2561638Srgrimes        MH.constant(Object.class, Undefined.getUndefined())
2571638Srgrimes    );
2581638Srgrimes
2591638Srgrimes    private static final double INT32_LIMIT = 4294967296.0;
2601638Srgrimes
2611638Srgrimes    /**
2621638Srgrimes     * Constructor
2631638Srgrimes     *
2641638Srgrimes     * @param typeName the type name
2651638Srgrimes     */
2661638Srgrimes    private JSType(final String typeName) {
2671638Srgrimes        this.typeName = typeName;
2681638Srgrimes    }
2691638Srgrimes
2701638Srgrimes    /**
2711638Srgrimes     * The external type name as returned by ECMAScript "typeof" operator
2721638Srgrimes     *
2731638Srgrimes     * @return type name for this type
2741638Srgrimes     */
2751638Srgrimes    public final String typeName() {
2761638Srgrimes        return this.typeName;
2771638Srgrimes    }
2781638Srgrimes
2791638Srgrimes    /**
2801638Srgrimes     * Return the JSType for a given object
2811638Srgrimes     *
2821638Srgrimes     * @param obj an object
2831638Srgrimes     *
2841638Srgrimes     * @return the JSType for the object
2851638Srgrimes     */
2861638Srgrimes    public static JSType of(final Object obj) {
28710073Speter        // Order of these statements is tuned for performance (see JDK-8024476)
2881638Srgrimes        if (obj == null) {
2891638Srgrimes            return JSType.NULL;
2901638Srgrimes        }
2911638Srgrimes
2921638Srgrimes        if (obj instanceof ScriptObject) {
2931638Srgrimes            return obj instanceof ScriptFunction ? JSType.FUNCTION : JSType.OBJECT;
2941638Srgrimes        }
2951638Srgrimes
2961638Srgrimes        if (obj instanceof Boolean) {
2971638Srgrimes            return JSType.BOOLEAN;
2981638Srgrimes        }
2991638Srgrimes
3001638Srgrimes        if (obj instanceof String || obj instanceof ConsString) {
3011638Srgrimes            return JSType.STRING;
3021638Srgrimes        }
3031638Srgrimes
3041638Srgrimes        if (obj instanceof Number) {
3051638Srgrimes            return JSType.NUMBER;
3061638Srgrimes        }
3071638Srgrimes
3081638Srgrimes        if (obj == ScriptRuntime.UNDEFINED) {
3091638Srgrimes            return JSType.UNDEFINED;
3101638Srgrimes        }
3111638Srgrimes
3121638Srgrimes        return Bootstrap.isCallable(obj) ? JSType.FUNCTION : JSType.OBJECT;
3131638Srgrimes    }
3141638Srgrimes
3151638Srgrimes    /**
3161638Srgrimes     * Similar to {@link #of(Object)}, but does not distinguish between {@link #FUNCTION} and {@link #OBJECT}, returning
3171638Srgrimes     * {@link #OBJECT} in both cases. The distinction is costly, and the EQ and STRICT_EQ predicates don't care about it
3181638Srgrimes     * so we maintain this version for their use.
3191638Srgrimes     *
3201638Srgrimes     * @param obj an object
3211638Srgrimes     *
3221638Srgrimes     * @return the JSType for the object; returns {@link #OBJECT} instead of {@link #FUNCTION} for functions.
3231638Srgrimes     */
3241638Srgrimes    public static JSType ofNoFunction(final Object obj) {
3251638Srgrimes        // Order of these statements is tuned for performance (see JDK-8024476)
3261638Srgrimes        if (obj == null) {
3271638Srgrimes            return JSType.NULL;
3281638Srgrimes        }
3291638Srgrimes
3301638Srgrimes        if (obj instanceof ScriptObject) {
3311638Srgrimes            return JSType.OBJECT;
3321638Srgrimes        }
3331638Srgrimes
3341638Srgrimes        if (obj instanceof Boolean) {
3351638Srgrimes            return JSType.BOOLEAN;
3361638Srgrimes        }
3371638Srgrimes
3381638Srgrimes        if (obj instanceof String || obj instanceof ConsString) {
3391638Srgrimes            return JSType.STRING;
3401638Srgrimes        }
3411638Srgrimes
3421638Srgrimes        if (obj instanceof Number) {
3431638Srgrimes            return JSType.NUMBER;
3441638Srgrimes        }
3451638Srgrimes
3461638Srgrimes        if (obj == ScriptRuntime.UNDEFINED) {
3471638Srgrimes            return JSType.UNDEFINED;
3481638Srgrimes        }
3491638Srgrimes
3501638Srgrimes        return JSType.OBJECT;
3511638Srgrimes    }
3521638Srgrimes
3531638Srgrimes    /**
3541638Srgrimes     * Void return method handle glue
3551638Srgrimes     */
3561638Srgrimes    public static void voidReturn() {
3571638Srgrimes        //empty
3581638Srgrimes        //TODO: fix up SetMethodCreator better so we don't need this stupid thing
3591638Srgrimes    }
3601638Srgrimes
3611638Srgrimes    /**
3621638Srgrimes     * Returns true if double number can be represented as an int
3631638Srgrimes     *
3641638Srgrimes     * @param number a long to inspect
3651638Srgrimes     *
3661638Srgrimes     * @return true for int representable longs
3671638Srgrimes     */
3681638Srgrimes    public static boolean isRepresentableAsInt(final long number) {
3691638Srgrimes        return (int)number == number;
3701638Srgrimes    }
3711638Srgrimes
3721638Srgrimes    /**
3731638Srgrimes     * Returns true if double number can be represented as an int. Note that it returns true for negative zero. If you
3741638Srgrimes     * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}.
3751638Srgrimes     *
3761638Srgrimes     * @param number a double to inspect
3771638Srgrimes     *
3781638Srgrimes     * @return true for int representable doubles
3791638Srgrimes     */
3801638Srgrimes    public static boolean isRepresentableAsInt(final double number) {
3811638Srgrimes        return (int)number == number;
3821638Srgrimes    }
3831638Srgrimes
3841638Srgrimes    /**
3851638Srgrimes     * Returns true if Object can be represented as an int
3861638Srgrimes     *
3871638Srgrimes     * @param obj an object to inspect
3881638Srgrimes     *
3891638Srgrimes     * @return true for int representable objects
3901638Srgrimes     */
3911638Srgrimes    public static boolean isRepresentableAsInt(final Object obj) {
3921638Srgrimes        if (obj instanceof Number) {
3931638Srgrimes            return isRepresentableAsInt(((Number)obj).doubleValue());
3941638Srgrimes        }
3951638Srgrimes        return false;
3961638Srgrimes    }
3971638Srgrimes
3981638Srgrimes    /**
3991638Srgrimes     * Returns true if double number can be represented as a long. Note that it returns true for negative zero. If you
4001638Srgrimes     * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}.
4011638Srgrimes     *
4021638Srgrimes     * @param number a double to inspect
4031638Srgrimes     * @return true for long representable doubles
4041638Srgrimes     */
4051638Srgrimes    public static boolean isRepresentableAsLong(final double number) {
4061638Srgrimes        return (long)number == number;
4071638Srgrimes    }
4081638Srgrimes
4091638Srgrimes    /**
4101638Srgrimes     * Returns true if Object can be represented as a long
4111638Srgrimes     *
4121638Srgrimes     * @param obj an object to inspect
4131638Srgrimes     *
4141638Srgrimes     * @return true for long representable objects
4151638Srgrimes     */
4161638Srgrimes    public static boolean isRepresentableAsLong(final Object obj) {
4171638Srgrimes        if (obj instanceof Number) {
4181638Srgrimes            return isRepresentableAsLong(((Number)obj).doubleValue());
4191638Srgrimes        }
4201638Srgrimes        return false;
4211638Srgrimes    }
4221638Srgrimes
4231638Srgrimes    /**
4241638Srgrimes     * Returns true if the number is the negative zero ({@code -0.0d}).
4251638Srgrimes     * @param number the number to test
4261638Srgrimes     * @return true if it is the negative zero, false otherwise.
4271638Srgrimes     */
4281638Srgrimes    public static boolean isNegativeZero(final double number) {
4291638Srgrimes        return number == 0.0d && Double.doubleToRawLongBits(number) == 0x8000000000000000L;
4301638Srgrimes    }
4311638Srgrimes
4321638Srgrimes    /**
4331638Srgrimes     * Check whether an object is primitive
4341638Srgrimes     *
4351638Srgrimes     * @param obj an object
4361638Srgrimes     *
4371638Srgrimes     * @return true if object is primitive (includes null and undefined)
4381638Srgrimes     */
4391638Srgrimes    public static boolean isPrimitive(final Object obj) {
4401638Srgrimes        return obj == null ||
4411638Srgrimes               obj == ScriptRuntime.UNDEFINED ||
4421638Srgrimes               obj instanceof Boolean ||
4431638Srgrimes               obj instanceof Number ||
4441638Srgrimes               obj instanceof String ||
4451638Srgrimes               obj instanceof ConsString;
4461638Srgrimes    }
4471638Srgrimes
4481638Srgrimes   /**
4491638Srgrimes    * Primitive converter for an object
4501638Srgrimes    *
4511638Srgrimes    * @param obj an object
4521638Srgrimes    *
4531638Srgrimes    * @return primitive form of the object
4541638Srgrimes    */
4551638Srgrimes    public static Object toPrimitive(final Object obj) {
4561638Srgrimes        return toPrimitive(obj, null);
4571638Srgrimes    }
4581638Srgrimes
4591638Srgrimes    /**
4601638Srgrimes     * Primitive converter for an object including type hint
4611638Srgrimes     * See ECMA 9.1 ToPrimitive
4621638Srgrimes     *
4631638Srgrimes     * @param obj  an object
4641638Srgrimes     * @param hint a type hint
4651638Srgrimes     *
4661638Srgrimes     * @return the primitive form of the object
4671638Srgrimes     */
4681638Srgrimes    public static Object toPrimitive(final Object obj, final Class<?> hint) {
4691638Srgrimes        return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj;
4701638Srgrimes    }
4711638Srgrimes
4721638Srgrimes    private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) {
4731638Srgrimes        final Object result = sobj.getDefaultValue(hint);
4741638Srgrimes
4751638Srgrimes        if (!isPrimitive(result)) {
4761638Srgrimes            throw typeError("bad.default.value", result.toString());
4771638Srgrimes        }
4781638Srgrimes
4791638Srgrimes        return result;
4801638Srgrimes    }
4811638Srgrimes
4821638Srgrimes    /**
4831638Srgrimes     * Combines a hintless toPrimitive and a toString call.
4841638Srgrimes     *
4851638Srgrimes     * @param obj  an object
4861638Srgrimes     *
4871638Srgrimes     * @return the string form of the primitive form of the object
4881638Srgrimes     */
4891638Srgrimes    public static String toPrimitiveToString(final Object obj) {
4901638Srgrimes        return toString(toPrimitive(obj));
4911638Srgrimes    }
4921638Srgrimes
4931638Srgrimes    /**
49414291Sjkh     * Like {@link #toPrimitiveToString(Object)}, but avoids conversion of ConsString to String.
4951638Srgrimes     *
4961638Srgrimes     * @param obj  an object
4971638Srgrimes     * @return the CharSequence form of the primitive form of the object
4981638Srgrimes     */
4991638Srgrimes    public static CharSequence toPrimitiveToCharSequence(final Object obj) {
5001638Srgrimes        return toCharSequence(toPrimitive(obj));
5011638Srgrimes    }
5021638Srgrimes
5031638Srgrimes    /**
5041638Srgrimes     * JavaScript compliant conversion of number to boolean
5051638Srgrimes     *
5061638Srgrimes     * @param num a number
5071638Srgrimes     *
5081638Srgrimes     * @return a boolean
5091638Srgrimes     */
5101638Srgrimes    public static boolean toBoolean(final double num) {
5111638Srgrimes        return num != 0 && !Double.isNaN(num);
5121638Srgrimes    }
5131638Srgrimes
5141638Srgrimes    /**
5151638Srgrimes     * JavaScript compliant conversion of Object to boolean
5161638Srgrimes     * See ECMA 9.2 ToBoolean
5171638Srgrimes     *
5181638Srgrimes     * @param obj an object
5191638Srgrimes     *
5201638Srgrimes     * @return a boolean
5211638Srgrimes     */
5221638Srgrimes    public static boolean toBoolean(final Object obj) {
5231638Srgrimes        if (obj instanceof Boolean) {
5241638Srgrimes            return (Boolean)obj;
5251638Srgrimes        }
5261638Srgrimes
5271638Srgrimes        if (nullOrUndefined(obj)) {
5281638Srgrimes            return false;
5291638Srgrimes        }
5301638Srgrimes
5311638Srgrimes        if (obj instanceof Number) {
5321638Srgrimes            final double num = ((Number)obj).doubleValue();
5331638Srgrimes            return num != 0 && !Double.isNaN(num);
5341638Srgrimes        }
5351638Srgrimes
5361638Srgrimes        if (obj instanceof String || obj instanceof ConsString) {
5371638Srgrimes            return ((CharSequence)obj).length() > 0;
5381638Srgrimes        }
5391638Srgrimes
5401638Srgrimes        return true;
5411638Srgrimes    }
5421638Srgrimes
5431638Srgrimes
5441638Srgrimes    /**
5451638Srgrimes     * JavaScript compliant converter of Object to String
5461638Srgrimes     * See ECMA 9.8 ToString
5471638Srgrimes     *
5481638Srgrimes     * @param obj an object
5491638Srgrimes     *
5501638Srgrimes     * @return a string
5511638Srgrimes     */
5521638Srgrimes    public static String toString(final Object obj) {
5531638Srgrimes        return toStringImpl(obj, false);
5541638Srgrimes    }
5551638Srgrimes
5561638Srgrimes    /**
5571638Srgrimes     * If obj is an instance of {@link ConsString} cast to CharSequence, else return
5581638Srgrimes     * result of {@link #toString(Object)}.
5591638Srgrimes     *
5601638Srgrimes     * @param obj an object
5611638Srgrimes     * @return an instance of String or ConsString
5621638Srgrimes     */
5631638Srgrimes    public static CharSequence toCharSequence(final Object obj) {
5641638Srgrimes        if (obj instanceof ConsString) {
5651638Srgrimes            return (CharSequence) obj;
5661638Srgrimes        }
5671638Srgrimes        return toString(obj);
5681638Srgrimes    }
5691638Srgrimes
5701638Srgrimes    /**
5711638Srgrimes     * Check whether a string is representable as a JavaScript number
5721638Srgrimes     *
5731638Srgrimes     * @param str  a string
5741638Srgrimes     *
5751638Srgrimes     * @return     true if string can be represented as a number
5761638Srgrimes     */
5771638Srgrimes    public static boolean isNumber(final String str) {
5781638Srgrimes        try {
5791638Srgrimes            Double.parseDouble(str);
5801638Srgrimes            return true;
5811638Srgrimes        } catch (final NumberFormatException e) {
5821638Srgrimes            return false;
5831638Srgrimes        }
5841638Srgrimes    }
5851638Srgrimes
5861638Srgrimes    /**
5871638Srgrimes     * JavaScript compliant conversion of integer to String
5881638Srgrimes     *
5891638Srgrimes     * @param num an integer
5901638Srgrimes     *
5911638Srgrimes     * @return a string
5921638Srgrimes     */
5931638Srgrimes    public static String toString(final int num) {
5941638Srgrimes        return Integer.toString(num);
5951638Srgrimes    }
5961638Srgrimes
5971638Srgrimes    /**
5981638Srgrimes     * JavaScript compliant conversion of number to String
5991638Srgrimes     * See ECMA 9.8.1
6001638Srgrimes     *
6011638Srgrimes     * @param num a number
6021638Srgrimes     *
6031638Srgrimes     * @return a string
6041638Srgrimes     */
6051638Srgrimes    public static String toString(final double num) {
6061638Srgrimes        if (isRepresentableAsInt(num)) {
6071800Sphk            return Integer.toString((int)num);
6081638Srgrimes        }
6091638Srgrimes
6101638Srgrimes        if (num == Double.POSITIVE_INFINITY) {
6111638Srgrimes            return "Infinity";
6121638Srgrimes        }
6131638Srgrimes
6141638Srgrimes        if (num == Double.NEGATIVE_INFINITY) {
6151638Srgrimes            return "-Infinity";
6161638Srgrimes        }
6171638Srgrimes
6181638Srgrimes        if (Double.isNaN(num)) {
6191638Srgrimes            return "NaN";
6201638Srgrimes        }
6211638Srgrimes
6221638Srgrimes        return NumberToString.stringFor(num);
6231638Srgrimes    }
6241638Srgrimes
6251638Srgrimes    /**
6261638Srgrimes     * JavaScript compliant conversion of number to String
6271638Srgrimes     *
6281638Srgrimes     * @param num   a number
6291638Srgrimes     * @param radix a radix for the conversion
6301638Srgrimes     *
6311638Srgrimes     * @return a string
6321638Srgrimes     */
6331638Srgrimes    public static String toString(final double num, final int radix) {
6341638Srgrimes        assert radix >= 2 && radix <= 36 : "invalid radix";
6351638Srgrimes
6361638Srgrimes        if (isRepresentableAsInt(num)) {
6371638Srgrimes            return Integer.toString((int)num, radix);
6381638Srgrimes        }
6391638Srgrimes
6401638Srgrimes        if (num == Double.POSITIVE_INFINITY) {
6411638Srgrimes            return "Infinity";
6421638Srgrimes        }
6431638Srgrimes
6441638Srgrimes        if (num == Double.NEGATIVE_INFINITY) {
6451638Srgrimes            return "-Infinity";
6461638Srgrimes        }
6471638Srgrimes
6481638Srgrimes        if (Double.isNaN(num)) {
6491638Srgrimes            return "NaN";
6501638Srgrimes        }
6511638Srgrimes
6521638Srgrimes        if (num == 0.0) {
6531638Srgrimes            return "0";
6541638Srgrimes        }
6551638Srgrimes
6561638Srgrimes        final String chars     = "0123456789abcdefghijklmnopqrstuvwxyz";
6571638Srgrimes        final StringBuilder sb = new StringBuilder();
6581638Srgrimes
6591638Srgrimes        final boolean negative  = num < 0.0;
6601638Srgrimes        final double  signedNum = negative ? -num : num;
6611638Srgrimes
6621638Srgrimes        double intPart = Math.floor(signedNum);
6631638Srgrimes        double decPart = signedNum - intPart;
6641638Srgrimes
6651638Srgrimes        // encode integer part from least significant digit, then reverse
6661638Srgrimes        do {
6671638Srgrimes            final double remainder = intPart % radix;
6681638Srgrimes            sb.append(chars.charAt((int) remainder));
6691638Srgrimes            intPart -= remainder;
6701638Srgrimes            intPart /= radix;
6711638Srgrimes        } while (intPart >= 1.0);
6721638Srgrimes
6731638Srgrimes        if (negative) {
6741638Srgrimes            sb.append('-');
6751638Srgrimes        }
6761638Srgrimes        sb.reverse();
6771638Srgrimes
6781638Srgrimes        // encode decimal part
6791638Srgrimes        if (decPart > 0.0) {
6801638Srgrimes            final int dot = sb.length();
6811638Srgrimes            sb.append('.');
6821638Srgrimes            do {
6831638Srgrimes                decPart *= radix;
6841638Srgrimes                final double d = Math.floor(decPart);
6851638Srgrimes                sb.append(chars.charAt((int)d));
6861638Srgrimes                decPart -= d;
6871638Srgrimes            } while (decPart > 0.0 && sb.length() - dot < 1100);
6881638Srgrimes            // somewhat arbitrarily use same limit as V8
6891638Srgrimes        }
6901638Srgrimes
6911638Srgrimes        return sb.toString();
6921638Srgrimes    }
6931638Srgrimes
6941638Srgrimes    /**
6951638Srgrimes     * JavaScript compliant conversion of Object to number
6961638Srgrimes     * See ECMA 9.3 ToNumber
6971638Srgrimes     *
6981638Srgrimes     * @param obj  an object
6991638Srgrimes     *
7001638Srgrimes     * @return a number
7011638Srgrimes     */
7021638Srgrimes    public static double toNumber(final Object obj) {
7031638Srgrimes        if (obj instanceof Number) {
7041638Srgrimes            return ((Number)obj).doubleValue();
7051638Srgrimes        }
7061638Srgrimes        return toNumberGeneric(obj);
7071638Srgrimes    }
7081638Srgrimes
7091638Srgrimes
7101638Srgrimes    /**
7111638Srgrimes     * JavaScript compliant conversion of Object to number
7121638Srgrimes     * See ECMA 9.3 ToNumber
7131638Srgrimes     *
7141638Srgrimes     * @param obj  an object
7151638Srgrimes     *
7161638Srgrimes     * @return a number
7171638Srgrimes     */
7181638Srgrimes    public static double toNumber(final ScriptObject obj) {
7191638Srgrimes        return toNumber(toPrimitive(obj, Number.class));
7201638Srgrimes    }
7211638Srgrimes
7221638Srgrimes    /**
7231638Srgrimes     * Optimistic number conversion - throws UnwarrantedOptimismException if Object
7241638Srgrimes     *
7251638Srgrimes     * @param obj           object to convert
7261638Srgrimes     * @param programPoint  program point
7271638Srgrimes     * @return double
7281638Srgrimes     */
7291638Srgrimes    public static double toNumberOptimistic(final Object obj, final int programPoint) {
7301638Srgrimes        if (obj != null) {
7311638Srgrimes            final Class<?> clz = obj.getClass();
7321638Srgrimes            if (clz == Double.class || clz == Integer.class || clz == Long.class) {
7331638Srgrimes                return ((Number)obj).doubleValue();
7341638Srgrimes            }
7351638Srgrimes        }
7361638Srgrimes        throw new UnwarrantedOptimismException(obj, programPoint);
7371638Srgrimes    }
7381638Srgrimes
7391638Srgrimes    /**
7401638Srgrimes     * Object to number conversion that delegates to either {@link #toNumber(Object)} or to
7411638Srgrimes     * {@link #toNumberOptimistic(Object, int)} depending on whether the program point is valid or not.
7421638Srgrimes     * @param obj the object to convert
7431638Srgrimes     * @param programPoint the program point; can be invalid.
7441638Srgrimes     * @return the value converted to a number
7451638Srgrimes     * @throws UnwarrantedOptimismException if the value can't be represented as a number and the program point is valid.
7461638Srgrimes     */
7471638Srgrimes    public static double toNumberMaybeOptimistic(final Object obj, final int programPoint) {
7481638Srgrimes        return UnwarrantedOptimismException.isValid(programPoint) ? toNumberOptimistic(obj, programPoint) : toNumber(obj);
7491638Srgrimes    }
7501638Srgrimes
7511638Srgrimes    /**
7521638Srgrimes     * Digit representation for a character
7531638Srgrimes     *
7541638Srgrimes     * @param ch     a character
7551638Srgrimes     * @param radix  radix
7561638Srgrimes     *
7571638Srgrimes     * @return the digit for this character
7581638Srgrimes     */
7591638Srgrimes    public static int digit(final char ch, final int radix) {
7601638Srgrimes        return digit(ch, radix, false);
7611638Srgrimes    }
7621638Srgrimes
7631638Srgrimes    /**
7641638Srgrimes     * Digit representation for a character
7651638Srgrimes     *
7661638Srgrimes     * @param ch             a character
7671638Srgrimes     * @param radix          radix
7681638Srgrimes     * @param onlyIsoLatin1  iso latin conversion only
7691638Srgrimes     *
7701638Srgrimes     * @return the digit for this character
7711638Srgrimes     */
7721638Srgrimes    public static int digit(final char ch, final int radix, final boolean onlyIsoLatin1) {
7731638Srgrimes        final char maxInRadix = (char)('a' + (radix - 1) - 10);
7741638Srgrimes        final char c          = Character.toLowerCase(ch);
7751638Srgrimes
7761638Srgrimes        if (c >= 'a' && c <= maxInRadix) {
7771638Srgrimes            return Character.digit(ch, radix);
7781638Srgrimes        }
7791638Srgrimes
7801638Srgrimes        if (Character.isDigit(ch)) {
7811638Srgrimes            if (!onlyIsoLatin1 || ch >= '0' && ch <= '9') {
7821638Srgrimes                return Character.digit(ch, radix);
7831638Srgrimes            }
7841638Srgrimes        }
7851638Srgrimes
7861638Srgrimes        return -1;
7871638Srgrimes    }
7881638Srgrimes
7891638Srgrimes    /**
7901638Srgrimes     * JavaScript compliant String to number conversion
7911638Srgrimes     *
7921638Srgrimes     * @param str  a string
7931638Srgrimes     *
7941638Srgrimes     * @return a number
7951638Srgrimes     */
7961638Srgrimes    public static double toNumber(final String str) {
7971638Srgrimes        int end = str.length();
7981638Srgrimes        if (end == 0) {
7991638Srgrimes            return 0.0; // Empty string
8001638Srgrimes        }
8011638Srgrimes
8021638Srgrimes        int  start = 0;
8031638Srgrimes        char f     = str.charAt(0);
8041638Srgrimes
8051638Srgrimes        while (Lexer.isJSWhitespace(f)) {
80610070Sjoerg            if (++start == end) {
8071638Srgrimes                return 0.0d; // All whitespace string
8081638Srgrimes            }
8091638Srgrimes            f = str.charAt(start);
8101638Srgrimes        }
8111638Srgrimes
8121638Srgrimes        // Guaranteed to terminate even without start >= end check, as the previous loop found at least one
8131638Srgrimes        // non-whitespace character.
8141638Srgrimes        while (Lexer.isJSWhitespace(str.charAt(end - 1))) {
8151638Srgrimes            end--;
8161638Srgrimes        }
8171638Srgrimes
8181638Srgrimes        final boolean negative;
8191638Srgrimes        if (f == '-') {
8201638Srgrimes            if(++start == end) {
8211638Srgrimes                return Double.NaN; // Single-char "-" string
8221638Srgrimes            }
8231638Srgrimes            f = str.charAt(start);
8241638Srgrimes            negative = true;
8251638Srgrimes        } else {
8261638Srgrimes            if (f == '+') {
8271638Srgrimes                if (++start == end) {
8281638Srgrimes                    return Double.NaN; // Single-char "+" string
8291638Srgrimes                }
8301638Srgrimes                f = str.charAt(start);
8311638Srgrimes            }
8321638Srgrimes            negative = false;
8331638Srgrimes        }
8341638Srgrimes
8351638Srgrimes        final double value;
8361638Srgrimes        if (start + 1 < end && f == '0' && Character.toLowerCase(str.charAt(start + 1)) == 'x') {
8371638Srgrimes            //decode hex string
8381638Srgrimes            value = parseRadix(str.toCharArray(), start + 2, end, 16);
8391638Srgrimes        } else {
8401638Srgrimes            // Fast (no NumberFormatException) path to NaN for non-numeric strings. We allow those starting with "I" or
8411638Srgrimes            // "N" to allow for parsing "NaN" and "Infinity" correctly.
8421638Srgrimes            if ((f < '0' || f > '9') && f != '.' && f != 'I' && f != 'N') {
8431638Srgrimes                return Double.NaN;
8441638Srgrimes            }
8451638Srgrimes            try {
8461638Srgrimes                value = Double.parseDouble(str.substring(start, end));
8471638Srgrimes            } catch (final NumberFormatException e) {
8481638Srgrimes                return Double.NaN;
8491638Srgrimes            }
8501638Srgrimes        }
8511638Srgrimes
8521638Srgrimes        return negative ? -value : value;
8531638Srgrimes    }
8541638Srgrimes
8551638Srgrimes    /**
8561638Srgrimes     * JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger
8571638Srgrimes     *
8581638Srgrimes     * <p>Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE}
8591638Srgrimes     * for double values that exceed the int range, including positive and negative Infinity. It is the
8601638Srgrimes     * caller's responsibility to handle such values correctly.</p>
8611638Srgrimes     *
8621638Srgrimes     * @param obj  an object
8631638Srgrimes     * @return an integer
8641638Srgrimes     */
8651638Srgrimes    public static int toInteger(final Object obj) {
8661638Srgrimes        return (int)toNumber(obj);
8671638Srgrimes    }
8681638Srgrimes
8691638Srgrimes    /**
8701638Srgrimes     * Converts an Object to long.
8711638Srgrimes     *
8721638Srgrimes     * <p>Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE}
8731638Srgrimes     * for double values that exceed the long range, including positive and negative Infinity. It is the
8741638Srgrimes     * caller's responsibility to handle such values correctly.</p>
8751638Srgrimes     *
8761638Srgrimes     * @param obj  an object
8771638Srgrimes     * @return a long
8781638Srgrimes     */
8791638Srgrimes    public static long toLong(final Object obj) {
8801638Srgrimes        return obj instanceof Long ? ((Long)obj).longValue() : toLong(toNumber(obj));
8811638Srgrimes    }
8821638Srgrimes
8831638Srgrimes    /**
8841638Srgrimes     * Converts a double to long.
8851638Srgrimes     *
8861638Srgrimes     * @param num the double to convert
8871638Srgrimes     * @return the converted long value
8881638Srgrimes     */
8891638Srgrimes    public static long toLong(final double num) {
8901638Srgrimes        return (long)num;
8911638Srgrimes    }
8921638Srgrimes
8931638Srgrimes    /**
8941638Srgrimes     * Optimistic long conversion - throws UnwarrantedOptimismException if double or Object
8951638Srgrimes     *
8961638Srgrimes     * @param obj           object to convert
8971638Srgrimes     * @param programPoint  program point
8981638Srgrimes     * @return long
8991638Srgrimes     */
9001638Srgrimes    public static long toLongOptimistic(final Object obj, final int programPoint) {
9011638Srgrimes        if (obj != null) {
9021638Srgrimes            final Class<?> clz = obj.getClass();
9031638Srgrimes            if (clz == Long.class || clz == Integer.class) {
9041638Srgrimes                return ((Number)obj).longValue();
9051638Srgrimes            }
9061638Srgrimes        }
9071638Srgrimes        throw new UnwarrantedOptimismException(obj, programPoint);
9081638Srgrimes    }
9091638Srgrimes
9101638Srgrimes    /**
9111638Srgrimes     * Object to int conversion that delegates to either {@link #toLong(Object)} or to
9121638Srgrimes     * {@link #toLongOptimistic(Object, int)} depending on whether the program point is valid or not.
9131638Srgrimes     * @param obj the object to convert
9141638Srgrimes     * @param programPoint the program point; can be invalid.
9151638Srgrimes     * @return the value converted to long
9161638Srgrimes     * @throws UnwarrantedOptimismException if the value can't be represented as long and the program point is valid.
9171638Srgrimes     */
9181638Srgrimes    public static long toLongMaybeOptimistic(final Object obj, final int programPoint) {
9191638Srgrimes        return UnwarrantedOptimismException.isValid(programPoint) ? toLongOptimistic(obj, programPoint) : toLong(obj);
9201638Srgrimes    }
9211638Srgrimes
9221638Srgrimes    /**
9231638Srgrimes     * JavaScript compliant Object to int32 conversion
9241638Srgrimes     * See ECMA 9.5 ToInt32
9251638Srgrimes     *
9261638Srgrimes     * @param obj an object
9271638Srgrimes     * @return an int32
9281638Srgrimes     */
9291638Srgrimes    public static int toInt32(final Object obj) {
9301638Srgrimes        return toInt32(toNumber(obj));
9311638Srgrimes    }
9321638Srgrimes
9331638Srgrimes    /**
9341638Srgrimes     * Optimistic int conversion - throws UnwarrantedOptimismException if double, long or Object
9351638Srgrimes     *
9361638Srgrimes     * @param obj           object to convert
9371638Srgrimes     * @param programPoint  program point
9381638Srgrimes     * @return double
9391638Srgrimes     */
9401638Srgrimes    public static int toInt32Optimistic(final Object obj, final int programPoint) {
9411638Srgrimes        if (obj != null) {
9421638Srgrimes            final Class<?> clz = obj.getClass();
9431638Srgrimes            if (clz == Integer.class) {
9441638Srgrimes                return ((Integer)obj).intValue();
9451638Srgrimes            }
9461638Srgrimes        }
9471638Srgrimes        throw new UnwarrantedOptimismException(obj, programPoint);
9481638Srgrimes    }
9491638Srgrimes
9501638Srgrimes    /**
9511638Srgrimes     * Object to int conversion that delegates to either {@link #toInt32(Object)} or to
9521638Srgrimes     * {@link #toInt32Optimistic(Object, int)} depending on whether the program point is valid or not.
9531638Srgrimes     * @param obj the object to convert
9541638Srgrimes     * @param programPoint the program point; can be invalid.
9551638Srgrimes     * @return the value converted to int
9561638Srgrimes     * @throws UnwarrantedOptimismException if the value can't be represented as int and the program point is valid.
9571638Srgrimes     */
9581638Srgrimes    public static int toInt32MaybeOptimistic(final Object obj, final int programPoint) {
9591638Srgrimes        return UnwarrantedOptimismException.isValid(programPoint) ? toInt32Optimistic(obj, programPoint) : toInt32(obj);
9601638Srgrimes    }
9611638Srgrimes
9621638Srgrimes    // Minimum and maximum range between which every long value can be precisely represented as a double.
9631638Srgrimes    private static final long MAX_PRECISE_DOUBLE = 1L << 53;
9641638Srgrimes    private static final long MIN_PRECISE_DOUBLE = -MAX_PRECISE_DOUBLE;
9651638Srgrimes
9661638Srgrimes    /**
9671638Srgrimes     * JavaScript compliant long to int32 conversion
9681638Srgrimes     *
9691638Srgrimes     * @param num a long
9701638Srgrimes     * @return an int32
9711638Srgrimes     */
9721638Srgrimes    public static int toInt32(final long num) {
9731638Srgrimes        return (int)(num >= MIN_PRECISE_DOUBLE && num <= MAX_PRECISE_DOUBLE ? num : (long)(num % INT32_LIMIT));
9741638Srgrimes    }
9751638Srgrimes
9761638Srgrimes
9771638Srgrimes    /**
9781638Srgrimes     * JavaScript compliant number to int32 conversion
9791638Srgrimes     *
9801638Srgrimes     * @param num a number
9811638Srgrimes     * @return an int32
9821638Srgrimes     */
9831638Srgrimes    public static int toInt32(final double num) {
9841638Srgrimes        return (int)doubleToInt32(num);
9851638Srgrimes    }
9861638Srgrimes
9871638Srgrimes    /**
9881638Srgrimes     * JavaScript compliant Object to uint32 conversion
9891638Srgrimes     *
9901638Srgrimes     * @param obj an object
9911638Srgrimes     * @return a uint32
9921638Srgrimes     */
9931638Srgrimes    public static long toUint32(final Object obj) {
9941638Srgrimes        return toUint32(toNumber(obj));
9951638Srgrimes    }
9961638Srgrimes
9971638Srgrimes    /**
9981638Srgrimes     * JavaScript compliant number to uint32 conversion
9991638Srgrimes     *
10001638Srgrimes     * @param num a number
10011638Srgrimes     * @return a uint32
10021638Srgrimes     */
10031638Srgrimes    public static long toUint32(final double num) {
10041638Srgrimes        return doubleToInt32(num) & MAX_UINT;
10051638Srgrimes    }
10061638Srgrimes
10071638Srgrimes    /**
10081638Srgrimes     * JavaScript compliant Object to uint16 conversion
10091638Srgrimes     * ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer)
10101638Srgrimes     *
10111638Srgrimes     * @param obj an object
10121638Srgrimes     * @return a uint16
10131638Srgrimes     */
10141638Srgrimes    public static int toUint16(final Object obj) {
10151638Srgrimes        return toUint16(toNumber(obj));
10161638Srgrimes    }
10171638Srgrimes
10181638Srgrimes    /**
10191638Srgrimes     * JavaScript compliant number to uint16 conversion
10201638Srgrimes     *
10211638Srgrimes     * @param num a number
10221638Srgrimes     * @return a uint16
10231638Srgrimes     */
10241638Srgrimes    public static int toUint16(final int num) {
10251638Srgrimes        return num & 0xffff;
10261638Srgrimes    }
10271638Srgrimes
10281638Srgrimes    /**
10291638Srgrimes     * JavaScript compliant number to uint16 conversion
10301638Srgrimes     *
10311638Srgrimes     * @param num a number
10321638Srgrimes     * @return a uint16
10331638Srgrimes     */
10341638Srgrimes    public static int toUint16(final long num) {
10351638Srgrimes        return (int)num & 0xffff;
10361638Srgrimes    }
10371638Srgrimes
10381638Srgrimes    /**
10391638Srgrimes     * JavaScript compliant number to uint16 conversion
10401638Srgrimes     *
10411638Srgrimes     * @param num a number
10421638Srgrimes     * @return a uint16
10431638Srgrimes     */
10441638Srgrimes    public static int toUint16(final double num) {
10451638Srgrimes        return (int)doubleToInt32(num) & 0xffff;
10461638Srgrimes    }
10471638Srgrimes
10481638Srgrimes    private static long doubleToInt32(final double num) {
10491638Srgrimes        final int exponent = Math.getExponent(num);
10501638Srgrimes        if (exponent < 31) {
10511638Srgrimes            return (long) num;  // Fits into 32 bits
10521638Srgrimes        }
10531638Srgrimes        if (exponent >= 84) {
10541638Srgrimes            // Either infinite or NaN or so large that shift / modulo will produce 0
10551638Srgrimes            // (52 bit mantissa + 32 bit target width).
10561638Srgrimes            return 0;
10571638Srgrimes        }
10581638Srgrimes        // This is rather slow and could probably be sped up using bit-fiddling.
10591638Srgrimes        final double d = num >= 0 ? Math.floor(num) : Math.ceil(num);
10601638Srgrimes        return (long)(d % INT32_LIMIT);
10611638Srgrimes    }
10621638Srgrimes
10631638Srgrimes    /**
10641638Srgrimes     * Check whether a number is finite
10651638Srgrimes     *
10661638Srgrimes     * @param num a number
10671638Srgrimes     * @return true if finite
10681638Srgrimes     */
10691638Srgrimes    public static boolean isFinite(final double num) {
10701638Srgrimes        return !Double.isInfinite(num) && !Double.isNaN(num);
10711638Srgrimes    }
10721638Srgrimes
10731638Srgrimes    /**
10741638Srgrimes     * Convert a primitive to a double
10751638Srgrimes     *
10761638Srgrimes     * @param num a double
10771638Srgrimes     * @return a boxed double
10781638Srgrimes     */
10791638Srgrimes    public static Double toDouble(final double num) {
10801638Srgrimes        return num;
10811638Srgrimes    }
10821638Srgrimes
10831638Srgrimes    /**
10841638Srgrimes     * Convert a primitive to a double
10851638Srgrimes     *
10861638Srgrimes     * @param num a long
10871638Srgrimes     * @return a boxed double
10881638Srgrimes     */
10891638Srgrimes    public static Double toDouble(final long num) {
10901638Srgrimes        return (double)num;
10911638Srgrimes    }
10921638Srgrimes
10931638Srgrimes    /**
10941638Srgrimes     * Convert a primitive to a double
10951638Srgrimes     *
10961638Srgrimes     * @param num an int
10971638Srgrimes     * @return a boxed double
10981638Srgrimes     */
10991638Srgrimes    public static Double toDouble(final int num) {
11001638Srgrimes        return (double)num;
11011638Srgrimes    }
11021638Srgrimes
11031638Srgrimes    /**
11041638Srgrimes     * Convert a boolean to an Object
11051638Srgrimes     *
11061638Srgrimes     * @param bool a boolean
11071638Srgrimes     * @return a boxed boolean, its Object representation
11081638Srgrimes     */
11091638Srgrimes    public static Object toObject(final boolean bool) {
11101638Srgrimes        return bool;
11111638Srgrimes    }
11121638Srgrimes
11131638Srgrimes    /**
11141638Srgrimes     * Convert a number to an Object
11151638Srgrimes     *
11161638Srgrimes     * @param num an integer
11171638Srgrimes     * @return the boxed number
11181638Srgrimes     */
11191638Srgrimes    public static Object toObject(final int num) {
11201638Srgrimes        return num;
11211638Srgrimes    }
11221638Srgrimes
11231638Srgrimes    /**
11241638Srgrimes     * Convert a number to an Object
11251638Srgrimes     *
11261638Srgrimes     * @param num a long
11271638Srgrimes     * @return the boxed number
11281638Srgrimes     */
11291638Srgrimes    public static Object toObject(final long num) {
11301638Srgrimes        return num;
11311638Srgrimes    }
11321638Srgrimes
11331638Srgrimes    /**
11341638Srgrimes     * Convert a number to an Object
11351638Srgrimes     *
11361638Srgrimes     * @param num a double
11371638Srgrimes     * @return the boxed number
11381638Srgrimes     */
11391638Srgrimes    public static Object toObject(final double num) {
11401638Srgrimes        return num;
11411638Srgrimes    }
11421638Srgrimes
11431638Srgrimes    /**
11441638Srgrimes     * Identity converter for objects.
11451638Srgrimes     *
11461638Srgrimes     * @param obj an object
11471638Srgrimes     * @return the boxed number
11481638Srgrimes     */
11491638Srgrimes    public static Object toObject(final Object obj) {
11501638Srgrimes        return obj;
11511638Srgrimes    }
11521638Srgrimes
11531638Srgrimes    /**
11541638Srgrimes     * Object conversion. This is used to convert objects and numbers to their corresponding
11551638Srgrimes     * NativeObject type
11561638Srgrimes     * See ECMA 9.9 ToObject
11571638Srgrimes     *
11581638Srgrimes     * @param obj     the object to convert
11591638Srgrimes     *
11601638Srgrimes     * @return the wrapped object
11611638Srgrimes     */
11621638Srgrimes    public static Object toScriptObject(final Object obj) {
11631638Srgrimes        return toScriptObject(Context.getGlobal(), obj);
11641638Srgrimes    }
11651638Srgrimes
11661638Srgrimes    /**
11671638Srgrimes     * Object conversion. This is used to convert objects and numbers to their corresponding
11681638Srgrimes     * NativeObject type
11691638Srgrimes     * See ECMA 9.9 ToObject
11701638Srgrimes     *
11711638Srgrimes     * @param global  the global object
11721638Srgrimes     * @param obj     the object to convert
11731638Srgrimes     *
11741638Srgrimes     * @return the wrapped object
11751638Srgrimes     */
11761638Srgrimes    public static Object toScriptObject(final Global global, final Object obj) {
11771638Srgrimes        if (nullOrUndefined(obj)) {
11781638Srgrimes            throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
11791638Srgrimes        }
11801638Srgrimes
11811638Srgrimes        if (obj instanceof ScriptObject) {
11821638Srgrimes            return obj;
11831638Srgrimes        }
11841638Srgrimes
11851638Srgrimes        return global.wrapAsObject(obj);
11861638Srgrimes    }
11871638Srgrimes
11881638Srgrimes    /**
11891638Srgrimes     * Script object to Java array conversion.
11901638Srgrimes     *
11911638Srgrimes     * @param obj script object to be converted to Java array
11921638Srgrimes     * @param componentType component type of the destination array required
11931638Srgrimes     * @return converted Java array
11941638Srgrimes     */
11951638Srgrimes    public static Object toJavaArray(final Object obj, final Class<?> componentType) {
11961638Srgrimes        if (obj instanceof ScriptObject) {
11971638Srgrimes            return ((ScriptObject)obj).getArray().asArrayOfType(componentType);
11981638Srgrimes        } else if (obj instanceof JSObject) {
11991638Srgrimes            final ArrayLikeIterator<?> itr = ArrayLikeIterator.arrayLikeIterator(obj);
12001638Srgrimes            final int len = (int) itr.getLength();
12011638Srgrimes            final Object[] res = new Object[len];
12021638Srgrimes            int idx = 0;
12031638Srgrimes            while (itr.hasNext()) {
12041638Srgrimes                res[idx++] = itr.next();
12051638Srgrimes            }
12061638Srgrimes            return convertArray(res, componentType);
12071638Srgrimes        } else if(obj == null) {
12081638Srgrimes            return null;
12091638Srgrimes        } else {
12101638Srgrimes            throw new IllegalArgumentException("not a script object");
12111638Srgrimes        }
12121638Srgrimes    }
12131638Srgrimes
12141638Srgrimes    /**
12151638Srgrimes     * Java array to java array conversion - but using type conversions implemented by linker.
12161638Srgrimes     *
12171638Srgrimes     * @param src source array
12181638Srgrimes     * @param componentType component type of the destination array required
12191638Srgrimes     * @return converted Java array
12201638Srgrimes     */
12211638Srgrimes    public static Object convertArray(final Object[] src, final Class<?> componentType) {
12221638Srgrimes        if(componentType == Object.class) {
12231638Srgrimes            for(int i = 0; i < src.length; ++i) {
12241638Srgrimes                final Object e = src[i];
12251638Srgrimes                if(e instanceof ConsString) {
12261638Srgrimes                    src[i] = e.toString();
12271638Srgrimes                }
12281638Srgrimes            }
12291638Srgrimes        }
12301638Srgrimes
12311638Srgrimes        final int l = src.length;
12321638Srgrimes        final Object dst = Array.newInstance(componentType, l);
12331638Srgrimes        final MethodHandle converter = Bootstrap.getLinkerServices().getTypeConverter(Object.class, componentType);
12341638Srgrimes        try {
12351638Srgrimes            for (int i = 0; i < src.length; i++) {
12361638Srgrimes                Array.set(dst, i, invoke(converter, src[i]));
12371638Srgrimes            }
12381638Srgrimes        } catch (final RuntimeException | Error e) {
12391638Srgrimes            throw e;
12401638Srgrimes        } catch (final Throwable t) {
12411638Srgrimes            throw new RuntimeException(t);
12421638Srgrimes        }
12431638Srgrimes        return dst;
12441638Srgrimes    }
12451638Srgrimes
12461638Srgrimes    /**
12471638Srgrimes     * Converts a JavaScript object to a Java List. See {@link ListAdapter} for details.
12481638Srgrimes     * @param obj the object to convert. Can be any array-like object.
12491638Srgrimes     * @return a List that is live-backed by the JavaScript object.
12501638Srgrimes     */
12511638Srgrimes    public static List<?> toJavaList(final Object obj) {
12521638Srgrimes        return ListAdapter.create(obj);
12531638Srgrimes    }
12541638Srgrimes
12551638Srgrimes    /**
12561638Srgrimes     * Converts a JavaScript object to a Java Deque. See {@link ListAdapter} for details.
12571638Srgrimes     * @param obj the object to convert. Can be any array-like object.
12581638Srgrimes     * @return a Deque that is live-backed by the JavaScript object.
12591638Srgrimes     */
12601638Srgrimes    public static Deque<?> toJavaDeque(final Object obj) {
12611638Srgrimes        return ListAdapter.create(obj);
12621638Srgrimes    }
12631638Srgrimes
12641638Srgrimes    /**
12651638Srgrimes     * Check if an object is null or undefined
12661638Srgrimes     *
12671638Srgrimes     * @param obj object to check
12681638Srgrimes     *
12691638Srgrimes     * @return true if null or undefined
12701638Srgrimes     */
12711638Srgrimes    public static boolean nullOrUndefined(final Object obj) {
12721638Srgrimes        return obj == null || obj == ScriptRuntime.UNDEFINED;
12731638Srgrimes    }
12741638Srgrimes
12751638Srgrimes    static String toStringImpl(final Object obj, final boolean safe) {
12761638Srgrimes        if (obj instanceof String) {
12771638Srgrimes            return (String)obj;
12781638Srgrimes        }
12791638Srgrimes
12801638Srgrimes        if (obj instanceof Number) {
12811638Srgrimes            return toString(((Number)obj).doubleValue());
12821638Srgrimes        }
12831638Srgrimes
12841638Srgrimes        if (obj == ScriptRuntime.UNDEFINED) {
12851638Srgrimes            return "undefined";
12861638Srgrimes        }
12871638Srgrimes
12881638Srgrimes        if (obj == null) {
12891638Srgrimes            return "null";
12901638Srgrimes        }
12911638Srgrimes
12921638Srgrimes        if (obj instanceof ScriptObject) {
12931638Srgrimes            if (safe) {
12941638Srgrimes                final ScriptObject sobj = (ScriptObject)obj;
12951638Srgrimes                final Global gobj = Context.getGlobal();
12961638Srgrimes                return gobj.isError(sobj) ?
12971638Srgrimes                    ECMAException.safeToString(sobj) :
12981638Srgrimes                    sobj.safeToString();
12991638Srgrimes            }
13001638Srgrimes
13011638Srgrimes            return toString(toPrimitive(obj, String.class));
13021638Srgrimes        }
13031638Srgrimes
13041638Srgrimes        if (obj instanceof StaticClass) {
13051638Srgrimes            return "[JavaClass " + ((StaticClass)obj).getRepresentedClass().getName() + "]";
13061638Srgrimes        }
13071638Srgrimes
13081638Srgrimes        return obj.toString();
13091638Srgrimes    }
13101638Srgrimes
13111638Srgrimes    // trim from left for JS whitespaces.
13121638Srgrimes    static String trimLeft(final String str) {
13131638Srgrimes        int start = 0;
13141638Srgrimes
13151638Srgrimes        while (start < str.length() && Lexer.isJSWhitespace(str.charAt(start))) {
13161638Srgrimes            start++;
13171638Srgrimes        }
13181638Srgrimes
13191638Srgrimes        return str.substring(start);
13201638Srgrimes    }
13211638Srgrimes
13221638Srgrimes    /**
13231638Srgrimes     * Throw an unwarranted optimism exception for a program point
13241638Srgrimes     * @param value         real return value
13251638Srgrimes     * @param programPoint  program point
13261638Srgrimes     * @return
13271638Srgrimes     */
13281638Srgrimes    @SuppressWarnings("unused")
13291638Srgrimes    private static Object throwUnwarrantedOptimismException(final Object value, final int programPoint) {
13301638Srgrimes        throw new UnwarrantedOptimismException(value, programPoint);
13311638Srgrimes    }
13321638Srgrimes
13331638Srgrimes    /**
13341638Srgrimes     * Wrapper for addExact
13351638Srgrimes     *
13361638Srgrimes     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
13371638Srgrimes     * containing the result and the program point of the failure
13381638Srgrimes     *
13391638Srgrimes     * @param x first term
13401638Srgrimes     * @param y second term
13411638Srgrimes     * @param programPoint program point id
13421638Srgrimes     * @return the result
13431638Srgrimes     * @throws UnwarrantedOptimismException if overflow occurs
13441638Srgrimes     */
13451638Srgrimes    public static int addExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
13461638Srgrimes        try {
13471638Srgrimes            return Math.addExact(x, y);
13481638Srgrimes        } catch (final ArithmeticException e) {
13491638Srgrimes            throw new UnwarrantedOptimismException((long)x + (long)y, programPoint);
13501638Srgrimes        }
13511638Srgrimes    }
13521638Srgrimes
13531638Srgrimes    /**
13541638Srgrimes     * Wrapper for addExact
13551638Srgrimes     *
13561638Srgrimes     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
13571638Srgrimes     * containing the result and the program point of the failure
13581638Srgrimes     *
13591638Srgrimes     * @param x first term
13601638Srgrimes     * @param y second term
13611638Srgrimes     * @param programPoint program point id
13621638Srgrimes     * @return the result
13631638Srgrimes     * @throws UnwarrantedOptimismException if overflow occurs
13641638Srgrimes     */
13651638Srgrimes    public static long addExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
13661638Srgrimes        try {
13671638Srgrimes            return Math.addExact(x, y);
13681638Srgrimes        } catch (final ArithmeticException e) {
13691638Srgrimes            throw new UnwarrantedOptimismException((double)x + (double)y, programPoint);
13701638Srgrimes        }
13711638Srgrimes    }
13721638Srgrimes
13731638Srgrimes    /**
13741638Srgrimes     * Wrapper for subExact
13751638Srgrimes     *
13761638Srgrimes     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
13771638Srgrimes     * containing the result and the program point of the failure
13781638Srgrimes     *
13791638Srgrimes     * @param x first term
13801638Srgrimes     * @param y second term
13811638Srgrimes     * @param programPoint program point id
13821638Srgrimes     * @return the result
13831638Srgrimes     * @throws UnwarrantedOptimismException if overflow occurs
13841638Srgrimes     */
13851638Srgrimes    public static int subExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
13861638Srgrimes        try {
13871638Srgrimes            return Math.subtractExact(x, y);
13881638Srgrimes        } catch (final ArithmeticException e) {
13891638Srgrimes            throw new UnwarrantedOptimismException((long)x - (long)y, programPoint);
13901638Srgrimes        }
13911638Srgrimes    }
13921638Srgrimes
13931638Srgrimes    /**
13941638Srgrimes     * Wrapper for subExact
13951638Srgrimes     *
13961638Srgrimes     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
13971638Srgrimes     * containing the result and the program point of the failure
13981638Srgrimes     *
13991638Srgrimes     * @param x first term
14001638Srgrimes     * @param y second term
14011638Srgrimes     * @param programPoint program point id
14021638Srgrimes     * @return the result
14031638Srgrimes     * @throws UnwarrantedOptimismException if overflow occurs
14041638Srgrimes     */
14051638Srgrimes    public static long subExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
14061638Srgrimes        try {
14071638Srgrimes            return Math.subtractExact(x, y);
14081638Srgrimes        } catch (final ArithmeticException e) {
14091638Srgrimes            throw new UnwarrantedOptimismException((double)x - (double)y, programPoint);
14101638Srgrimes        }
14111638Srgrimes    }
14121638Srgrimes
14131638Srgrimes    /**
14141638Srgrimes     * Wrapper for mulExact
14151638Srgrimes     *
14161638Srgrimes     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
14171638Srgrimes     * containing the result and the program point of the failure
14181638Srgrimes     *
14191638Srgrimes     * @param x first term
14201638Srgrimes     * @param y second term
14211638Srgrimes     * @param programPoint program point id
14221638Srgrimes     * @return the result
14231638Srgrimes     * @throws UnwarrantedOptimismException if overflow occurs
14241638Srgrimes     */
14251638Srgrimes    public static int mulExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
14261638Srgrimes        try {
14271638Srgrimes            return Math.multiplyExact(x, y);
14281638Srgrimes        } catch (final ArithmeticException e) {
14291638Srgrimes            throw new UnwarrantedOptimismException((long)x * (long)y, programPoint);
14301638Srgrimes        }
14311638Srgrimes    }
14321638Srgrimes
14331638Srgrimes    /**
14341638Srgrimes     * Wrapper for mulExact
14356197Sjkh     *
14366197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
14376197Sjkh     * containing the result and the program point of the failure
14386197Sjkh     *
14396197Sjkh     * @param x first term
14406197Sjkh     * @param y second term
14416519Sjoerg     * @param programPoint program point id
14426197Sjkh     * @return the result
14436197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
14446197Sjkh     */
14456197Sjkh    public static long mulExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
14466197Sjkh        try {
14476519Sjoerg            return Math.multiplyExact(x, y);
14486197Sjkh        } catch (final ArithmeticException e) {
14496197Sjkh            throw new UnwarrantedOptimismException((double)x * (double)y, programPoint);
14506197Sjkh        }
14516197Sjkh    }
14526197Sjkh
14536519Sjoerg    /**
14546197Sjkh     * Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
14556197Sjkh     * int.
14566197Sjkh     *
14576197Sjkh     * @param x first term
14586197Sjkh     * @param y second term
14596519Sjoerg     * @param programPoint program point id
14606197Sjkh     * @return the result
14616197Sjkh     * @throws UnwarrantedOptimismException if the result of the division can't be represented as int.
14626197Sjkh     */
14636197Sjkh    public static int divExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
14646197Sjkh        final int res;
14656519Sjoerg        try {
14666197Sjkh            res = x / y;
14676197Sjkh        } catch (final ArithmeticException e) {
14686197Sjkh            assert y == 0; // Only div by zero anticipated
14696197Sjkh            throw new UnwarrantedOptimismException(x > 0 ? Double.POSITIVE_INFINITY : x < 0 ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint);
14706197Sjkh        }
14716519Sjoerg        final int rem = x % y;
14726197Sjkh        if (rem == 0) {
14736197Sjkh            return res;
14746197Sjkh        }
14756197Sjkh        // go directly to double here, as anything with non zero remainder is a floating point number in JavaScript
14766197Sjkh        throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
14776197Sjkh    }
14786197Sjkh
14796519Sjoerg    /**
14806197Sjkh     * Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
14816197Sjkh     *
14826197Sjkh     * @param x first term
14836197Sjkh     * @param y second term
14846197Sjkh     * @param programPoint program point id
14856519Sjoerg     * @return the result
14866197Sjkh     * @throws UnwarrantedOptimismException if the modulo can't be represented as int.
14876197Sjkh     */
14886197Sjkh    public static int remExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException {
14896197Sjkh        try {
14906197Sjkh            return x % y;
14916519Sjoerg        } catch (final ArithmeticException e) {
14926197Sjkh            assert y == 0; // Only mod by zero anticipated
14936197Sjkh            throw new UnwarrantedOptimismException(Double.NaN, programPoint);
14946197Sjkh        }
14956197Sjkh    }
14966197Sjkh
14976519Sjoerg    /**
14986197Sjkh     * Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
14996197Sjkh     * long.
15006197Sjkh     *
15016197Sjkh     * @param x first term
15026197Sjkh     * @param y second term
15036519Sjoerg     * @param programPoint program point id
15046197Sjkh     * @return the result
15056197Sjkh     * @throws UnwarrantedOptimismException if the result of the division can't be represented as long.
15066197Sjkh     */
15076197Sjkh    public static long divExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
15086197Sjkh        final long res;
15096519Sjoerg        try {
15106197Sjkh            res = x / y;
15116197Sjkh        } catch (final ArithmeticException e) {
15126197Sjkh            assert y == 0L; // Only div by zero anticipated
15136197Sjkh            throw new UnwarrantedOptimismException(x > 0L ? Double.POSITIVE_INFINITY : x < 0L ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint);
15146197Sjkh        }
15156197Sjkh        final long rem = x % y;
15166197Sjkh        if (rem == 0L) {
15176197Sjkh            return res;
15186197Sjkh        }
15196519Sjoerg        throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
15206197Sjkh    }
15216197Sjkh
15226197Sjkh    /**
15236197Sjkh     * Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
15246197Sjkh     *
15256197Sjkh     * @param x first term
15266519Sjoerg     * @param y second term
15276197Sjkh     * @param programPoint program point id
15286197Sjkh     * @return the result
15296197Sjkh     * @throws UnwarrantedOptimismException if the modulo can't be represented as int.
15306197Sjkh     */
15316197Sjkh    public static long remExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
15326197Sjkh        try {
15336519Sjoerg            return x % y;
15346197Sjkh        } catch (final ArithmeticException e) {
15356197Sjkh            assert y == 0L; // Only mod by zero anticipated
15366197Sjkh            throw new UnwarrantedOptimismException(Double.NaN, programPoint);
15376197Sjkh        }
15386197Sjkh    }
15396197Sjkh
15406519Sjoerg    /**
15416197Sjkh     * Wrapper for decrementExact
15426197Sjkh     *
15436197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
15446197Sjkh     * containing the result and the program point of the failure
15456197Sjkh     *
15466197Sjkh     * @param x number to negate
15476519Sjoerg     * @param programPoint program point id
15486197Sjkh     * @return the result
15496197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
15506197Sjkh     */
15516197Sjkh    public static int decrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
15526197Sjkh        try {
15536197Sjkh            return Math.decrementExact(x);
15546519Sjoerg        } catch (final ArithmeticException e) {
15556197Sjkh            throw new UnwarrantedOptimismException((long)x - 1, programPoint);
15566197Sjkh        }
15576197Sjkh    }
15586197Sjkh
15596197Sjkh    /**
15606197Sjkh     * Wrapper for decrementExact
15616197Sjkh     *
15626197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
15636197Sjkh     * containing the result and the program point of the failure
15646197Sjkh     *
15656519Sjoerg     * @param x number to negate
15666197Sjkh     * @param programPoint program point id
15676197Sjkh     * @return the result
15686197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
15696197Sjkh     */
15706197Sjkh    public static long decrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
15716197Sjkh        try {
15726519Sjoerg            return Math.decrementExact(x);
15736197Sjkh        } catch (final ArithmeticException e) {
15746197Sjkh            throw new UnwarrantedOptimismException((double)x - 1L, programPoint);
15756197Sjkh        }
15766197Sjkh    }
15776197Sjkh
15786197Sjkh    /**
15796519Sjoerg     * Wrapper for incrementExact
15806197Sjkh     *
15816197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
15826197Sjkh     * containing the result and the program point of the failure
15836197Sjkh     *
15846197Sjkh     * @param x the number to increment
15856197Sjkh     * @param programPoint program point id
15866519Sjoerg     * @return the result
15876197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
15886197Sjkh     */
15896197Sjkh    public static int incrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
15906197Sjkh        try {
15916197Sjkh            return Math.incrementExact(x);
15926197Sjkh        } catch (final ArithmeticException e) {
15936519Sjoerg            throw new UnwarrantedOptimismException((long)x + 1, programPoint);
15946197Sjkh        }
15956197Sjkh    }
15966197Sjkh
15976197Sjkh    /**
15986197Sjkh     * Wrapper for incrementExact
15996197Sjkh     *
16006519Sjoerg     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
16016197Sjkh     * containing the result and the program point of the failure
16026197Sjkh     *
16036197Sjkh     * @param x the number to increment
16046197Sjkh     * @param programPoint program point id
16056197Sjkh     * @return the result
16066197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
16076519Sjoerg     */
16086197Sjkh    public static long incrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
16096197Sjkh        try {
16106197Sjkh            return Math.incrementExact(x);
16116197Sjkh        } catch (final ArithmeticException e) {
16126197Sjkh            throw new UnwarrantedOptimismException((double)x + 1L, programPoint);
16136197Sjkh        }
16146197Sjkh    }
16156197Sjkh
16166197Sjkh    /**
16176197Sjkh     * Wrapper for negateExact
16186197Sjkh     *
16196197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
16206197Sjkh     * containing the result and the program point of the failure
16216197Sjkh     *
16226197Sjkh     * @param x the number to negate
16236197Sjkh     * @param programPoint program point id
16246197Sjkh     * @return the result
16256197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
16266197Sjkh     */
16276197Sjkh    public static int negateExact(final int x, final int programPoint) throws UnwarrantedOptimismException {
16286197Sjkh        try {
16296197Sjkh            if (x == 0) {
16306197Sjkh                throw new UnwarrantedOptimismException(-0.0, programPoint);
16316197Sjkh            }
16326197Sjkh            return Math.negateExact(x);
16336197Sjkh        } catch (final ArithmeticException e) {
16346197Sjkh            throw new UnwarrantedOptimismException(-(long)x, programPoint);
16356197Sjkh        }
16366197Sjkh    }
16376197Sjkh
16386197Sjkh    /**
16396197Sjkh     * Wrapper for negateExact
16406197Sjkh     *
16416197Sjkh     * Catches ArithmeticException and rethrows as UnwarrantedOptimismException
16426197Sjkh     * containing the result and the program point of the failure
16436197Sjkh     *
16446197Sjkh     * @param x the number to negate
16456197Sjkh     * @param programPoint program point id
16466197Sjkh     * @return the result
16476197Sjkh     * @throws UnwarrantedOptimismException if overflow occurs
16486197Sjkh     */
16496197Sjkh    public static long negateExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
16506197Sjkh        try {
16516197Sjkh            if (x == 0L) {
16526197Sjkh                throw new UnwarrantedOptimismException(-0.0, programPoint);
16536197Sjkh            }
16546197Sjkh            return Math.negateExact(x);
16556197Sjkh        } catch (final ArithmeticException e) {
16566197Sjkh            throw new UnwarrantedOptimismException(-(double)x, programPoint);
16576197Sjkh        }
16586197Sjkh    }
16596197Sjkh
16606197Sjkh    /**
16616197Sjkh     * Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
16626197Sjkh     *
16636197Sjkh     * @param type the type
16646197Sjkh     *
16656197Sjkh     * @return the accessor index, or -1 if no accessor of this type exists
16666197Sjkh     */
16676197Sjkh    public static int getAccessorTypeIndex(final Type type) {
16686197Sjkh        return getAccessorTypeIndex(type.getTypeClass());
16696197Sjkh    }
16706197Sjkh
16716197Sjkh    /**
16726197Sjkh     * Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
16736197Sjkh     *
16746197Sjkh     * Note that this is hardcoded with respect to the dynamic contents of the accessor
16756197Sjkh     * types array for speed. Hotspot got stuck with this as 5% of the runtime in
16766197Sjkh     * a benchmark when it looped over values and increased an index counter. :-(
16776197Sjkh     *
16786197Sjkh     * @param type the type
16796197Sjkh     *
16806197Sjkh     * @return the accessor index, or -1 if no accessor of this type exists
16816197Sjkh     */
16826197Sjkh    public static int getAccessorTypeIndex(final Class<?> type) {
16836197Sjkh        if (type == null) {
16846197Sjkh            return TYPE_UNDEFINED_INDEX;
16851638Srgrimes        } else if (type == int.class) {
16861638Srgrimes            return TYPE_INT_INDEX;
16871638Srgrimes        } else if (type == long.class) {
16881638Srgrimes            return TYPE_LONG_INDEX;
16891638Srgrimes        } else if (type == double.class) {
16901638Srgrimes            return TYPE_DOUBLE_INDEX;
16911638Srgrimes        } else if (!type.isPrimitive()) {
16921638Srgrimes            return TYPE_OBJECT_INDEX;
16931638Srgrimes        }
16941638Srgrimes        return -1;
16951638Srgrimes    }
16961638Srgrimes
16971638Srgrimes    /**
16981638Srgrimes     * Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
16991638Srgrimes     * Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always
17001638Srgrimes     * go to a type of higher index
17011638Srgrimes     *
17021638Srgrimes     * @param index accessor type index
17031638Srgrimes     *
17041638Srgrimes     * @return a type corresponding to the index.
17051638Srgrimes     */
17061638Srgrimes
17071638Srgrimes    public static Type getAccessorType(final int index) {
17081638Srgrimes        return ACCESSOR_TYPES.get(index);
17091638Srgrimes    }
17101638Srgrimes
17111638Srgrimes    /**
17121638Srgrimes     * Return the number of accessor types available.
17131638Srgrimes     *
17141638Srgrimes     * @return number of accessor types in system
17151638Srgrimes     */
17161638Srgrimes    public static int getNumberOfAccessorTypes() {
17171638Srgrimes        return ACCESSOR_TYPES.size();
17181638Srgrimes    }
17191638Srgrimes
17201638Srgrimes    private static double parseRadix(final char chars[], final int start, final int length, final int radix) {
17211638Srgrimes        int pos = 0;
17221638Srgrimes
17231638Srgrimes        for (int i = start; i < length ; i++) {
17241638Srgrimes            if (digit(chars[i], radix) == -1) {
17251638Srgrimes                return Double.NaN;
17261638Srgrimes            }
17271638Srgrimes            pos++;
17281638Srgrimes        }
17291638Srgrimes
17301638Srgrimes        if (pos == 0) {
17311638Srgrimes            return Double.NaN;
17321638Srgrimes        }
17331638Srgrimes
17341638Srgrimes        double value = 0.0;
17351638Srgrimes        for (int i = start; i < start + pos; i++) {
17361638Srgrimes            value *= radix;
17371638Srgrimes            value += digit(chars[i], radix);
17381638Srgrimes        }
17391638Srgrimes
17401638Srgrimes        return value;
17411638Srgrimes    }
17421638Srgrimes
17431638Srgrimes    private static double toNumberGeneric(final Object obj) {
17441638Srgrimes        if (obj == null) {
17451638Srgrimes            return +0.0;
17461638Srgrimes        }
17471638Srgrimes
17481638Srgrimes        if (obj instanceof String) {
17491638Srgrimes            return toNumber((String)obj);
17501638Srgrimes        }
17511638Srgrimes
17521638Srgrimes        if (obj instanceof ConsString) {
17531638Srgrimes            return toNumber(obj.toString());
17541638Srgrimes        }
17551638Srgrimes
17561638Srgrimes        if (obj instanceof Boolean) {
17571638Srgrimes            return (Boolean)obj ? 1 : +0.0;
17581638Srgrimes        }
17591638Srgrimes
17601638Srgrimes        if (obj instanceof ScriptObject) {
17611638Srgrimes            return toNumber((ScriptObject)obj);
17621638Srgrimes        }
17631638Srgrimes
17641638Srgrimes        if (obj instanceof JSObject) {
17651638Srgrimes            return ((JSObject)obj).toNumber();
17661638Srgrimes        }
17671638Srgrimes
17681638Srgrimes        return Double.NaN;
17691638Srgrimes    }
17701638Srgrimes
17711638Srgrimes    private static Object invoke(final MethodHandle mh, final Object arg) {
17721638Srgrimes        try {
17731638Srgrimes            return mh.invoke(arg);
17741638Srgrimes        } catch (final RuntimeException | Error e) {
17751638Srgrimes            throw e;
17761638Srgrimes        } catch (final Throwable t) {
17771638Srgrimes            throw new RuntimeException(t);
17781638Srgrimes        }
17791638Srgrimes    }
17801638Srgrimes
17811638Srgrimes    /**
17821638Srgrimes     * Create a method handle constant of the correct primitive type
17831638Srgrimes     * for a constant object
17841638Srgrimes     * @param o object
17851638Srgrimes     * @return constant function that returns object
17861638Srgrimes     */
17871638Srgrimes    public static MethodHandle unboxConstant(final Object o) {
17881638Srgrimes        if (o != null) {
17891638Srgrimes            if (o.getClass() == Integer.class) {
17901638Srgrimes                return MH.constant(int.class, ((Integer)o).intValue());
17911638Srgrimes            } else if (o.getClass() == Long.class) {
17921638Srgrimes                return MH.constant(long.class, ((Long)o).longValue());
17931638Srgrimes            } else if (o.getClass() == Double.class) {
17941638Srgrimes                return MH.constant(double.class, ((Double)o).doubleValue());
17951638Srgrimes            }
17961638Srgrimes        }
17971638Srgrimes        return MH.constant(Object.class, o);
17981638Srgrimes    }
17991638Srgrimes
18001638Srgrimes    /**
18011638Srgrimes     * Get the unboxed (primitive) type for an object
18021638Srgrimes     * @param o object
18031638Srgrimes     * @return primive type or Object.class if not primitive
18041638Srgrimes     */
18051638Srgrimes    public static Class<?> unboxedFieldType(final Object o) {
18061638Srgrimes        if (OBJECT_FIELDS_ONLY) {
18071638Srgrimes            return Object.class;
18081638Srgrimes        }
18091638Srgrimes
18101638Srgrimes        if (o == null) {
18111638Srgrimes            return Object.class;
18121638Srgrimes        } else if (o.getClass() == Integer.class) {
18131638Srgrimes            return int.class;
18141638Srgrimes        } else if (o.getClass() == Long.class) {
18151638Srgrimes            return long.class;
18161638Srgrimes        } else if (o.getClass() == Double.class) {
18171638Srgrimes            return double.class;
18181638Srgrimes        } else {
18191638Srgrimes            return Object.class;
18201638Srgrimes        }
18211638Srgrimes    }
18221638Srgrimes
18231638Srgrimes    private static final List<MethodHandle> toUnmodifiableList(final MethodHandle... methodHandles) {
18241638Srgrimes        return Collections.unmodifiableList(Arrays.asList(methodHandles));
18251638Srgrimes    }
18261638Srgrimes}
18271638Srgrimes