aboutsummaryrefslogtreecommitdiff
path: root/src/jdk/nashorn/internal/runtime/JSType.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/JSType.java')
-rw-r--r--src/jdk/nashorn/internal/runtime/JSType.java946
1 files changed, 890 insertions, 56 deletions
diff --git a/src/jdk/nashorn/internal/runtime/JSType.java b/src/jdk/nashorn/internal/runtime/JSType.java
index 11cecbfe..7e54b1e2 100644
--- a/src/jdk/nashorn/internal/runtime/JSType.java
+++ b/src/jdk/nashorn/internal/runtime/JSType.java
@@ -26,16 +26,20 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
+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.Deque;
import java.util.List;
import jdk.internal.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.parser.Lexer;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
@@ -72,58 +76,199 @@ public enum JSType {
/** Max value for an uint32 in JavaScript */
public static final long MAX_UINT = 0xFFFF_FFFFL;
- private static final MethodHandles.Lookup myLookup = MethodHandles.lookup();
+ private static final MethodHandles.Lookup JSTYPE_LOOKUP = MethodHandles.lookup();
/** JavaScript compliant conversion function from Object to boolean */
- public static final Call TO_BOOLEAN = staticCall(myLookup, JSType.class, "toBoolean", boolean.class, Object.class);
+ 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(myLookup, JSType.class, "toBoolean", boolean.class, double.class);
+ public static final Call TO_BOOLEAN_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toBoolean", boolean.class, double.class);
/** JavaScript compliant conversion function from Object to integer */
- public static final Call TO_INTEGER = staticCall(myLookup, JSType.class, "toInteger", int.class, Object.class);
+ 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(myLookup, JSType.class, "toLong", long.class, Object.class);
+ 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(myLookup, JSType.class, "toNumber", double.class, Object.class);
+ 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(myLookup, JSType.class, "toString", String.class, Object.class);
+ 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(myLookup, JSType.class, "toInt32", int.class, Object.class);
+ 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(myLookup, JSType.class, "toInt32", int.class, double.class);
+ public static final Call TO_INT32_D = staticCall(JSTYPE_LOOKUP, JSType.class, "toInt32", int.class, double.class);
- /** JavaScript compliant conversion function from Object to uint32 */
- public static final Call TO_UINT32 = staticCall(myLookup, JSType.class, "toUint32", long.class, Object.class);
+ /** JavaScript compliant conversion function from int to uint32 */
+ public static final Call TO_UINT32_I = staticCall(JSTYPE_LOOKUP, JSType.class, "toUint32", long.class, int.class);
- /** JavaScript compliant conversion function from number to uint32 */
- public static final Call TO_UINT32_D = staticCall(myLookup, JSType.class, "toUint32", long.class, double.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 Object to int64 */
- public static final Call TO_INT64 = staticCall(myLookup, JSType.class, "toInt64", long.class, Object.class);
+ /** JavaScript compliant conversion function from Object to long with type check */
+ public static final Call TO_LONG_OPTIMISTIC = staticCall(JSTYPE_LOOKUP, JSType.class, "toLongOptimistic", long.class, Object.class, int.class);
- /** JavaScript compliant conversion function from number to int64 */
- public static final Call TO_INT64_D = staticCall(myLookup, JSType.class, "toInt64", long.class, double.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(myLookup, JSType.class, "toString", String.class, double.class);
+ 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(myLookup, JSType.class, "toPrimitiveToString", String.class, Object.class);
+ 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);
+
+ /** Add exact wrapper for potentially overflowing long operations */
+ public static final Call ADD_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "addExact", long.class, long.class, long.class, int.class);
+
+ /** Sub exact wrapper for potentially overflowing long operations */
+ public static final Call SUB_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "subExact", long.class, long.class, long.class, int.class);
+
+ /** Multiply exact wrapper for potentially overflowing long operations */
+ public static final Call MUL_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "mulExact", long.class, long.class, long.class, int.class);
+
+ /** Div exact wrapper for potentially integer division that turns into float point */
+ public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
+
+ /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
+ public static final Call DIV_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
+
+ /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
+ public static final Call REM_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
+
+ /** Mod exact wrapper for potentially integer remainders that turns into float point */
+ public static final Call REM_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
+
+ /** Decrement exact wrapper for potentially overflowing long operations */
+ public static final Call DECREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "decrementExact", long.class, long.class, int.class);
+
+ /** Increment exact wrapper for potentially overflowing long operations */
+ public static final Call INCREMENT_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "incrementExact", long.class, long.class, int.class);
+
+ /** Negate exact exact wrapper for potentially overflowing long operations */
+ public static final Call NEGATE_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "negateExact", long.class, long.class, int.class);
/** Method handle to convert a JS Object to a Java array. */
- public static final Call TO_JAVA_ARRAY = staticCall(myLookup, JSType.class, "toJavaArray", Object.class, Object.class, Class.class);
+ 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 List. */
- public static final Call TO_JAVA_LIST = staticCall(myLookup, JSType.class, "toJavaList", List.class, Object.class);
+ public static final Call TO_JAVA_LIST = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaList", List.class, Object.class);
/** Method handle to convert a JS Object to a Java deque. */
- public static final Call TO_JAVA_DEQUE = staticCall(myLookup, JSType.class, "toJavaDeque", Deque.class, Object.class);
+ public static final Call TO_JAVA_DEQUE = staticCall(JSTYPE_LOOKUP, JSType.class, "toJavaDeque", Deque.class, Object.class);
+
+ /** Method handle for void returns. */
+ public static final Call VOID_RETURN = staticCall(JSTYPE_LOOKUP, JSType.class, "voidReturn", void.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<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
+ Arrays.asList(
+ Type.INT,
+ Type.LONG,
+ 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 long type - hard coded so it can be used in switches at compile time */
+ public static final int TYPE_LONG_INDEX = 1; //getAccessorTypeIndex(long.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 = 2; //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 = 3; //getAccessorTypeIndex(Object.class);
+
+ /** object conversion quickies with JS semantics - used for return value and parameter filter */
+ public static final List<MethodHandle> CONVERT_OBJECT = toUnmodifiableList(
+ JSType.TO_INT32.methodHandle(),
+ JSType.TO_UINT32.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<MethodHandle> CONVERT_OBJECT_OPTIMISTIC = toUnmodifiableList(
+ JSType.TO_INT32_OPTIMISTIC.methodHandle(),
+ JSType.TO_LONG_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;
+
+ /**
+ * Method handles for getters that return undefined coerced
+ * to the appropriate type
+ */
+ public static final List<MethodHandle> GET_UNDEFINED = toUnmodifiableList(
+ MH.constant(int.class, UNDEFINED_INT),
+ MH.constant(long.class, UNDEFINED_LONG),
+ MH.constant(double.class, UNDEFINED_DOUBLE),
+ MH.constant(Object.class, Undefined.getUndefined())
+ );
private static final double INT32_LIMIT = 4294967296.0;
@@ -159,7 +304,7 @@ public enum JSType {
}
if (obj instanceof ScriptObject) {
- return (obj instanceof ScriptFunction) ? JSType.FUNCTION : JSType.OBJECT;
+ return obj instanceof ScriptFunction ? JSType.FUNCTION : JSType.OBJECT;
}
if (obj instanceof Boolean) {
@@ -182,6 +327,52 @@ public enum JSType {
}
/**
+ * 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 (obj instanceof String || obj instanceof ConsString) {
+ return JSType.STRING;
+ }
+
+ if (obj instanceof Number) {
+ return JSType.NUMBER;
+ }
+
+ if (obj == ScriptRuntime.UNDEFINED) {
+ return JSType.UNDEFINED;
+ }
+
+ 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
@@ -193,7 +384,8 @@ public enum JSType {
}
/**
- * Returns true if double number can be represented as an int
+ * 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, combine this check with {@link #isNegativeZero(double)}.
*
* @param number a double to inspect
*
@@ -204,7 +396,22 @@ public enum JSType {
}
/**
- * Returns true if double number can be represented as a long
+ * 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. If you
+ * need to exclude negative zero, combine this check with {@link #isNegativeZero(double)}.
*
* @param number a double to inspect
* @return true for long representable doubles
@@ -214,13 +421,36 @@ public enum JSType {
}
/**
+ * Returns true if Object can be represented as a long
+ *
+ * @param obj an object to inspect
+ *
+ * @return true for long representable objects
+ */
+ public static boolean isRepresentableAsLong(final Object obj) {
+ if (obj instanceof Number) {
+ return isRepresentableAsLong(((Number)obj).doubleValue());
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the number is the negative zero ({@code -0.0d}).
+ * @param number the number to test
+ * @return true if it is the negative zero, false otherwise.
+ */
+ public static boolean isNegativeZero(final double number) {
+ return number == 0.0d && 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) {
+ public static boolean isPrimitive(final Object obj) {
return obj == null ||
obj == ScriptRuntime.UNDEFINED ||
obj instanceof Boolean ||
@@ -270,11 +500,21 @@ public enum JSType {
*
* @return the string form of the primitive form of the object
*/
- public static String toPrimitiveToString(Object obj) {
+ 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
@@ -474,6 +714,9 @@ public enum JSType {
* @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();
}
@@ -494,6 +737,35 @@ public enum JSType {
}
/**
+ * 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
@@ -612,7 +884,7 @@ public enum JSType {
}
/**
- * JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger
+ * Converts an Object to long.
*
* <p>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
@@ -622,7 +894,46 @@ public enum JSType {
* @return a long
*/
public static long toLong(final Object obj) {
- return (long)toNumber(obj);
+ return obj instanceof Long ? ((Long)obj).longValue() : 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;
+ }
+
+ /**
+ * Optimistic long conversion - throws UnwarrantedOptimismException if double or Object
+ *
+ * @param obj object to convert
+ * @param programPoint program point
+ * @return long
+ */
+ public static long toLongOptimistic(final Object obj, final int programPoint) {
+ if (obj != null) {
+ final Class<?> clz = obj.getClass();
+ if (clz == Long.class || clz == Integer.class) {
+ return ((Number)obj).longValue();
+ }
+ }
+ throw new UnwarrantedOptimismException(obj, programPoint);
+ }
+
+ /**
+ * Object to int conversion that delegates to either {@link #toLong(Object)} or to
+ * {@link #toLongOptimistic(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 long
+ * @throws UnwarrantedOptimismException if the value can't be represented as long and the program point is valid.
+ */
+ public static long toLongMaybeOptimistic(final Object obj, final int programPoint) {
+ return UnwarrantedOptimismException.isValid(programPoint) ? toLongOptimistic(obj, programPoint) : toLong(obj);
}
/**
@@ -637,46 +948,54 @@ public enum JSType {
}
/**
- * JavaScript compliant long to int32 conversion
+ * Optimistic int conversion - throws UnwarrantedOptimismException if double, long or Object
*
- * @param num a long
- * @return an int32
+ * @param obj object to convert
+ * @param programPoint program point
+ * @return double
*/
- public static int toInt32(final long num) {
- return (int)num;
+ public static int toInt32Optimistic(final Object obj, final int programPoint) {
+ if (obj != null && obj.getClass() == Integer.class) {
+ return ((Integer)obj).intValue();
+ }
+ throw new UnwarrantedOptimismException(obj, programPoint);
}
/**
- * JavaScript compliant number to int32 conversion
- *
- * @param num a number
- * @return an int32
+ * 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 toInt32(final double num) {
- return (int)doubleToInt32(num);
+ public static int toInt32MaybeOptimistic(final Object obj, final int programPoint) {
+ return UnwarrantedOptimismException.isValid(programPoint) ? toInt32Optimistic(obj, programPoint) : toInt32(obj);
}
+ // 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;
+
/**
- * JavaScript compliant Object to int64 conversion
+ * JavaScript compliant long to int32 conversion
*
- * @param obj an object
- * @return an int64
+ * @param num a long
+ * @return an int32
*/
- public static long toInt64(final Object obj) {
- return toInt64(toNumber(obj));
+ 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 int64 conversion
+ * JavaScript compliant number to int32 conversion
*
* @param num a number
- * @return an int64
+ * @return an int32
*/
- public static long toInt64(final double num) {
- if (Double.isInfinite(num)) {
- return 0L;
- }
- return (long)num;
+ public static int toInt32(final double num) {
+ return (int)doubleToInt32(num);
}
/**
@@ -700,6 +1019,16 @@ public enum JSType {
}
/**
+ * JavaScript compliant int to uint32 conversion
+ *
+ * @param num an int
+ * @return a uint32
+ */
+ public static long toUint32(final int num) {
+ return num & MAX_UINT;
+ }
+
+ /**
* JavaScript compliant Object to uint16 conversion
* ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer)
*
@@ -727,7 +1056,7 @@ public enum JSType {
* @return a uint16
*/
public static int toUint16(final long num) {
- return ((int)num) & 0xffff;
+ return (int)num & 0xffff;
}
/**
@@ -737,7 +1066,7 @@ public enum JSType {
* @return a uint16
*/
public static int toUint16(final double num) {
- return ((int)doubleToInt32(num)) & 0xffff;
+ return (int)doubleToInt32(num) & 0xffff;
}
private static long doubleToInt32(final double num) {
@@ -751,7 +1080,7 @@ public enum JSType {
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);
+ final double d = num >= 0 ? Math.floor(num) : Math.ceil(num);
return (long)(d % INT32_LIMIT);
}
@@ -1014,6 +1343,448 @@ public enum JSType {
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((long)x + (long)y, 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 long addExact(final long x, final long 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((long)x - (long)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 long subExact(final long x, final long 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((long)x * (long)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 long mulExact(final long x, final long 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 divExact. Throws UnwarrantedOptimismException if the result of the division can't be represented as
+ * long.
+ *
+ * @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 long.
+ */
+ public static long divExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+ final long res;
+ try {
+ res = x / y;
+ } catch (final ArithmeticException e) {
+ assert y == 0L; // Only div by zero anticipated
+ throw new UnwarrantedOptimismException(x > 0L ? Double.POSITIVE_INFINITY : x < 0L ? Double.NEGATIVE_INFINITY : Double.NaN, programPoint);
+ }
+ final long rem = x % y;
+ if (rem == 0L) {
+ return res;
+ }
+ throw new UnwarrantedOptimismException((double)x / (double)y, programPoint);
+ }
+
+ /**
+ * Implements long division but allows {@code x / 0} to be represented as 0. Useful when division of two longs
+ * is coerced to long.
+ * @param x the dividend
+ * @param y the divisor
+ * @return the result
+ */
+ public static long divZero(final long x, final long y) {
+ return y == 0L ? 0L : x / y;
+ }
+
+ /**
+ * Implements long remainder but allows {@code x % 0} to be represented as 0. Useful when remainder of two longs
+ * is coerced to long.
+ * @param x the dividend
+ * @param y the divisor
+ * @return the remainder
+ */
+ public static long remZero(final long x, final long y) {
+ return y == 0L ? 0L : 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 long remExact(final long x, final long y, final int programPoint) throws UnwarrantedOptimismException {
+ try {
+ return x % y;
+ } catch (final ArithmeticException e) {
+ assert y == 0L; // 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((long)x - 1, 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 long decrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+ try {
+ return Math.decrementExact(x);
+ } catch (final ArithmeticException e) {
+ throw new UnwarrantedOptimismException((double)x - 1L, 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((long)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 long incrementExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+ try {
+ return Math.incrementExact(x);
+ } catch (final ArithmeticException e) {
+ throw new UnwarrantedOptimismException((double)x + 1L, 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(-(long)x, 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 long negateExact(final long x, final int programPoint) throws UnwarrantedOptimismException {
+ try {
+ if (x == 0L) {
+ 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 == long.class) {
+ return TYPE_LONG_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;
@@ -1074,4 +1845,67 @@ public enum JSType {
throw new RuntimeException(t);
}
}
+
+ /**
+ * Returns the boxed version of a primitive class
+ * @param clazz the class
+ * @return the boxed type of clazz, or unchanged if not primitive
+ */
+ public static Class<?> getBoxedClass(final Class<?> clazz) {
+ if (clazz == int.class) {
+ return Integer.class;
+ } else if (clazz == long.class) {
+ return Long.class;
+ } else if (clazz == double.class) {
+ return Double.class;
+ }
+ assert !clazz.isPrimitive();
+ return clazz;
+ }
+
+ /**
+ * 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, ((Integer)o).intValue());
+ } else if (o.getClass() == Long.class) {
+ return MH.constant(long.class, ((Long)o).longValue());
+ } else if (o.getClass() == Double.class) {
+ return MH.constant(double.class, ((Double)o).doubleValue());
+ }
+ }
+ return MH.constant(Object.class, o);
+ }
+
+ /**
+ * Get the unboxed (primitive) type for an object
+ * @param o object
+ * @return primive type or Object.class if not primitive
+ */
+ public static Class<?> unboxedFieldType(final Object o) {
+ if (OBJECT_FIELDS_ONLY) {
+ return Object.class;
+ }
+
+ if (o == null) {
+ return Object.class;
+ } else if (o.getClass() == Integer.class) {
+ return int.class;
+ } else if (o.getClass() == Long.class) {
+ return long.class;
+ } else if (o.getClass() == Double.class) {
+ return double.class;
+ } else {
+ return Object.class;
+ }
+ }
+
+ private static final List<MethodHandle> toUnmodifiableList(final MethodHandle... methodHandles) {
+ return Collections.unmodifiableList(Arrays.asList(methodHandles));
+ }
}