diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonBuilder.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonBuilder.java index 648872328..6dcac23a9 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonBuilder.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonBuilder.java @@ -89,6 +89,7 @@ public class JCLComparisonBuilder { copyResource("html/package_obj.png"); copyResource("html/int_obj.png"); copyResource("html/enum_obj.png"); + copyResource("html/annotation_obj.png"); try (Writer out = new OutputStreamWriter(new FileOutputStream(new File( outputDirectory, "jcl.html")), "UTF-8")) { generateHtml(out, packages); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonVisitor.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonVisitor.java index 816ae6e36..60a892e67 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonVisitor.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLComparisonVisitor.java @@ -57,10 +57,10 @@ class JCLComparisonVisitor implements ClassVisitor { jclClass = new JCLClass(simpleName); jclClass.status = classReader != null ? JCLStatus.FOUND : JCLStatus.MISSING; jclClass.visibility = (access & Opcodes.ACC_PROTECTED) != 0 ? JCLVisibility.PROTECTED : JCLVisibility.PUBLIC; - if ((access & Opcodes.ACC_INTERFACE) != 0) { - jclClass.type = JCLClassType.INTERFACE; - } else if ((access & Opcodes.ACC_ANNOTATION) != 0) { + if ((access & Opcodes.ACC_ANNOTATION) != 0) { jclClass.type = JCLClassType.ANNOTATION; + } else if ((access & Opcodes.ACC_INTERFACE) != 0) { + jclClass.type = JCLClassType.INTERFACE; } else if ((access & Opcodes.ACC_ENUM) != 0) { jclClass.type = JCLClassType.ENUM; } else { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index 1171babb1..a04a5731c 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -86,6 +86,12 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "charClass": context.getWriter().append("$rt_cls($rt_charcls())"); break; + case "byteClass": + context.getWriter().append("$rt_cls($rt_bytecls())"); + break; + case "doubleClass": + context.getWriter().append("$rt_cls($rt_doublecls())"); + break; case "wrap": context.writeExpr(context.getArgument(0)); break; @@ -176,6 +182,9 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "booleanClass": case "intClass": case "charClass": + case "byteClass": + case "voidClass": + case "doubleClass": case "wrap": case "getSuperclass": case "getComponentType0": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java index bb78f8d6c..6c30c9617 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/DoubleNativeGenerator.java @@ -19,13 +19,15 @@ import java.io.IOException; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.javascript.ni.Injector; +import org.teavm.javascript.ni.InjectorContext; import org.teavm.model.MethodReference; /** * * @author Alexey Andreev */ -public class DoubleNativeGenerator implements Generator { +public class DoubleNativeGenerator implements Generator, Injector { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { @@ -38,6 +40,15 @@ public class DoubleNativeGenerator implements Generator { } } + @Override + public void generate(InjectorContext context, MethodReference methodRef) throws IOException { + switch (methodRef.getName()) { + case "getNaN": + context.getWriter().append("NaN"); + break; + } + } + private void generateIsNaN(GeneratorContext context, SourceWriter writer) throws IOException { writer.append("return (isNaN(").append(context.getParameterName(1)).append(")").ws().append("?") .ws().append("1").ws().append(":").ws().append("0").ws().append(");").softNewLine(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java index 0bc454b27..235c88f12 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TArrayIndexOutOfBoundsException.java @@ -29,4 +29,8 @@ public class TArrayIndexOutOfBoundsException extends TIndexOutOfBoundsException public TArrayIndexOutOfBoundsException(TString message) { super(message); } + + public TArrayIndexOutOfBoundsException(int index) { + super(TInteger.toString(index)); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TByte.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TByte.java index 536f219b3..170c354a9 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TByte.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TByte.java @@ -22,12 +22,20 @@ import org.teavm.javascript.ni.Rename; * @author Alexey Andreev */ public class TByte extends TNumber implements TComparable { + public static final byte MIN_VALUE = -128; + public static final byte MAX_VALUE = 127; + public static final TClass TYPE = TClass.byteClass(); + public static final int SIZE = 8; private byte value; public TByte(byte value) { this.value = value; } + public TByte(TString value) { + this.value = parseByte(value, 10); + } + @Override public int intValue() { return value; @@ -54,6 +62,7 @@ public class TByte extends TNumber implements TComparable { } public static TByte valueOf(byte value) { + // TODO: add caching return new TByte(value); } @@ -85,4 +94,32 @@ public class TByte extends TNumber implements TComparable { public int compareTo(TByte other) { return compare(value, other.value); } + + public static byte parseByte(TString s) throws TNumberFormatException { + return parseByte(s, 10); + } + + public static byte parseByte(TString s, int radix) throws TNumberFormatException { + int value = TInteger.parseInt(s, radix); + if (value < MIN_VALUE || value >= MAX_VALUE) { + throw new TNumberFormatException(); + } + return (byte)value; + } + + public static TByte valueOf(TString s, int radix) throws TNumberFormatException { + return valueOf(parseByte(s, radix)); + } + + public static TByte valueOf(TString s) throws TNumberFormatException { + return valueOf(parseByte(s)); + } + + public static TByte decode(TString nm) throws TNumberFormatException { + TInteger value = TInteger.decode(nm); + if (value.intValue() < MIN_VALUE || value.intValue() >= MAX_VALUE) { + throw new TNumberFormatException(); + } + return TByte.valueOf((byte)value.intValue()); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java index edbb2e6ca..252f28ff0 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java @@ -558,6 +558,10 @@ public class TCharacter extends TObject implements TComparable { } } + public static boolean isUnicodeIdentifierPart(char ch) { + return isUnicodeIdentifierPart((int)ch); + } + public static boolean isUnicodeIdentifierPart(int codePoint) { switch (getType(codePoint)) { case UPPERCASE_LETTER: diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index e26b9dcbc..a714e5b9d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -85,6 +85,14 @@ public class TClass extends TObject { @PluggableDependency(ClassNativeGenerator.class) static native TClass voidClass(); + @InjectedBy(ClassNativeGenerator.class) + @PluggableDependency(ClassNativeGenerator.class) + static native TClass byteClass(); + + @InjectedBy(ClassNativeGenerator.class) + @PluggableDependency(ClassNativeGenerator.class) + static native TClass doubleClass(); + @InjectedBy(ClassNativeGenerator.class) @PluggableDependency(ClassNativeGenerator.class) public static native TClass wrap(Class cls); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNotFoundException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNotFoundException.java index 9fc95b1d2..7b5ee6187 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNotFoundException.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNotFoundException.java @@ -33,4 +33,8 @@ public class TClassNotFoundException extends TReflectiveOperationException { public TClassNotFoundException(TString message) { super(message); } + + public TThrowable getException() { + return getCause(); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index a99722a4d..dac196fd7 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.lang; import org.teavm.javascript.ni.GeneratedBy; +import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.Rename; /** @@ -23,14 +24,26 @@ import org.teavm.javascript.ni.Rename; * @author Alexey Andreev */ public class TDouble extends TNumber implements TComparable { - public static double POSITIVE_INFINITY = 1 / 0.0; - public static double NEGATIVE_INFINITY = -POSITIVE_INFINITY; + public static final double POSITIVE_INFINITY = 1 / 0.0; + public static final double NEGATIVE_INFINITY = -POSITIVE_INFINITY; + public static final double NaN = getNaN(); + public static final double MAX_VALUE = 0x1.FFFFFFFFFFFFFP+1023; + public static final double MIN_NORMAL = -0x1.0P+1022; + public static final double MIN_VALUE = 0x0.0000000000001P-1022; + public static final int MAX_EXPONENT = 1023; + public static final int MIN_EXPONENT = -1022; + public static final int SIZE = 64; + public static final TClass TYPE = TClass.doubleClass(); private double value; public TDouble(double value) { this.value = value; } + public TDouble(TString value) throws TNumberFormatException { + this.value = parseDouble(value); + } + @Override public double doubleValue() { return value; @@ -199,9 +212,151 @@ public class TDouble extends TNumber implements TComparable { return compare(value, other.value); } + public boolean isNaN() { + return isNaN(value); + } + + public boolean isInfinite() { + return isInfinite(value); + } + @GeneratedBy(DoubleNativeGenerator.class) public static native boolean isNaN(double v); + @InjectedBy(DoubleNativeGenerator.class) + private static native double getNaN(); + @GeneratedBy(DoubleNativeGenerator.class) public static native boolean isInfinite(double v); + + public static long doubleToRawLongBits(double value) { + 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); + if (exp < -1022) { + exp = -1023; + } + long mantissa = (long)(abs * binaryExponent(exp + 52)) & 0xFFFFFFFFFFFFFL; + return mantissa | ((exp + 1023L) << 52) | (value < 0 ? (1L << 63) : 0); + } + + 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 & (1 << 63)) != 0; + int rawExp = (int)((bits >> 52) & 0x7FFL) - 1023; + long mantissa = bits & 0xFFFFFFFFFFFFFL; + if (rawExp == 0) { + mantissa <<= 1; + } else { + mantissa |= (1L << 52); + } + double value = mantissa * binaryExponent(rawExp - 1023 - 52); + return !negative ? value : -value; + } + + public static TString toHexString(double d) { + if (isNaN(d)) { + return TString.wrap("NaN"); + } else if (isInfinite(d)) { + return d > 0 ? TString.wrap("Infinity") : TString.wrap("-Infinity"); + } + char[] buffer = new char[30]; + int sz = 0; + long bits = doubleToLongBits(d); + long mantissa = bits & 0xFFFFFFFFFFFFFL; + for (int i = 0; i < 13; ++i) { + int digit = (int)(mantissa & 0xF); + if (digit > 0 || sz > 0) { + buffer[sz++] = TCharacter.forDigit(digit, 16); + } + mantissa >>>= 4; + } + if (sz == 0) { + buffer[sz++] = '0'; + } + buffer[sz++] = '.'; + int exp = (int)((bits >>> 52) & 0x7FF); + if (exp == -1023) { + buffer[sz++] = '0'; + ++exp; + } else { + buffer[sz++] = '1'; + } + buffer[sz++] = 'x'; + buffer[sz++] = '0'; + if ((bits & (1L << 63)) == 0) { + buffer[sz++] = '-'; + } + int half = sz / 2; + for (int i = 0; i < half; ++i) { + char tmp = buffer[i]; + buffer[i] = buffer[sz - i - 1]; + buffer[sz - i - 1] = tmp; + } + + buffer[sz++] = 'p'; + if (exp < 0) { + exp = -exp; + buffer[sz++] = '-'; + } + int pos = 1000; + boolean first = true; + for (int i = 0; i < 4; ++i) { + int digit = exp / pos; + if (digit > 0 || !first) { + buffer[sz++] = TCharacter.forDigit(digit, 10); + first = false; + } + digit *= 10; + pos /= 10; + } + if (first) { + buffer[sz++] = '0'; + } + + return new TString(buffer, 0, sz); + } + + public 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/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index 2fc765c26..6e01979d6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -224,6 +224,11 @@ public final class TMath extends TObject { } } else if (d < 1) { int expBit = 1 << (negativeExponents.length - 1); + int offset = 0; + if (d < 0x1P-1024) { + d *= 0x1P52; + offset = 52; + } for (int i = negativeExponents.length - 1; i >= 0; --i) { if (d < negativeExponents[i]) { d *= exponents[i]; @@ -231,7 +236,7 @@ public final class TMath extends TObject { } expBit >>>= 1; } - exp = -exp; + exp = -(exp + offset); } return exp; } @@ -263,7 +268,9 @@ public final class TMath extends TObject { } private static class ExponentConstants { - public static double[] exponents = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 }; - public static double[] negativeExponents = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32, 1E-64, 1E-128, 1E-256 }; + public static double[] exponents = { 0x1P1, 0x1P2, 0x1P4, 0x1P8, 0x1P16, 0x1P32, 0x1P64, 0x1P128, 0x1P256, + 0x1P256, 0x1P512 }; + public static double[] negativeExponents = { 0x1P-1, 0x1P-2, 0x1P-4, 0x1P-8, 0x1P-16, 0x1P-32, 0x1P-64, + 0x1P-128, 0x1P-256, 0x1P-256, 0x1P-512 }; } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java index 1254366af..7ed5ee8b8 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/DoubleTest.java @@ -48,4 +48,37 @@ public class DoubleTest { assertEquals(0, Double.parseDouble("00000"), 1E-12); assertEquals(0, Double.parseDouble("00000.0000"), 1E-12); } + + @Test + public void longBitsExtracted() { + assertEquals(0x41E23456789ABCDEL, Double.doubleToLongBits(0x1.23456789ABCDEP+31)); + } + + @Test + public void subNormalLongBitsExtracted() { + assertEquals(0x00000056789ABCDEL, Double.doubleToLongBits(0x0.00056789ABCDEP-1022)); + } + + @Test + public void longBitsPacked() { + assertEquals(0x1.23456789ABCDEP+31, Double.longBitsToDouble(0x41E23456789ABCDEL), 0x1.0P-19); + } + + @Test + public void subNormalLongBitsPacked() { + assertEquals(0x0.00056789ABCDEP-1022, Double.longBitsToDouble(0x00000056789ABCDEL), 0x1.0P-19); + } + + @Test + public void hexStringBuilt() { + assertEquals("0x1.23456789abcdep31", Double.toHexString(0x1.23456789ABCDEP+31)); + assertEquals("0x1.0p0", Double.toHexString(1)); + assertEquals("-0x1.0p0", Double.toHexString(-1)); + assertEquals("0x1.0p1", Double.toHexString(2)); + assertEquals("0x1.8p1", Double.toHexString(3)); + assertEquals("0x1.0p-1", Double.toHexString(0.5)); + assertEquals("0x1.0p-2", Double.toHexString(0.25)); + assertEquals("0x1.0p-1022", Double.toHexString(Double.MIN_NORMAL)); + assertEquals("0x0.0000000000001p-1022", Double.toHexString(Double.MIN_VALUE)); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 682a30384..23397c78e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -817,19 +817,19 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext visitBinary(expr, "<<"); break; case LEFT_SHIFT_LONG: - visitBinaryFunction(expr, "Long_lsh"); + visitBinaryFunction(expr, "Long_shr"); break; case RIGHT_SHIFT: visitBinary(expr, ">>"); break; case RIGHT_SHIFT_LONG: - visitBinaryFunction(expr, "Long_rsh"); + visitBinaryFunction(expr, "Long_shr"); break; case UNSIGNED_RIGHT_SHIFT: visitBinary(expr, ">>>"); break; case UNSIGNED_RIGHT_SHIFT_LONG: - visitBinaryFunction(expr, "Long_rshu"); + visitBinaryFunction(expr, "Long_shru"); break; } }