--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java 2020-04-15 18:49:54.000000000 +0530 +++ /dev/null 2020-04-15 18:49:54.000000000 +0530 @@ -1,1813 +0,0 @@ -/* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.nashorn.internal.runtime; - -import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; -import static jdk.nashorn.internal.lookup.Lookup.MH; -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import jdk.dynalink.SecureLookupSupplier; -import jdk.dynalink.beans.StaticClass; -import jdk.nashorn.api.scripting.JSObject; -import jdk.nashorn.internal.codegen.CompilerConstants.Call; -import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.objects.Global; -import jdk.nashorn.internal.objects.NativeSymbol; -import jdk.nashorn.internal.parser.Lexer; -import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; -import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion; -import jdk.nashorn.internal.runtime.linker.Bootstrap; - -/** - * Representation for ECMAScript types - this maps directly to the ECMA script standard - */ -public enum JSType { - /** The undefined type */ - UNDEFINED("undefined"), - - /** The null type */ - NULL("object"), - - /** The boolean type */ - BOOLEAN("boolean"), - - /** The number type */ - NUMBER("number"), - - /** The string type */ - STRING("string"), - - /** The object type */ - OBJECT("object"), - - /** The function type */ - FUNCTION("function"), - - /** The symbol type */ - SYMBOL("symbol"); - - /** The type name as returned by ECMAScript "typeof" operator*/ - private final String typeName; - - /** Max value for an uint32 in JavaScript */ - public static final long MAX_UINT = 0xFFFF_FFFFL; - - private static final MethodHandles.Lookup JSTYPE_LOOKUP = MethodHandles.lookup(); - - /** JavaScript compliant conversion function from Object to boolean */ - public static final Call TO_BOOLEAN = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, Object.class); - - /** JavaScript compliant conversion function from number to boolean */ - public static final Call TO_BOOLEAN_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, double.class); - - /** JavaScript compliant conversion function from int to boolean */ - public static final Call TO_BOOLEAN_I = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, int.class); - - /** JavaScript compliant conversion function from Object to integer */ - public static final Call TO_INTEGER = staticCall(JSTYPE_LOOKUP, JSType.class, "toInteger", int.class, Object.class); - - /** JavaScript compliant conversion function from Object to long */ - public static final Call TO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, Object.class); - - /** JavaScript compliant conversion function from double to long */ - public static final Call TO_LONG_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toLong", long.class, double.class); - - /** JavaScript compliant conversion function from Object to number */ - public static final Call TO_NUMBER = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumber", double.class, Object.class); - - /** JavaScript compliant conversion function from Object to number with type check */ - public static final Call TO_NUMBER_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toNumberOptimistic", double.class, Object.class, int.class); - - /** JavaScript compliant conversion function from Object to String */ - public static final Call TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, Object.class); - - /** JavaScript compliant conversion function from Object to int32 */ - public static final Call TO_INT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, Object.class); - - /** JavaScript compliant conversion function from Object to int32 */ - public static final Call TO_INT32_L = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, long.class); - - /** JavaScript compliant conversion function from Object to int32 with type check */ - public static final Call TO_INT32_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32Optimistic", int.class, Object.class, int.class); - - /** JavaScript compliant conversion function from double to int32 */ - public static final Call TO_INT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, double.class); - - /** JavaScript compliant conversion function from int to uint32 */ - public static final Call TO_UINT32_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32Optimistic", int.class, int.class, int.class); - - /** JavaScript compliant conversion function from int to uint32 */ - public static final Call TO_UINT32_DOUBLE = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32Double", double.class, int.class); - - /** JavaScript compliant conversion function from Object to uint32 */ - public static final Call TO_UINT32 = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, Object.class); - - /** JavaScript compliant conversion function from number to uint32 */ - public static final Call TO_UINT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, double.class); - - /** JavaScript compliant conversion function from number to String */ - public static final Call TO_STRING_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toString", String.class, double.class); - - /** Combined call to toPrimitive followed by toString. */ - public static final Call TO_PRIMITIVE_TO_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToString", String.class, Object.class); - - /** Combined call to toPrimitive followed by toCharSequence. */ - public static final Call TO_PRIMITIVE_TO_CHARSEQUENCE = staticCall(JSTYPE_LOOKUP, JSType.class, "toPrimitiveToCharSequence", CharSequence.class, Object.class); - - /** Throw an unwarranted optimism exception */ - public static final Call THROW_UNWARRANTED = staticCall(JSTYPE_LOOKUP, JSType.class, "throwUnwarrantedOptimismException", Object.class, Object.class, int.class); - - /** Add exact wrapper for potentially overflowing integer operations */ - public static final Call ADD_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", int.class, int.class, int.class, int.class); - - /** Sub exact wrapper for potentially overflowing integer operations */ - public static final Call SUB_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", int.class, int.class, int.class, int.class); - - /** Multiply exact wrapper for potentially overflowing integer operations */ - public static final Call MUL_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", int.class, int.class, int.class, int.class); - - /** Div exact wrapper for potentially integer division that turns into float point */ - public static final Call DIV_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class); - - /** Div zero wrapper for integer division that handles (0/0)|0 == 0 */ - public static final Call DIV_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", int.class, int.class, int.class); - - /** Mod zero wrapper for integer division that handles (0%0)|0 == 0 */ - public static final Call REM_ZERO = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", int.class, int.class, int.class); - - /** Mod exact wrapper for potentially integer remainders that turns into float point */ - public static final Call REM_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class); - - /** Decrement exact wrapper for potentially overflowing integer operations */ - public static final Call DECREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact", int.class, int.class, int.class); - - /** Increment exact wrapper for potentially overflowing integer operations */ - public static final Call INCREMENT_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact", int.class, int.class, int.class); - - /** Negate exact exact wrapper for potentially overflowing integer operations */ - public static final Call NEGATE_EXACT = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact", int.class, int.class, int.class); - - /** Method handle to convert a JS Object to a Java array. */ - public static final Call TO_JAVA_ARRAY = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaArray", Object.class, Object.class, Class.class); - - /** Method handle to convert a JS Object to a Java array. */ - public static final Call TO_JAVA_ARRAY_WITH_LOOKUP = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaArrayWithLookup", Object.class, Object.class, Class.class, SecureLookupSupplier.class); - - /** Method handle for void returns. */ - public static final Call VOID_RETURN = staticCall(JSTYPE_LOOKUP, JSType.class, "voidReturn", void.class); - - /** Method handle for isString method */ - public static final Call IS_STRING = staticCall(JSTYPE_LOOKUP, JSType.class, "isString", boolean.class, Object.class); - - /** Method handle for isNumber method */ - public static final Call IS_NUMBER = staticCall(JSTYPE_LOOKUP, JSType.class, "isNumber", boolean.class, Object.class); - - /** - * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide - * in the dual--fields world - */ - private static final List ACCESSOR_TYPES = Collections.unmodifiableList( - Arrays.asList( - Type.INT, - Type.NUMBER, - Type.OBJECT)); - - /** table index for undefined type - hard coded so it can be used in switches at compile time */ - public static final int TYPE_UNDEFINED_INDEX = -1; - /** table index for integer type - hard coded so it can be used in switches at compile time */ - public static final int TYPE_INT_INDEX = 0; //getAccessorTypeIndex(int.class); - /** table index for double type - hard coded so it can be used in switches at compile time */ - public static final int TYPE_DOUBLE_INDEX = 1; //getAccessorTypeIndex(double.class); - /** table index for object type - hard coded so it can be used in switches at compile time */ - public static final int TYPE_OBJECT_INDEX = 2; //getAccessorTypeIndex(Object.class); - - /** object conversion quickies with JS semantics - used for return value and parameter filter */ - public static final List CONVERT_OBJECT = toUnmodifiableList( - JSType.TO_INT32.methodHandle(), - JSType.TO_NUMBER.methodHandle(), - null - ); - - /** - * object conversion quickies with JS semantics - used for return value and parameter filter, optimistic - * throws exception upon incompatible type (asking for a narrower one than the storage) - */ - public static final List CONVERT_OBJECT_OPTIMISTIC = toUnmodifiableList( - JSType.TO_INT32_OPTIMISTIC.methodHandle(), - JSType.TO_NUMBER_OPTIMISTIC.methodHandle(), - null - ); - - /** The value of Undefined cast to an int32 */ - public static final int UNDEFINED_INT = 0; - /** The value of Undefined cast to a long */ - public static final long UNDEFINED_LONG = 0L; - /** The value of Undefined cast to a double */ - public static final double UNDEFINED_DOUBLE = Double.NaN; - - // Minimum and maximum range between which every long value can be precisely represented as a double. - private static final long MAX_PRECISE_DOUBLE = 1L << 53; - private static final long MIN_PRECISE_DOUBLE = -MAX_PRECISE_DOUBLE; - - /** - * Method handles for getters that return undefined coerced - * to the appropriate type - */ - public static final List GET_UNDEFINED = toUnmodifiableList( - MH.constant(int.class, UNDEFINED_INT), - MH.constant(double.class, UNDEFINED_DOUBLE), - MH.constant(Object.class, Undefined.getUndefined()) - ); - - private static final double INT32_LIMIT = 4294967296.0; - - /** - * Constructor - * - * @param typeName the type name - */ - private JSType(final String typeName) { - this.typeName = typeName; - } - - /** - * The external type name as returned by ECMAScript "typeof" operator - * - * @return type name for this type - */ - public final String typeName() { - return this.typeName; - } - - /** - * Return the JSType for a given object - * - * @param obj an object - * - * @return the JSType for the object - */ - public static JSType of(final Object obj) { - // Order of these statements is tuned for performance (see JDK-8024476) - if (obj == null) { - return JSType.NULL; - } - - if (obj instanceof ScriptObject) { - return obj instanceof ScriptFunction ? JSType.FUNCTION : JSType.OBJECT; - } - - if (obj instanceof Boolean) { - return JSType.BOOLEAN; - } - - if (isString(obj)) { - return JSType.STRING; - } - - if (isNumber(obj)) { - return JSType.NUMBER; - } - - if (obj instanceof Symbol) { - return JSType.SYMBOL; - } - - if (obj == ScriptRuntime.UNDEFINED) { - return JSType.UNDEFINED; - } - - return Bootstrap.isCallable(obj) ? JSType.FUNCTION : JSType.OBJECT; - } - - /** - * Similar to {@link #of(Object)}, but does not distinguish between {@link #FUNCTION} and {@link #OBJECT}, returning - * {@link #OBJECT} in both cases. The distinction is costly, and the EQ and STRICT_EQ predicates don't care about it - * so we maintain this version for their use. - * - * @param obj an object - * - * @return the JSType for the object; returns {@link #OBJECT} instead of {@link #FUNCTION} for functions. - */ - public static JSType ofNoFunction(final Object obj) { - // Order of these statements is tuned for performance (see JDK-8024476) - if (obj == null) { - return JSType.NULL; - } - - if (obj instanceof ScriptObject) { - return JSType.OBJECT; - } - - if (obj instanceof Boolean) { - return JSType.BOOLEAN; - } - - if (isString(obj)) { - return JSType.STRING; - } - - if (isNumber(obj)) { - return JSType.NUMBER; - } - - if (obj == ScriptRuntime.UNDEFINED) { - return JSType.UNDEFINED; - } - - if (obj instanceof Symbol) { - return JSType.SYMBOL; - } - - return JSType.OBJECT; - } - - /** - * Void return method handle glue - */ - public static void voidReturn() { - //empty - //TODO: fix up SetMethodCreator better so we don't need this stupid thing - } - - /** - * Returns true if double number can be represented as an int - * - * @param number a long to inspect - * - * @return true for int representable longs - */ - public static boolean isRepresentableAsInt(final long number) { - return (int)number == number; - } - - /** - * Returns true if double number can be represented as an int. Note that it returns true for negative - * zero. If you need to exclude negative zero, use {@link #isStrictlyRepresentableAsInt(double)}. - * - * @param number a double to inspect - * - * @return true for int representable doubles - */ - public static boolean isRepresentableAsInt(final double number) { - return (int)number == number; - } - - /** - * Returns true if double number can be represented as an int. Note that it returns false for negative - * zero. If you don't need to distinguish negative zero, use {@link #isRepresentableAsInt(double)}. - * - * @param number a double to inspect - * - * @return true for int representable doubles - */ - public static boolean isStrictlyRepresentableAsInt(final double number) { - return isRepresentableAsInt(number) && isNotNegativeZero(number); - } - - /** - * Returns true if Object can be represented as an int - * - * @param obj an object to inspect - * - * @return true for int representable objects - */ - public static boolean isRepresentableAsInt(final Object obj) { - if (obj instanceof Number) { - return isRepresentableAsInt(((Number)obj).doubleValue()); - } - return false; - } - - /** - * Returns true if double number can be represented as a long. Note that it returns true for negative - * zero. - * - * @param number a double to inspect - * @return true for long representable doubles - */ - public static boolean isRepresentableAsLong(final double number) { - return (long)number == number; - } - - /** - * Returns true if long number can be represented as double without loss of precision. - * @param number a long number - * @return true if the double representation does not lose precision - */ - public static boolean isRepresentableAsDouble(final long number) { - return MAX_PRECISE_DOUBLE >= number && number >= MIN_PRECISE_DOUBLE; - } - - /** - * Returns true if the number is not the negative zero ({@code -0.0d}). - * @param number the number to test - * @return true if it is not the negative zero, false otherwise. - */ - private static boolean isNotNegativeZero(final double number) { - return Double.doubleToRawLongBits(number) != 0x8000000000000000L; - } - - /** - * Check whether an object is primitive - * - * @param obj an object - * - * @return true if object is primitive (includes null and undefined) - */ - public static boolean isPrimitive(final Object obj) { - return obj == null || - obj == ScriptRuntime.UNDEFINED || - isString(obj) || - isNumber(obj) || - obj instanceof Boolean || - obj instanceof Symbol; - } - - /** - * Primitive converter for an object - * - * @param obj an object - * - * @return primitive form of the object - */ - public static Object toPrimitive(final Object obj) { - return toPrimitive(obj, null); - } - - /** - * Primitive converter for an object including type hint - * See ECMA 9.1 ToPrimitive - * - * @param obj an object - * @param hint a type hint - * - * @return the primitive form of the object - */ - public static Object toPrimitive(final Object obj, final Class hint) { - if (obj instanceof ScriptObject) { - return toPrimitive((ScriptObject)obj, hint); - } else if (isPrimitive(obj)) { - return obj; - } else if (hint == Number.class && obj instanceof Number) { - return ((Number) obj).doubleValue(); - } else if (obj instanceof JSObject) { - return toPrimitive((JSObject)obj, hint); - } else if (obj instanceof StaticClass) { - final String name = ((StaticClass)obj).getRepresentedClass().getName(); - return new StringBuilder(12 + name.length()).append("[JavaClass ").append(name).append(']').toString(); - } - return obj.toString(); - } - - private static Object toPrimitive(final ScriptObject sobj, final Class hint) { - return requirePrimitive(sobj.getDefaultValue(hint)); - } - - private static Object requirePrimitive(final Object result) { - if (!isPrimitive(result)) { - throw typeError("bad.default.value", result.toString()); - } - return result; - } - - /** - * Primitive converter for a {@link JSObject} including type hint. Invokes - * {@link JSObject#getDefaultValue(Class)} and translates any thrown {@link UnsupportedOperationException} - * to a ECMAScript {@code TypeError}. - * See ECMA 9.1 ToPrimitive - * - * @param jsobj a JSObject - * @param hint a type hint - * - * @return the primitive form of the JSObject - */ - public static Object toPrimitive(final JSObject jsobj, final Class hint) { - try { - return requirePrimitive(jsobj.getDefaultValue(hint)); - } catch (final UnsupportedOperationException e) { - throw new ECMAException(Context.getGlobal().newTypeError(e.getMessage()), e); - } - } - - /** - * Combines a hintless toPrimitive and a toString call. - * - * @param obj an object - * - * @return the string form of the primitive form of the object - */ - public static String toPrimitiveToString(final Object obj) { - return toString(toPrimitive(obj)); - } - - /** - * Like {@link #toPrimitiveToString(Object)}, but avoids conversion of ConsString to String. - * - * @param obj an object - * @return the CharSequence form of the primitive form of the object - */ - public static CharSequence toPrimitiveToCharSequence(final Object obj) { - return toCharSequence(toPrimitive(obj)); - } - - /** - * JavaScript compliant conversion of number to boolean - * - * @param num a number - * - * @return a boolean - */ - public static boolean toBoolean(final double num) { - return num != 0 && !Double.isNaN(num); - } - - /** - * JavaScript compliant conversion of int to boolean - * - * @param num an int - * - * @return a boolean - */ - public static boolean toBoolean(final int num) { - return num != 0; - } - - /** - * JavaScript compliant conversion of Object to boolean - * See ECMA 9.2 ToBoolean - * - * @param obj an object - * - * @return a boolean - */ - public static boolean toBoolean(final Object obj) { - if (obj instanceof Boolean) { - return (Boolean)obj; - } - - if (nullOrUndefined(obj)) { - return false; - } - - if (obj instanceof Number) { - final double num = ((Number)obj).doubleValue(); - return num != 0 && !Double.isNaN(num); - } - - if (isString(obj)) { - return ((CharSequence)obj).length() > 0; - } - - return true; - } - - - /** - * JavaScript compliant converter of Object to String - * See ECMA 9.8 ToString - * - * @param obj an object - * - * @return a string - */ - public static String toString(final Object obj) { - return toStringImpl(obj, false); - } - - /** - * See ES6 #7.1.14 - * @param obj key object - * @return property key - */ - public static Object toPropertyKey(final Object obj) { - return obj instanceof Symbol ? obj : toStringImpl(obj, false); - } - - /** - * If obj is an instance of {@link ConsString} cast to CharSequence, else return - * result of {@link #toString(Object)}. - * - * @param obj an object - * @return an instance of String or ConsString - */ - public static CharSequence toCharSequence(final Object obj) { - if (obj instanceof ConsString) { - return (CharSequence) obj; - } - return toString(obj); - } - - /** - * Returns true if object represents a primitive JavaScript string value. - * @param obj the object - * @return true if the object represents a primitive JavaScript string value. - */ - public static boolean isString(final Object obj) { - return obj instanceof String || obj instanceof ConsString; - } - - /** - * Returns true if object represents a primitive JavaScript number value. Note that we only - * treat wrapper objects of Java primitive number types as objects that can be fully represented - * as JavaScript numbers (doubles). This means we exclude {@code long} and special purpose Number - * instances such as {@link java.util.concurrent.atomic.AtomicInteger}, as well as arbitrary precision - * numbers such as {@link java.math.BigInteger}. - * - * @param obj the object - * @return true if the object represents a primitive JavaScript number value. - */ - public static boolean isNumber(final Object obj) { - if (obj != null) { - final Class c = obj.getClass(); - return c == Integer.class || c == Double.class || c == Float.class || c == Short.class || c == Byte.class; - } - return false; - } - - /** - * JavaScript compliant conversion of integer to String - * - * @param num an integer - * - * @return a string - */ - public static String toString(final int num) { - return Integer.toString(num); - } - - /** - * JavaScript compliant conversion of number to String - * See ECMA 9.8.1 - * - * @param num a number - * - * @return a string - */ - public static String toString(final double num) { - if (isRepresentableAsInt(num)) { - return Integer.toString((int)num); - } - - if (num == Double.POSITIVE_INFINITY) { - return "Infinity"; - } - - if (num == Double.NEGATIVE_INFINITY) { - return "-Infinity"; - } - - if (Double.isNaN(num)) { - return "NaN"; - } - - return DoubleConversion.toShortestString(num); - } - - /** - * JavaScript compliant conversion of number to String - * - * @param num a number - * @param radix a radix for the conversion - * - * @return a string - */ - public static String toString(final double num, final int radix) { - assert radix >= 2 && radix <= 36 : "invalid radix"; - - if (isRepresentableAsInt(num)) { - return Integer.toString((int)num, radix); - } - - if (num == Double.POSITIVE_INFINITY) { - return "Infinity"; - } - - if (num == Double.NEGATIVE_INFINITY) { - return "-Infinity"; - } - - if (Double.isNaN(num)) { - return "NaN"; - } - - if (num == 0.0) { - return "0"; - } - - final String chars = "0123456789abcdefghijklmnopqrstuvwxyz"; - final StringBuilder sb = new StringBuilder(); - - final boolean negative = num < 0.0; - final double signedNum = negative ? -num : num; - - double intPart = Math.floor(signedNum); - double decPart = signedNum - intPart; - - // encode integer part from least significant digit, then reverse - do { - final double remainder = intPart % radix; - sb.append(chars.charAt((int) remainder)); - intPart -= remainder; - intPart /= radix; - } while (intPart >= 1.0); - - if (negative) { - sb.append('-'); - } - sb.reverse(); - - // encode decimal part - if (decPart > 0.0) { - final int dot = sb.length(); - sb.append('.'); - do { - decPart *= radix; - final double d = Math.floor(decPart); - sb.append(chars.charAt((int)d)); - decPart -= d; - } while (decPart > 0.0 && sb.length() - dot < 1100); - // somewhat arbitrarily use same limit as V8 - } - - return sb.toString(); - } - - /** - * JavaScript compliant conversion of Object to number - * See ECMA 9.3 ToNumber - * - * @param obj an object - * - * @return a number - */ - public static double toNumber(final Object obj) { - if (obj instanceof Double) { - return (Double)obj; - } - if (obj instanceof Number) { - return ((Number)obj).doubleValue(); - } - return toNumberGeneric(obj); - } - - /** - * Converts an object for a comparison with a number. Almost identical to {@link #toNumber(Object)} but - * converts {@code null} to {@code NaN} instead of zero, so it won't compare equal to zero. - * - * @param obj an object - * - * @return a number - */ - public static double toNumberForEq(final Object obj) { - // we are not able to detect Symbol objects from codegen, so we need to - // handle them here to avoid throwing an error in toNumber conversion. - return obj == null || obj instanceof Symbol || obj instanceof NativeSymbol ? Double.NaN : toNumber(obj); - } - - /** - * Converts an object for strict comparison with a number. Returns {@code NaN} for any object that is not - * a {@link Number}, so only boxed numerics can compare strictly equal to numbers. - * - * @param obj an object - * - * @return a number - */ - public static double toNumberForStrictEq(final Object obj) { - if (obj instanceof Double) { - return (Double)obj; - } - if (isNumber(obj)) { - return ((Number)obj).doubleValue(); - } - return Double.NaN; - } - - /** - * Convert a long to the narrowest JavaScript Number type. This returns either a - * {@link Integer} or {@link Double} depending on the magnitude of {@code l}. - * @param l a long value - * @return the value converted to Integer or Double - */ - public static Number toNarrowestNumber(final long l) { - if (isRepresentableAsInt(l)) { - return Integer.valueOf((int) l); - } else { - return Double.valueOf(l); - } - } - - /** - * JavaScript compliant conversion of Boolean to number - * See ECMA 9.3 ToNumber - * - * @param b a boolean - * - * @return JS numeric value of the boolean: 1.0 or 0.0 - */ - public static double toNumber(final Boolean b) { - return b ? 1d : +0d; - } - - /** - * JavaScript compliant conversion of Object to number - * See ECMA 9.3 ToNumber - * - * @param obj an object - * - * @return a number - */ - public static double toNumber(final ScriptObject obj) { - return toNumber(toPrimitive(obj, Number.class)); - } - - /** - * Optimistic number conversion - throws UnwarrantedOptimismException if Object - * - * @param obj object to convert - * @param programPoint program point - * @return double - */ - public static double toNumberOptimistic(final Object obj, final int programPoint) { - if (obj != null) { - final Class clz = obj.getClass(); - if (clz == Double.class || clz == Integer.class || clz == Long.class) { - return ((Number)obj).doubleValue(); - } - } - throw new UnwarrantedOptimismException(obj, programPoint); - } - - /** - * Object to number conversion that delegates to either {@link #toNumber(Object)} or to - * {@link #toNumberOptimistic(Object, int)} depending on whether the program point is valid or not. - * @param obj the object to convert - * @param programPoint the program point; can be invalid. - * @return the value converted to a number - * @throws UnwarrantedOptimismException if the value can't be represented as a number and the program point is valid. - */ - public static double toNumberMaybeOptimistic(final Object obj, final int programPoint) { - return UnwarrantedOptimismException.isValid(programPoint) ? toNumberOptimistic(obj, programPoint) : toNumber(obj); - } - - /** - * Digit representation for a character - * - * @param ch a character - * @param radix radix - * - * @return the digit for this character - */ - public static int digit(final char ch, final int radix) { - return digit(ch, radix, false); - } - - /** - * Digit representation for a character - * - * @param ch a character - * @param radix radix - * @param onlyIsoLatin1 iso latin conversion only - * - * @return the digit for this character - */ - public static int digit(final char ch, final int radix, final boolean onlyIsoLatin1) { - final char maxInRadix = (char)('a' + (radix - 1) - 10); - final char c = Character.toLowerCase(ch); - - if (c >= 'a' && c <= maxInRadix) { - return Character.digit(ch, radix); - } - - if (Character.isDigit(ch)) { - if (!onlyIsoLatin1 || ch >= '0' && ch <= '9') { - return Character.digit(ch, radix); - } - } - - return -1; - } - - /** - * JavaScript compliant String to number conversion - * - * @param str a string - * - * @return a number - */ - public static double toNumber(final String str) { - int end = str.length(); - if (end == 0) { - return 0.0; // Empty string - } - - int start = 0; - char f = str.charAt(0); - - while (Lexer.isJSWhitespace(f)) { - if (++start == end) { - return 0.0d; // All whitespace string - } - f = str.charAt(start); - } - - // Guaranteed to terminate even without start >= end check, as the previous loop found at least one - // non-whitespace character. - while (Lexer.isJSWhitespace(str.charAt(end - 1))) { - end--; - } - - final boolean negative; - if (f == '-') { - if(++start == end) { - return Double.NaN; // Single-char "-" string - } - f = str.charAt(start); - negative = true; - } else { - if (f == '+') { - if (++start == end) { - return Double.NaN; // Single-char "+" string - } - f = str.charAt(start); - } - negative = false; - } - - final double value; - if (start + 1 < end && f == '0' && Character.toLowerCase(str.charAt(start + 1)) == 'x') { - //decode hex string - value = parseRadix(str.toCharArray(), start + 2, end, 16); - } else if (f == 'I' && end - start == 8 && str.regionMatches(start, "Infinity", 0, 8)) { - return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; - } else { - // Fast (no NumberFormatException) path to NaN for non-numeric strings. - for (int i = start; i < end; i++) { - f = str.charAt(i); - if ((f < '0' || f > '9') && f != '.' && f != 'e' && f != 'E' && f != '+' && f != '-') { - return Double.NaN; - } - } - try { - value = Double.parseDouble(str.substring(start, end)); - } catch (final NumberFormatException e) { - return Double.NaN; - } - } - - return negative ? -value : value; - } - - /** - * JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger - * - *

Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE} - * for double values that exceed the int range, including positive and negative Infinity. It is the - * caller's responsibility to handle such values correctly.

- * - * @param obj an object - * @return an integer - */ - public static int toInteger(final Object obj) { - return (int)toNumber(obj); - } - - /** - * Converts an Object to long. - * - *

Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE} - * for double values that exceed the long range, including positive and negative Infinity. It is the - * caller's responsibility to handle such values correctly.

- * - * @param obj an object - * @return a long - */ - public static long toLong(final Object obj) { - return obj instanceof Long ? ((Long)obj) : toLong(toNumber(obj)); - } - - /** - * Converts a double to long. - * - * @param num the double to convert - * @return the converted long value - */ - public static long toLong(final double num) { - return (long)num; - } - - /** - * JavaScript compliant Object to int32 conversion - * See ECMA 9.5 ToInt32 - * - * @param obj an object - * @return an int32 - */ - public static int toInt32(final Object obj) { - return toInt32(toNumber(obj)); - } - - /** - * Optimistic int conversion - throws UnwarrantedOptimismException if double, long or Object - * - * @param obj object to convert - * @param programPoint program point - * @return double - */ - public static int toInt32Optimistic(final Object obj, final int programPoint) { - if (obj != null && obj.getClass() == Integer.class) { - return ((Integer)obj); - } - throw new UnwarrantedOptimismException(obj, programPoint); - } - - /** - * Object to int conversion that delegates to either {@link #toInt32(Object)} or to - * {@link #toInt32Optimistic(Object, int)} depending on whether the program point is valid or not. - * @param obj the object to convert - * @param programPoint the program point; can be invalid. - * @return the value converted to int - * @throws UnwarrantedOptimismException if the value can't be represented as int and the program point is valid. - */ - public static int toInt32MaybeOptimistic(final Object obj, final int programPoint) { - return UnwarrantedOptimismException.isValid(programPoint) ? toInt32Optimistic(obj, programPoint) : toInt32(obj); - } - - /** - * JavaScript compliant long to int32 conversion - * - * @param num a long - * @return an int32 - */ - public static int toInt32(final long num) { - return (int)(num >= MIN_PRECISE_DOUBLE && num <= MAX_PRECISE_DOUBLE ? num : (long)(num % INT32_LIMIT)); - } - - - /** - * JavaScript compliant number to int32 conversion - * - * @param num a number - * @return an int32 - */ - public static int toInt32(final double num) { - return (int)doubleToInt32(num); - } - - /** - * JavaScript compliant Object to uint32 conversion - * - * @param obj an object - * @return a uint32 - */ - public static long toUint32(final Object obj) { - return toUint32(toNumber(obj)); - } - - /** - * JavaScript compliant number to uint32 conversion - * - * @param num a number - * @return a uint32 - */ - public static long toUint32(final double num) { - return doubleToInt32(num) & MAX_UINT; - } - - /** - * JavaScript compliant int to uint32 conversion - * - * @param num an int - * @return a uint32 - */ - public static long toUint32(final int num) { - return num & MAX_UINT; - } - - /** - * Optimistic JavaScript compliant int to uint32 conversion - * @param num an int - * @param pp the program point - * @return the uint32 value if it can be represented by an int - * @throws UnwarrantedOptimismException if uint32 value cannot be represented by an int - */ - public static int toUint32Optimistic(final int num, final int pp) { - if (num >= 0) { - return num; - } - throw new UnwarrantedOptimismException(toUint32Double(num), pp, Type.NUMBER); - } - - /** - * JavaScript compliant int to uint32 conversion with double return type - * @param num an int - * @return the uint32 value as double - */ - public static double toUint32Double(final int num) { - return toUint32(num); - } - - /** - * JavaScript compliant Object to uint16 conversion - * ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer) - * - * @param obj an object - * @return a uint16 - */ - public static int toUint16(final Object obj) { - return toUint16(toNumber(obj)); - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final int num) { - return num & 0xffff; - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final long num) { - return (int)num & 0xffff; - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final double num) { - return (int)doubleToInt32(num) & 0xffff; - } - - private static long doubleToInt32(final double num) { - final int exponent = Math.getExponent(num); - if (exponent < 31) { - return (long) num; // Fits into 32 bits - } - if (exponent >= 84) { - // Either infinite or NaN or so large that shift / modulo will produce 0 - // (52 bit mantissa + 32 bit target width). - return 0; - } - // This is rather slow and could probably be sped up using bit-fiddling. - final double d = num >= 0 ? Math.floor(num) : Math.ceil(num); - return (long)(d % INT32_LIMIT); - } - - /** - * Check whether a number is finite - * - * @param num a number - * @return true if finite - */ - public static boolean isFinite(final double num) { - return !Double.isInfinite(num) && !Double.isNaN(num); - } - - /** - * Convert a primitive to a double - * - * @param num a double - * @return a boxed double - */ - public static Double toDouble(final double num) { - return num; - } - - /** - * Convert a primitive to a double - * - * @param num a long - * @return a boxed double - */ - public static Double toDouble(final long num) { - return (double)num; - } - - /** - * Convert a primitive to a double - * - * @param num an int - * @return a boxed double - */ - public static Double toDouble(final int num) { - return (double)num; - } - - /** - * Convert a boolean to an Object - * - * @param bool a boolean - * @return a boxed boolean, its Object representation - */ - public static Object toObject(final boolean bool) { - return bool; - } - - /** - * Convert a number to an Object - * - * @param num an integer - * @return the boxed number - */ - public static Object toObject(final int num) { - return num; - } - - /** - * Convert a number to an Object - * - * @param num a long - * @return the boxed number - */ - public static Object toObject(final long num) { - return num; - } - - /** - * Convert a number to an Object - * - * @param num a double - * @return the boxed number - */ - public static Object toObject(final double num) { - return num; - } - - /** - * Identity converter for objects. - * - * @param obj an object - * @return the boxed number - */ - public static Object toObject(final Object obj) { - return obj; - } - - /** - * Object conversion. This is used to convert objects and numbers to their corresponding - * NativeObject type - * See ECMA 9.9 ToObject - * - * @param obj the object to convert - * - * @return the wrapped object - */ - public static Object toScriptObject(final Object obj) { - return toScriptObject(Context.getGlobal(), obj); - } - - /** - * Object conversion. This is used to convert objects and numbers to their corresponding - * NativeObject type - * See ECMA 9.9 ToObject - * - * @param global the global object - * @param obj the object to convert - * - * @return the wrapped object - */ - public static Object toScriptObject(final Global global, final Object obj) { - if (nullOrUndefined(obj)) { - throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj)); - } - - if (obj instanceof ScriptObject) { - return obj; - } - - return global.wrapAsObject(obj); - } - - /** - * Script object to Java array conversion. - * - * @param obj script object to be converted to Java array - * @param componentType component type of the destination array required - * @return converted Java array - */ - public static Object toJavaArray(final Object obj, final Class componentType) { - if (obj instanceof ScriptObject) { - return ((ScriptObject)obj).getArray().asArrayOfType(componentType); - } else if (obj instanceof JSObject) { - final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj); - final int len = (int) itr.getLength(); - final Object[] res = new Object[len]; - int idx = 0; - while (itr.hasNext()) { - res[idx++] = itr.next(); - } - return convertArray(res, componentType); - } else if(obj == null) { - return null; - } else { - throw new IllegalArgumentException("not a script object"); - } - } - - /** - * Script object to Java array conversion. - * - * @param obj script object to be converted to Java array - * @param componentType component type of the destination array required - * @param lookupSupplier supplier for the lookup of the class invoking the - * conversion. Can be used to use protection-domain specific converters - * if the target type is a SAM. - * @return converted Java array - */ - public static Object toJavaArrayWithLookup(final Object obj, final Class componentType, final SecureLookupSupplier lookupSupplier) { - return Bootstrap.getLinkerServices().getWithLookup(()->toJavaArray(obj, componentType), lookupSupplier); - } - - /** - * Java array to java array conversion - but using type conversions implemented by linker. - * - * @param src source array - * @param componentType component type of the destination array required - * @return converted Java array - */ - public static Object convertArray(final Object[] src, final Class componentType) { - if(componentType == Object.class) { - for(int i = 0; i < src.length; ++i) { - final Object e = src[i]; - if(e instanceof ConsString) { - src[i] = e.toString(); - } - } - } - - final int l = src.length; - final Object dst = Array.newInstance(componentType, l); - final MethodHandle converter = Bootstrap.getLinkerServices().getTypeConverter(Object.class, componentType); - try { - for (int i = 0; i < src.length; i++) { - Array.set(dst, i, invoke(converter, src[i])); - } - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } - return dst; - } - - /** - * Check if an object is null or undefined - * - * @param obj object to check - * - * @return true if null or undefined - */ - public static boolean nullOrUndefined(final Object obj) { - return obj == null || obj == ScriptRuntime.UNDEFINED; - } - - static String toStringImpl(final Object obj, final boolean safe) { - if (obj instanceof String) { - return (String)obj; - } - - if (obj instanceof ConsString) { - return obj.toString(); - } - - if (isNumber(obj)) { - return toString(((Number)obj).doubleValue()); - } - - if (obj == ScriptRuntime.UNDEFINED) { - return "undefined"; - } - - if (obj == null) { - return "null"; - } - - if (obj instanceof Boolean) { - return obj.toString(); - } - - if (obj instanceof Symbol) { - if (safe) { - return obj.toString(); - } - throw typeError("symbol.to.string"); - } - - if (safe && obj instanceof ScriptObject) { - final ScriptObject sobj = (ScriptObject)obj; - final Global gobj = Context.getGlobal(); - return gobj.isError(sobj) ? - ECMAException.safeToString(sobj) : - sobj.safeToString(); - } - - return toString(toPrimitive(obj, String.class)); - } - - // trim from left for JS whitespaces. - static String trimLeft(final String str) { - int start = 0; - - while (start < str.length() && Lexer.isJSWhitespace(str.charAt(start))) { - start++; - } - - return str.substring(start); - } - - /** - * Throw an unwarranted optimism exception for a program point - * @param value real return value - * @param programPoint program point - * @return - */ - @SuppressWarnings("unused") - private static Object throwUnwarrantedOptimismException(final Object value, final int programPoint) { - throw new UnwarrantedOptimismException(value, programPoint); - } - - /** - * Wrapper for addExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x first term - * @param y second term - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int addExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException { - try { - return Math.addExact(x, y); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException((double)x + (double)y, programPoint); - } - } - - /** - * Wrapper for subExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x first term - * @param y second term - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int subExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException { - try { - return Math.subtractExact(x, y); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException((double)x - (double)y, programPoint); - } - } - - /** - * Wrapper for mulExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x first term - * @param y second term - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int mulExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException { - try { - return Math.multiplyExact(x, y); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException((double)x * (double)y, programPoint); - } - } - - /** - * Wrapper for divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as - * int. - * - * @param x first term - * @param y second term - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if the result of the division can't be represented as int. - */ - public static int divExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException { - final int res; - try { - res = x / y; - } catch (final ArithmeticException e) { - assert y == 0; // Only div by zero anticipated - throw new UnwarrantedOptimismException(x > 0 ? Double.POSITIVE_INFINITY : x < 0 ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint); - } - final int rem = x % y; - if (rem == 0) { - return res; - } - // go directly to double here, as anything with non zero remainder is a floating point number in JavaScript - throw new UnwarrantedOptimismException((double)x / (double)y, programPoint); - } - - /** - * Implements int division but allows {@code x / 0} to be represented as 0. Basically equivalent to - * {@code (x / y)|0} JavaScript expression (division of two ints coerced to int). - * @param x the dividend - * @param y the divisor - * @return the result - */ - public static int divZero(final int x, final int y) { - return y == 0 ? 0 : x / y; - } - - /** - * Implements int remainder but allows {@code x % 0} to be represented as 0. Basically equivalent to - * {@code (x % y)|0} JavaScript expression (remainder of two ints coerced to int). - * @param x the dividend - * @param y the divisor - * @return the remainder - */ - public static int remZero(final int x, final int y) { - return y == 0 ? 0 : x % y; - } - - /** - * Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int. - * - * @param x first term - * @param y second term - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if the modulo can't be represented as int. - */ - public static int remExact(final int x, final int y, final int programPoint) throws UnwarrantedOptimismException { - try { - return x % y; - } catch (final ArithmeticException e) { - assert y == 0; // Only mod by zero anticipated - throw new UnwarrantedOptimismException(Double.NaN, programPoint); - } - } - - /** - * Wrapper for decrementExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x number to negate - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int decrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException { - try { - return Math.decrementExact(x); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException((double)x - 1, programPoint); - } - } - - /** - * Wrapper for incrementExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x the number to increment - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int incrementExact(final int x, final int programPoint) throws UnwarrantedOptimismException { - try { - return Math.incrementExact(x); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException((double)x + 1, programPoint); - } - } - - /** - * Wrapper for negateExact - * - * Catches ArithmeticException and rethrows as UnwarrantedOptimismException - * containing the result and the program point of the failure - * - * @param x the number to negate - * @param programPoint program point id - * @return the result - * @throws UnwarrantedOptimismException if overflow occurs - */ - public static int negateExact(final int x, final int programPoint) throws UnwarrantedOptimismException { - try { - if (x == 0) { - throw new UnwarrantedOptimismException(-0.0, programPoint); - } - return Math.negateExact(x); - } catch (final ArithmeticException e) { - throw new UnwarrantedOptimismException(-(double)x, programPoint); - } - } - - /** - * Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes()) - * - * @param type the type - * - * @return the accessor index, or -1 if no accessor of this type exists - */ - public static int getAccessorTypeIndex(final Type type) { - return getAccessorTypeIndex(type.getTypeClass()); - } - - /** - * Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes()) - * - * Note that this is hardcoded with respect to the dynamic contents of the accessor - * types array for speed. Hotspot got stuck with this as 5% of the runtime in - * a benchmark when it looped over values and increased an index counter. :-( - * - * @param type the type - * - * @return the accessor index, or -1 if no accessor of this type exists - */ - public static int getAccessorTypeIndex(final Class type) { - if (type == null) { - return TYPE_UNDEFINED_INDEX; - } else if (type == int.class) { - return TYPE_INT_INDEX; - } else if (type == double.class) { - return TYPE_DOUBLE_INDEX; - } else if (!type.isPrimitive()) { - return TYPE_OBJECT_INDEX; - } - return -1; - } - - /** - * Return the accessor type based on its index in [0..getNumberOfAccessorTypes()) - * Indexes are ordered narrower{@literal ->}wider / optimistic{@literal ->}pessimistic. Invalidations always - * go to a type of higher index - * - * @param index accessor type index - * - * @return a type corresponding to the index. - */ - - public static Type getAccessorType(final int index) { - return ACCESSOR_TYPES.get(index); - } - - /** - * Return the number of accessor types available. - * - * @return number of accessor types in system - */ - public static int getNumberOfAccessorTypes() { - return ACCESSOR_TYPES.size(); - } - - private static double parseRadix(final char chars[], final int start, final int length, final int radix) { - int pos = 0; - - for (int i = start; i < length ; i++) { - if (digit(chars[i], radix) == -1) { - return Double.NaN; - } - pos++; - } - - if (pos == 0) { - return Double.NaN; - } - - double value = 0.0; - for (int i = start; i < start + pos; i++) { - value *= radix; - value += digit(chars[i], radix); - } - - return value; - } - - private static double toNumberGeneric(final Object obj) { - if (obj == null) { - return +0.0; - } - - if (obj instanceof String) { - return toNumber((String)obj); - } - - if (obj instanceof ConsString) { - return toNumber(obj.toString()); - } - - if (obj instanceof Boolean) { - return toNumber((Boolean)obj); - } - - if (obj instanceof ScriptObject) { - return toNumber((ScriptObject)obj); - } - - if (obj instanceof Undefined) { - return Double.NaN; - } - - if (obj instanceof Symbol) { - throw typeError("symbol.to.number"); - } - - return toNumber(toPrimitive(obj, Number.class)); - } - - private static Object invoke(final MethodHandle mh, final Object arg) { - try { - return mh.invoke(arg); - } catch (final RuntimeException | Error e) { - throw e; - } catch (final Throwable t) { - throw new RuntimeException(t); - } - } - - /** - * Create a method handle constant of the correct primitive type - * for a constant object - * @param o object - * @return constant function that returns object - */ - public static MethodHandle unboxConstant(final Object o) { - if (o != null) { - if (o.getClass() == Integer.class) { - return MH.constant(int.class, o); - } else if (o.getClass() == Double.class) { - return MH.constant(double.class, o); - } - } - return MH.constant(Object.class, o); - } - - /** - * Get the unboxed (primitive) type for an object - * @param o object - * @return primitive type or Object.class if not primitive - */ - public static Class unboxedFieldType(final Object o) { - if (o == null) { - return Object.class; - } else if (o.getClass() == Integer.class) { - return int.class; - } else if (o.getClass() == Double.class) { - return double.class; - } else { - return Object.class; - } - } - - private static List toUnmodifiableList(final MethodHandle... methodHandles) { - return Collections.unmodifiableList(Arrays.asList(methodHandles)); - } -}