зеркало из https://github.com/mozilla/rhino.git
Aggressively try to use Integer objects
When doing math, try to do integer arithmetic when the arguments passed are Integer objects, rather than always falling back to floating-point math. This speeds up some benchmarks that rely heavily on integer arithmetic.
This commit is contained in:
Родитель
cbb0e2f170
Коммит
8b7b580cb3
|
@ -120,7 +120,8 @@ public class ArrayLikeAbstractOperations {
|
|||
break;
|
||||
case FIND_INDEX:
|
||||
case FIND_LAST_INDEX:
|
||||
if (ScriptRuntime.toBoolean(result)) return ScriptRuntime.wrapNumber(i);
|
||||
if (ScriptRuntime.toBoolean(result))
|
||||
return ScriptRuntime.wrapNumber((double) i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -458,7 +458,7 @@ public class ScriptRuntime {
|
|||
// Preserve backward-compatibility with historical value of this.
|
||||
public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L);
|
||||
|
||||
public static final Double zeroObj = Double.valueOf(0.0);
|
||||
public static final Integer zeroObj = Integer.valueOf(0);
|
||||
public static final Double negativeZeroObj = Double.valueOf(-0.0);
|
||||
|
||||
static double stringPrefixToNumber(String s, int start, int radix) {
|
||||
|
@ -2991,6 +2991,9 @@ public class ScriptRuntime {
|
|||
|| (val1 instanceof BigInteger && val2 instanceof Number)) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
}
|
||||
if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return add((Integer) val1, (Integer) val2);
|
||||
}
|
||||
if (val1 instanceof Number && val2 instanceof Number) {
|
||||
return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue());
|
||||
}
|
||||
|
@ -3064,6 +3067,8 @@ public class ScriptRuntime {
|
|||
return ((BigInteger) val1).subtract((BigInteger) val2);
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return subtract((Integer) val1, (Integer) val2);
|
||||
} else {
|
||||
return val1.doubleValue() - val2.doubleValue();
|
||||
}
|
||||
|
@ -3074,6 +3079,8 @@ public class ScriptRuntime {
|
|||
return ((BigInteger) val1).multiply((BigInteger) val2);
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return multiply((Integer) val1, (Integer) val2);
|
||||
} else {
|
||||
return val1.doubleValue() * val2.doubleValue();
|
||||
}
|
||||
|
@ -3088,6 +3095,8 @@ public class ScriptRuntime {
|
|||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else {
|
||||
// Do not try to optimize for the integer case because JS doesn't
|
||||
// have an integer type.
|
||||
return val1.doubleValue() / val2.doubleValue();
|
||||
}
|
||||
}
|
||||
|
@ -3101,10 +3110,41 @@ public class ScriptRuntime {
|
|||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else {
|
||||
// Do not try an integer-specific optimization because we need to get
|
||||
// both +0 and -0 right.
|
||||
return val1.doubleValue() % val2.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
// Integer-optimized methods.
|
||||
|
||||
public static Object add(Integer i1, Integer i2) {
|
||||
// Try to add integers for efficiency, but account for overflow
|
||||
long r = (long) i1.intValue() + (long) i2.intValue();
|
||||
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
|
||||
return Integer.valueOf((int) r);
|
||||
}
|
||||
return Double.valueOf((double) r);
|
||||
}
|
||||
|
||||
public static Number subtract(Integer i1, Integer i2) {
|
||||
// Account for overflow
|
||||
long r = (long) i1.intValue() - (long) i2.intValue();
|
||||
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
|
||||
return Integer.valueOf((int) r);
|
||||
}
|
||||
return Double.valueOf((double) r);
|
||||
}
|
||||
|
||||
public static Number multiply(Integer i1, Integer i2) {
|
||||
// Aunt for overflow
|
||||
long r = (long) i1.intValue() * (long) i2.intValue();
|
||||
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
|
||||
return Integer.valueOf((int) r);
|
||||
}
|
||||
return Double.valueOf((double) r);
|
||||
}
|
||||
|
||||
public static Number exponentiate(Number val1, Number val2) {
|
||||
if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
|
||||
if (((BigInteger) val2).signum() == -1) {
|
||||
|
@ -3130,6 +3170,8 @@ public class ScriptRuntime {
|
|||
return ((BigInteger) val1).and((BigInteger) val2);
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return Integer.valueOf(((Integer) val1).intValue() & ((Integer) val2).intValue());
|
||||
} else {
|
||||
int result = toInt32(val1.doubleValue()) & toInt32(val2.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3141,6 +3183,8 @@ public class ScriptRuntime {
|
|||
return ((BigInteger) val1).or((BigInteger) val2);
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return Integer.valueOf(((Integer) val1).intValue() | ((Integer) val2).intValue());
|
||||
} else {
|
||||
int result = toInt32(val1.doubleValue()) | toInt32(val2.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3152,6 +3196,8 @@ public class ScriptRuntime {
|
|||
return ((BigInteger) val1).xor((BigInteger) val2);
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return Integer.valueOf(((Integer) val1).intValue() ^ ((Integer) val2).intValue());
|
||||
} else {
|
||||
int result = toInt32(val1.doubleValue()) ^ toInt32(val2.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3169,6 +3215,8 @@ public class ScriptRuntime {
|
|||
}
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return Integer.valueOf(((Integer) val1).intValue() << ((Integer) val2).intValue());
|
||||
} else {
|
||||
int result = toInt32(val1.doubleValue()) << toInt32(val2.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3186,6 +3234,8 @@ public class ScriptRuntime {
|
|||
}
|
||||
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
|
||||
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
|
||||
} else if (val1 instanceof Integer && val2 instanceof Integer) {
|
||||
return Integer.valueOf(((Integer) val1).intValue() >> ((Integer) val2).intValue());
|
||||
} else {
|
||||
int result = toInt32(val1.doubleValue()) >> toInt32(val2.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3195,6 +3245,8 @@ public class ScriptRuntime {
|
|||
public static Number bitwiseNOT(Number val) {
|
||||
if (val instanceof BigInteger) {
|
||||
return ((BigInteger) val).not();
|
||||
} else if (val instanceof Integer) {
|
||||
return Integer.valueOf(~((Integer) val).intValue());
|
||||
} else {
|
||||
int result = ~toInt32(val.doubleValue());
|
||||
return Double.valueOf(result);
|
||||
|
@ -3292,6 +3344,12 @@ public class ScriptRuntime {
|
|||
} else {
|
||||
result = ((BigInteger) number).subtract(BigInteger.ONE);
|
||||
}
|
||||
} else if (number instanceof Integer) {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = ((Integer) number).intValue() + 1;
|
||||
} else {
|
||||
result = ((Integer) number).intValue() - 1;
|
||||
}
|
||||
} else {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = number.doubleValue() + 1.0;
|
||||
|
@ -3332,6 +3390,12 @@ public class ScriptRuntime {
|
|||
} else {
|
||||
result = ((BigInteger) number).subtract(BigInteger.ONE);
|
||||
}
|
||||
} else if (number instanceof Integer) {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = ((Integer) number).intValue() + 1;
|
||||
} else {
|
||||
result = ((Integer) number).intValue() - 1;
|
||||
}
|
||||
} else {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = number.doubleValue() + 1.0;
|
||||
|
@ -3371,6 +3435,12 @@ public class ScriptRuntime {
|
|||
} else {
|
||||
result = ((BigInteger) number).subtract(BigInteger.ONE);
|
||||
}
|
||||
} else if (number instanceof Integer) {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = ((Integer) number).intValue() + 1;
|
||||
} else {
|
||||
result = ((Integer) number).intValue() - 1;
|
||||
}
|
||||
} else {
|
||||
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
|
||||
result = number.doubleValue() + 1.0;
|
||||
|
@ -3390,6 +3460,17 @@ public class ScriptRuntime {
|
|||
if (val instanceof BigInteger) {
|
||||
return ((BigInteger) val).negate();
|
||||
}
|
||||
if (val instanceof Integer) {
|
||||
int iv = (Integer) val;
|
||||
if (iv == 0) {
|
||||
return negativeZeroObj;
|
||||
}
|
||||
if (iv > Integer.MIN_VALUE && iv < Integer.MAX_VALUE) {
|
||||
// Account for twos-complement representation by not trying
|
||||
// to negate values at the extremes
|
||||
return Integer.valueOf(-((Integer) val).intValue());
|
||||
}
|
||||
}
|
||||
return -val.doubleValue();
|
||||
}
|
||||
|
||||
|
|
|
@ -1143,7 +1143,7 @@ public class Codegen implements Evaluator {
|
|||
ByteCode.GETSTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"zeroObj",
|
||||
"Ljava/lang/Double;");
|
||||
"Ljava/lang/Integer;");
|
||||
} else {
|
||||
cfw.addPush(num);
|
||||
addDoubleWrap(cfw);
|
||||
|
@ -1154,7 +1154,7 @@ public class Codegen implements Evaluator {
|
|||
ByteCode.GETSTATIC,
|
||||
"org/mozilla/javascript/optimizer/OptRuntime",
|
||||
"oneObj",
|
||||
"Ljava/lang/Double;");
|
||||
"Ljava/lang/Integer;");
|
||||
return;
|
||||
|
||||
} else if (num == -1.0) {
|
||||
|
@ -1162,7 +1162,7 @@ public class Codegen implements Evaluator {
|
|||
ByteCode.GETSTATIC,
|
||||
"org/mozilla/javascript/optimizer/OptRuntime",
|
||||
"minusOneObj",
|
||||
"Ljava/lang/Double;");
|
||||
"Ljava/lang/Integer;");
|
||||
|
||||
} else if (Double.isNaN(num)) {
|
||||
cfw.add(
|
||||
|
|
|
@ -21,8 +21,8 @@ import org.mozilla.javascript.ScriptableObject;
|
|||
import org.mozilla.javascript.Undefined;
|
||||
|
||||
public final class OptRuntime extends ScriptRuntime {
|
||||
public static final Double oneObj = Double.valueOf(1.0);
|
||||
public static final Double minusOneObj = Double.valueOf(-1.0);
|
||||
public static final Integer oneObj = Integer.valueOf(1);
|
||||
public static final Integer minusOneObj = Integer.valueOf(-1);
|
||||
|
||||
/** Implement ....() call shrinking optimizer code. */
|
||||
public static Object call0(Callable fun, Scriptable thisObj, Context cx, Scriptable scope) {
|
||||
|
@ -145,16 +145,7 @@ public final class OptRuntime extends ScriptRuntime {
|
|||
}
|
||||
|
||||
public static Double wrapDouble(double num) {
|
||||
if (num == 0.0) {
|
||||
if (1 / num > 0) {
|
||||
// +0.0
|
||||
return zeroObj;
|
||||
}
|
||||
} else if (num == 1.0) {
|
||||
return oneObj;
|
||||
} else if (num == -1.0) {
|
||||
return minusOneObj;
|
||||
} else if (Double.isNaN(num)) {
|
||||
if (Double.isNaN(num)) {
|
||||
return NaNobj;
|
||||
}
|
||||
return Double.valueOf(num);
|
||||
|
|
|
@ -56,9 +56,9 @@ public class Bug687669Test {
|
|||
public void eval() {
|
||||
// test EmptyStatement node doesn't infer with return values (in
|
||||
// contrast to wrapping EmptyExpression into an ExpressionStatement)
|
||||
assertEquals(1d, eval("1;;;;"));
|
||||
assertEquals(1, eval("1;;;;"));
|
||||
assertEquals(Undefined.instance, eval("(function(){1;;;;})()"));
|
||||
assertEquals(1d, eval("(function(){return 1;;;;})()"));
|
||||
assertEquals(1, eval("(function(){return 1;;;;})()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -48,7 +48,7 @@ public class DefineFunctionPropertiesTest {
|
|||
public void simpleFunction() {
|
||||
try (Context cx = Context.enter()) {
|
||||
Object result = cx.evaluateString(global, "f(7) + 1", "test source", 1, null);
|
||||
assertEquals(15.0, result);
|
||||
assertEquals(15, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -440,8 +440,7 @@ public class NativeConsoleTest {
|
|||
assertPrintCalls(
|
||||
"console.assert(false, 'Fail', 1)",
|
||||
Collections.singletonList(
|
||||
new PrinterCall(
|
||||
Level.ERROR, new Object[] {"Assertion failed: Fail", 1.0})));
|
||||
new PrinterCall(Level.ERROR, new Object[] {"Assertion failed: Fail", 1})));
|
||||
assertPrintMsg("console.assert(false, 'Fail', 1)", "Assertion failed: Fail 1");
|
||||
|
||||
assertPrintCalls(
|
||||
|
|
Загрузка…
Ссылка в новой задаче