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 f2f9deb60..88aa1666e 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 @@ -92,6 +92,9 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "intClass": context.getWriter().append("$rt_cls($rt_intcls())"); break; + case "longClass": + context.getWriter().append("$rt_cls($rt_longcls())"); + break; case "floatClass": context.getWriter().append("$rt_cls($rt_floatcls())"); break; @@ -191,6 +194,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "shortClass": case "charClass": case "intClass": + case "longClass": case "floatClass": case "doubleClass": case "wrapClass": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java index 54f324dd4..4a195bfbf 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java @@ -33,6 +33,10 @@ public class LongNativeGenerator implements Generator { writer.append("Long_compare(").append(context.getParameterName(1)).append(", ") .append(context.getParameterName(2)).append(");").softNewLine(); break; + case "hashCode": + writer.append("return ").append(context.getParameterName(1)).append(".hi ^ ") + .append(context.getParameterName(1)).append(".lo;").softNewLine(); + break; } } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java index c4e39724d..754c5f0b4 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TAbstractStringBuilder.java @@ -152,43 +152,30 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ } protected TAbstractStringBuilder insert(int target, long value) { + return insert(target, value, 10); + } + + protected TAbstractStringBuilder insert(int target, long value, int radix) { boolean positive = true; if (value < 0) { positive = false; value = -value; } - if (value < 10) { + if (value < radix) { if (!positive) { insertSpace(target, target + 2); buffer[target++] = '-'; } else { insertSpace(target, target + 1); } - buffer[target++] = (char)('0' + value); + buffer[target++] = Character.forDigit((int)value, radix); } else { - int sz = 0; + int sz = 1; long pos = 1; - if (pos * 10000000000000000L <= value) { - pos *= 10000000000000000L; - sz |= 16; + while (pos * radix > pos && pos * radix <= value) { + pos *= radix; + ++sz; } - if ((sz | 8) <= 18 && pos * 100000000L <= value) { - pos *= 100000000L; - sz |= 8; - } - if ((sz | 4) <= 18 && pos * 10000L <= value) { - pos *= 10000L; - sz |= 4; - } - if ((sz | 2) <= 18 && pos * 100L <= value) { - pos *= 100L; - sz |= 2; - } - if ((sz | 1) <= 18 && pos * 10L <= value) { - pos *= 10L; - sz |= 1; - } - ++sz; if (!positive) { ++sz; } @@ -197,9 +184,9 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ buffer[target++] = '-'; } while (pos > 0) { - buffer[target++] = (char)('0' + value / pos); + buffer[target++] = TCharacter.forDigit((int)(value / pos), radix); value %= pos; - pos /= 10; + pos /= radix; } } return this; 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 d89fa5d9a..04d7f76d9 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 @@ -93,6 +93,10 @@ public class TClass extends TObject { @PluggableDependency(ClassNativeGenerator.class) static native TClass intClass(); + @InjectedBy(ClassNativeGenerator.class) + @PluggableDependency(ClassNativeGenerator.class) + static native TClass longClass(); + @InjectedBy(ClassNativeGenerator.class) @PluggableDependency(ClassNativeGenerator.class) static native TClass floatClass(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java index 8d75bcf3a..d70498198 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInteger.java @@ -172,7 +172,7 @@ public class TInteger extends TNumber implements TComparable { } public static TInteger getInteger(TString nm, int val) { - return getInteger(nm, val); + return getInteger(nm, TInteger.valueOf(val)); } public static TInteger getInteger(TString nm, TInteger val) { @@ -336,16 +336,16 @@ public class TInteger extends TNumber implements TComparable { } public static int reverse(int i) { - i = (i & 0xAAAAAAAA) >> 1 + (i & 0x55555555) << 1; - i = (i & 0xCCCCCCCC) >> 2 + (i & 0x33333333) << 2; - i = (i & 0xF0F0F0F0) >> 4 + (i & 0x0F0F0F0F) << 4; - i = (i & 0xFF00FF00) >> 8 + (i & 0x00FF00FF) << 8; - i = (i & 0xFFFF0000) >> 16 + (i & 0x0000FFFF) << 16; + i = (i & 0xAAAAAAAA) >> 1 | (i & 0x55555555) << 1; + i = (i & 0xCCCCCCCC) >> 2 | (i & 0x33333333) << 2; + i = (i & 0xF0F0F0F0) >> 4 | (i & 0x0F0F0F0F) << 4; + i = (i & 0xFF00FF00) >> 8 | (i & 0x00FF00FF) << 8; + i = (i & 0xFFFF0000) >> 16 | (i & 0x0000FFFF) << 16; return i; } public static int reverseBytes(int i) { - i = (i & 0xFF00FF00) >> 8 + (i & 0x00FF00FF) << 8; + i = (i & 0xFF00FF00) >> 8 | (i & 0x00FF00FF) << 8; i = i >> 16 + i << 16; return i; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java index dcb3c166b..38c01effe 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TLong.java @@ -22,17 +22,138 @@ import org.teavm.javascript.ni.Rename; * * @author Alexey Andreev */ -public class TLong extends TNumber { +public class TLong extends TNumber implements TComparable { + public static final long MIN_VALUE = -0x8000000000000000L; + public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL; + public static final TClass TYPE = TClass.longClass(); + public static final int SIZE = 64; private long value; public TLong(long value) { this.value = value; } + public TLong(TString value) throws TNumberFormatException { + this(parseLong(value)); + } + public static TLong valueOf(long value) { return new TLong(value); } + public static long parseLong(TString s, int radix) throws TNumberFormatException { + if (radix < TCharacter.MIN_RADIX || radix > TCharacter.MAX_RADIX) { + throw new TNumberFormatException(TString.wrap("Illegal radix: " + radix)); + } + if (s == null || s.isEmpty()) { + throw new TNumberFormatException(TString.wrap("String is null or empty")); + } + boolean negative = false; + int index = 0; + switch (s.charAt(0)) { + case '-': + negative = true; + index = 1; + break; + case '+': + index = 1; + break; + } + long value = 0; + while (index < s.length()) { + int digit = TCharacter.getNumericValue(s.charAt(index++)); + if (digit < 0) { + throw new TNumberFormatException(TString.wrap("String contains invalid digits: " + s)); + } + if (digit >= radix) { + throw new TNumberFormatException(TString.wrap("String contains digits out of radix " + radix + + ": " + s)); + } + value = radix * value + digit; + if (value < 0) { + if (index == s.length() && value == MIN_VALUE && negative) { + return MIN_VALUE; + } + throw new TNumberFormatException(TString.wrap("The value is too big for int type: " + s)); + } + } + return negative ? -value : value; + } + + public static long parseLong(TString s) throws TNumberFormatException { + return parseLong(s, 10); + } + + public static TLong valueOf(TString s, int radix) throws TNumberFormatException { + return valueOf(parseLong(s, radix)); + } + + public static TLong valueOf(TString s) throws TNumberFormatException { + return valueOf(parseLong(s)); + } + + public static TLong decode(TString nm) throws TNumberFormatException { + if (nm == null || nm.isEmpty()) { + throw new TNumberFormatException(TString.wrap("Can't parse empty or null string")); + } + int index = 0; + boolean negaive = false; + if (nm.charAt(index) == '+') { + ++index; + } else if (nm.charAt(index) == '-') { + ++index; + negaive = true; + } + if (index >= nm.length()) { + throw new TNumberFormatException(TString.wrap("The string does not represent a number")); + } + int radix = 10; + if (nm.charAt(index) == '#') { + radix = 16; + ++index; + } else if (nm.charAt(index) == '0') { + ++index; + if (index == nm.length()) { + return TLong.valueOf(0); + } + if (nm.charAt(index) == 'x' || nm.charAt(index) == 'X') { + radix = 16; + ++index; + } else { + radix = 8; + } + } + if (index >= nm.length()) { + throw new TNumberFormatException(TString.wrap("The string does not represent a number")); + } + long value = 0; + while (index < nm.length()) { + int digit = decodeDigit(nm.charAt(index++)); + if (digit >= radix) { + throw new TNumberFormatException(TString.wrap("The string does not represent a number")); + } + value = value * radix + digit; + if (value < 0) { + if (negaive && value == MIN_VALUE && index == nm.length()) { + return TLong.valueOf(MIN_VALUE); + } + throw new TNumberFormatException(TString.wrap("The string represents a too big number")); + } + } + return TLong.valueOf(negaive ? -value : value); + } + + private static int decodeDigit(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'z') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'Z') { + return c - 'A' + 10; + } else { + return 255; + } + } @Override public int intValue() { return (int)value; @@ -53,6 +174,22 @@ public class TLong extends TNumber { return value; } + public static TString toString(long i, int radix) { + return TString.wrap(new TStringBuilder().insert(0, i, radix).toString()); + } + + public static TString toHexString(long i) { + return toString(i, 16); + } + + public static TString toOctalString(long i) { + return toString(i, 8); + } + + public static TString toBinaryString(long i) { + return toString(i, 2); + } + public TString toString(long value) { return TString.wrap(new TStringBuilder().append(value).toString()); } @@ -63,6 +200,10 @@ public class TLong extends TNumber { return toString(value); } + @Override + @GeneratedBy(LongNativeGenerator.class) + public native int hashCode(); + @Override public boolean equals(TObject other) { if (this == other) { @@ -73,4 +214,136 @@ public class TLong extends TNumber { @GeneratedBy(LongNativeGenerator.class) public static native int compare(long a, long b); + + @Override + public int compareTo(TLong other) { + return compare(value, other.value); + } + + public static TLong getLong(TString nm) { + return getLong(nm, null); + } + + public static TLong getLong(TString nm, long val) { + return getLong(nm, TLong.valueOf(val)); + } + + public static TLong getLong(TString nm, TLong val) { + TString result = TSystem.getProperty(nm); + return result != null ? TLong.valueOf(result) : val; + } + + public static long highestOneBit(long i) { + return 0x8000000000000000L >>> numberOfLeadingZeros(i); + } + + public static int numberOfLeadingZeros(long i) { + if (i == 0) { + return SIZE; + } + int n = 0; + if (i >>> 32 != 0) { + i >>>= 32; + n |= 32; + } + if (i >>> 16 != 0) { + i >>>= 16; + n |= 16; + } + if (i >>> 8 != 0) { + i >>>= 8; + n |= 8; + } + if (i >>> 4 != 0) { + i >>>= 4; + n |= 4; + } + if (i >>> 2 != 0) { + i >>>= 2; + n |= 2; + } + if (i >>> 1 != 0) { + i >>>= 1; + n |= 1; + } + return SIZE - n - 1; + } + + public static int numberOfTrailingZeros(long i) { + if (i == 0) { + return SIZE; + } + int n = 0; + if (i << 32 != 0) { + i <<= 32; + n |= 32; + } + if (i << 16 != 0) { + i <<= 16; + n |= 16; + } + if (i << 8 != 0) { + i <<= 8; + n |= 8; + } + if (i << 4 != 0) { + i <<= 4; + n |= 4; + } + if (i << 2 != 0) { + i <<= 2; + n |= 2; + } + if (i << 1 != 0) { + i <<= 1; + n |= 1; + } + return SIZE - n - 1; + } + + + public static long lowestOneBit(long i) { + return 1L << numberOfTrailingZeros(i); + } + + public static int bitCount(long i) { + i = (i & 0xAAAAAAAAAAAAAAAAL) >> 1 + i & 0x5555555555555555L; + i = (i & 0xCCCCCCCCCCCCCCCCL) >> 2 + i & 0x3333333333333333L; + i = (i & 0x3030303030303030L) >> 4 + i & 0x0303030303030303L; + i = (i & 0x0700070007000700L) >> 8 + i & 0x0007000700070007L; + i = (i & 0x000F0000000F0000L) >> 16 + i & 0x0000000F0000000FL; + i = (i & 0x0000001F00000000L) >> 32 + i & 0x000000000000001FL; + return (int)i; + } + + public static long rotateLeft(long i, int distance) { + distance &= 0x3F; + return (i << distance) | (i >>> (64 - distance)); + } + + public static long rotateRight(long i, int distance) { + distance &= 0x3F; + return (i >>> distance) | (i << (64 - distance)); + } + + public static long reverse(long i) { + i = (i & 0xAAAAAAAAAAAAAAAAL) >> 1 | (i & 0x5555555555555555L) << 1; + i = (i & 0xCCCCCCCCCCCCCCCCL) >> 2 | (i & 0x3333333333333333L) << 2; + i = (i & 0xF0F0F0F0F0F0F0F0L) >> 4 | (i & 0x0F0F0F0F0F0F0F0FL) << 4; + i = (i & 0xFF00FF00FF00FF00L) >> 8 | (i & 0x00FF00FF00FF00FFL) << 8; + i = (i & 0xFFFF0000FFFF0000L) >> 16 | (i & 0x0000FFFF0000FFFFL) << 16; + i = (i & 0xFFFF0000FFFF0000L) >> 32 | (i & 0x0000FFFF0000FFFFL) << 32; + return i; + } + + public static long reverseBytes(long i) { + i = (i & 0xFF00FF00FF00FF00L) >> 8 | (i & 0x00FF00FF00FF00FFL) << 8; + i = (i & 0xFFFF0000FFFF0000L) >> 16 | (i & 0x0000FFFF0000FFFFL) << 16; + i = i >> 32 | i << 32; + return i; + } + + public static int signum(long i) { + return (int)((i >> 63) | (-i >>> 63)); + } }