From cf9090e0fa4dfd2f7eb5f0cb7500e35e5aa5b3e0 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 23 Nov 2018 14:44:18 +0300 Subject: [PATCH] Use JS typed arrays to reinterpret floats and ints. Also, drop IE9 support --- .../classlib/java/lang/DoubleGenerator.java | 30 ++++++ .../java/lang/TAbstractStringBuilder.java | 8 +- .../org/teavm/classlib/java/lang/TDouble.java | 75 +-------------- .../org/teavm/classlib/java/lang/TFloat.java | 74 +-------------- .../org/teavm/backend/javascript/runtime.js | 94 +++++++++---------- 5 files changed, 90 insertions(+), 191 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java new file mode 100644 index 000000000..bf284dfc6 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/DoubleGenerator.java @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang; + +import java.io.IOException; +import org.teavm.backend.javascript.spi.Injector; +import org.teavm.backend.javascript.spi.InjectorContext; +import org.teavm.model.MethodReference; + +public class DoubleGenerator implements Injector { + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + context.getWriter().append("$rt_").append(methodRef.getName()).append("("); + context.writeExpr(context.getArgument(0)); + context.getWriter().append(")"); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java index fed2b9762..930348a7f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java @@ -415,9 +415,13 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ exp = 0; double digit = 1; for (int i = negDoublePowersOfTen.length - 1; i >= 0; --i) { - if ((exp | bit) <= DOUBLE_MAX_EXPONENT && negDoublePowersOfTen[i] * digit * 10 > value) { - digit *= negDoublePowersOfTen[i]; + if ((exp | bit) <= 324 && negDoublePowersOfTen[i] * digit * 10 > value) { exp |= bit; + if (exp == 324) { + value /= negDoublePowersOfTen[i]; + } else { + digit *= negDoublePowersOfTen[i]; + } } bit >>= 1; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index cdd076127..0ed37ccf3 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.lang; +import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.interop.Import; import org.teavm.jso.JSBody; @@ -241,52 +242,11 @@ public class TDouble extends TNumber implements TComparable { return doubleToLongBits(value); } - public static long doubleToLongBits(double value) { - if (value == POSITIVE_INFINITY) { - return 0x7FF0000000000000L; - } else if (value == NEGATIVE_INFINITY) { - return 0xFFF0000000000000L; - } else if (isNaN(value)) { - return 0x7FF8000000000000L; - } - double abs = TMath.abs(value); - int exp = TMath.getExponent(abs); - int negExp = -exp + 52; - if (exp < -1022) { - exp = -1023; - negExp = 1022 + 52; - } - double doubleMantissa; - if (negExp <= 1022) { - doubleMantissa = abs * binaryExponent(negExp); - } else { - doubleMantissa = abs * 0x1p1022 * binaryExponent(negExp - 1022); - } - long mantissa = (long) (doubleMantissa + 0.5) & 0xFFFFFFFFFFFFFL; - return mantissa | ((exp + 1023L) << 52) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1L << 63) : 0); - } + @InjectedBy(DoubleGenerator.class) + public static native long doubleToLongBits(double value); - public static double longBitsToDouble(long bits) { - if ((bits & 0x7FF0000000000000L) == 0x7FF0000000000000L) { - if (bits == 0x7FF0000000000000L) { - return POSITIVE_INFINITY; - } else if (bits == 0xFFF0000000000000L) { - return NEGATIVE_INFINITY; - } else { - return NaN; - } - } - boolean negative = (bits & (1L << 63)) != 0; - int rawExp = (int) ((bits >> 52) & 0x7FFL); - long mantissa = bits & 0xFFFFFFFFFFFFFL; - if (rawExp == 0) { - mantissa <<= 1; - } else { - mantissa |= 1L << 52; - } - double value = mantissa * binaryExponent(rawExp - 1023 - 52); - return !negative ? value : -value; - } + @InjectedBy(DoubleGenerator.class) + public static native double longBitsToDouble(long bits); public static TString toHexString(double d) { if (isNaN(d)) { @@ -351,29 +311,4 @@ public class TDouble extends TNumber implements TComparable { return new TString(buffer, 0, sz); } - - private static double binaryExponent(int n) { - double result = 1; - if (n >= 0) { - double d = 2; - while (n != 0) { - if (n % 2 != 0) { - result *= d; - } - n /= 2; - d *= d; - } - } else { - n = -n; - double d = 0.5; - while (n != 0) { - if (n % 2 != 0) { - result *= d; - } - n /= 2; - d *= d; - } - } - return result; - } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index af2db912a..9e9a0ee9f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -246,77 +246,11 @@ public class TFloat extends TNumber implements TComparable { return floatToIntBits(value); } - public static int floatToIntBits(float value) { - if (value == POSITIVE_INFINITY) { - return 0x7F800000; - } else if (value == NEGATIVE_INFINITY) { - return 0xFF800000; - } else if (isNaN(value)) { - return 0x7FC00000; - } - float abs = TMath.abs(value); - int exp = TMath.getExponent(abs); - int negExp = -exp + 23; - if (exp < -126) { - exp = -127; - negExp = 126 + 23; - } - float doubleMantissa; - if (negExp <= 126) { - doubleMantissa = abs * binaryExponent(negExp); - } else { - doubleMantissa = abs * 0x1p126f * binaryExponent(negExp - 126); - } - int mantissa = (int) (doubleMantissa + 0.5f) & 0x7FFFFF; - return mantissa | ((exp + 127) << 23) | (value < 0 || 1 / value == NEGATIVE_INFINITY ? (1 << 31) : 0); - } + @JSBody(params = "value", script = "return $rt_floatToIntBits(value);") + public static native int floatToIntBits(float value); - public static float intBitsToFloat(int bits) { - if ((bits & 0x7F800000) == 0x7F800000) { - if (bits == 0x7F800000) { - return POSITIVE_INFINITY; - } else if (bits == 0xFF800000) { - return NEGATIVE_INFINITY; - } else { - return NaN; - } - } - boolean negative = (bits & (1 << 31)) != 0; - int rawExp = (bits >> 23) & 0xFF; - int mantissa = bits & 0x7FFFFF; - if (rawExp == 0) { - mantissa <<= 1; - } else { - mantissa |= 1L << 23; - } - float value = mantissa * binaryExponent(rawExp - 127 - 23); - return !negative ? value : -value; - } - - private static float binaryExponent(int n) { - float result = 1; - if (n >= 0) { - float d = 2; - while (n != 0) { - if (n % 2 != 0) { - result *= d; - } - n /= 2; - d *= d; - } - } else { - n = -n; - float d = 0.5f; - while (n != 0) { - if (n % 2 != 0) { - result *= d; - } - n /= 2; - d *= d; - } - } - return result; - } + @JSBody(params = "bits", script = "return $rt_intBitsToFloat(bits);") + public static native float intBitsToFloat(int bits); public static TString toHexString(float f) { if (isNaN(f)) { diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 3310bd880..c0a0d55ae 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -67,56 +67,31 @@ function $rt_createLongArray(sz) { } return arr; } -var $rt_createNumericArray; -var $rt_createCharArray; -var $rt_createByteArray; -var $rt_createShortArray; -var $rt_createIntArray; -var $rt_createBooleanArray; -var $rt_createFloatArray; -var $rt_createDoubleArray; -if (typeof 'ArrayBuffer' !== 'undefined') { - $rt_createNumericArray = function(cls, nativeArray) { - return new ($rt_arraycls(cls))(nativeArray); - }; - $rt_createCharArray = function(sz) { - return $rt_createNumericArray($rt_charcls(), new Uint16Array(sz)); - }; - $rt_createByteArray = function(sz) { - return $rt_createNumericArray($rt_bytecls(), new Int8Array(sz)); - }; - $rt_createShortArray = function(sz) { - return $rt_createNumericArray($rt_shortcls(), new Int16Array(sz)); - }; - $rt_createIntArray = function(sz) { - return $rt_createNumericArray($rt_intcls(), new Int32Array(sz)); - }; - $rt_createBooleanArray = function(sz) { - return $rt_createNumericArray($rt_booleancls(), new Int8Array(sz)); - }; - $rt_createFloatArray = function(sz) { - return $rt_createNumericArray($rt_floatcls(), new Float32Array(sz)); - }; - $rt_createDoubleArray = function(sz) { - return $rt_createNumericArray($rt_doublecls(), new Float64Array(sz)); - }; -} else { - $rt_createNumericArray = function(cls, sz) { - var data = new Array(sz); - var arr = new ($rt_arraycls(cls))(data); - for (var i = 0; i < sz; i = (i + 1) | 0) { - data[i] = 0; - } - return arr; - }; - $rt_createByteArray = function(sz) { return $rt_createNumericArray($rt_bytecls(), sz); }; - $rt_createShortArray = function(sz) { return $rt_createNumericArray($rt_shortcls(), sz); }; - $rt_createIntArray = function(sz) { return $rt_createNumericArray($rt_intcls(), sz); }; - $rt_createBooleanArray = function(sz) { return $rt_createNumericArray($rt_booleancls(), sz); }; - $rt_createFloatArray = function(sz) { return $rt_createNumericArray($rt_floatcls(), sz); }; - $rt_createDoubleArray = function(sz) { return $rt_createNumericArray($rt_doublecls(), sz); }; - $rt_createCharArray = function(sz) { return $rt_createNumericArray($rt_charcls(), sz); } +function $rt_createNumericArray(cls, nativeArray) { + return new ($rt_arraycls(cls))(nativeArray); } +function $rt_createCharArray(sz) { + return $rt_createNumericArray($rt_charcls(), new Uint16Array(sz)); +} +function $rt_createByteArray(sz) { + return $rt_createNumericArray($rt_bytecls(), new Int8Array(sz)); +} +function $rt_createShortArray(sz) { + return $rt_createNumericArray($rt_shortcls(), new Int16Array(sz)); +} +function $rt_createIntArray(sz) { + return $rt_createNumericArray($rt_intcls(), new Int32Array(sz)); +} +function $rt_createBooleanArray(sz) { + return $rt_createNumericArray($rt_booleancls(), new Int8Array(sz)); +} +function $rt_createFloatArray(sz) { + return $rt_createNumericArray($rt_floatcls(), new Float32Array(sz)); +} +function $rt_createDoubleArray(sz) { + return $rt_createNumericArray($rt_doublecls(), new Float64Array(sz)); +} + function $rt_arraycls(cls) { var result = cls.$array; if (result === null) { @@ -615,6 +590,27 @@ function $rt_nativeThread() { function $rt_invalidPointer() { throw new Error("Invalid recorded state"); } + +var $rt_numberConversionView = new DataView(new ArrayBuffer(8)); + +function $rt_doubleToLongBits(n) { + $rt_numberConversionView.setFloat64(0, n, true); + return new Long($rt_numberConversionView.getInt32(0, true), $rt_numberConversionView.getInt32(4, true)); +} +function $rt_longBitsToDouble(n) { + $rt_numberConversionView.setInt32(0, n.lo, true); + $rt_numberConversionView.setInt32(4, n.hi, true); + return $rt_numberConversionView.getFloat64(0, true); +} +function $rt_floatToIntBits(n) { + $rt_numberConversionView.setFloat32(0, n); + return $rt_numberConversionView.getInt32(0); +} +function $rt_intBitsToFloat(n) { + $rt_numberConversionView.setInt32(0, n); + return $rt_numberConversionView.getFloat32(0); +} + function $dbg_class(obj) { var cls = obj.constructor; var arrayDegree = 0;