Make Long/Float/Double/Integer compare/hashCode methods cross-platform

This commit is contained in:
Alexey Andreev 2017-05-07 22:06:44 +03:00
parent e884bb35c9
commit 48d14570b2
11 changed files with 158 additions and 23 deletions

View File

@ -53,5 +53,7 @@ public class JCLPlugin implements TeaVMPlugin {
String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class,
CallSite.class), new LambdaMetafactorySubstitutor()); CallSite.class), new LambdaMetafactorySubstitutor());
host.add(new ScalaHacks()); host.add(new ScalaHacks());
host.add(new NumericClassTransformer());
} }
} }

View File

@ -0,0 +1,93 @@
/*
* Copyright 2017 konsoletyper.
*
* 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.impl;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.Program;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.NumericOperandType;
public class NumericClassTransformer implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
switch (cls.getName()) {
case "java.lang.Integer":
transformInteger(cls);
break;
case "java.lang.Long":
transformLong(cls);
break;
case "java.lang.Float":
transformFloat(cls);
break;
case "java.lang.Double":
transformDouble(cls);
break;
}
}
private void transformInteger(ClassHolder cls) {
transformCompareMethod(cls, ValueType.INTEGER, NumericOperandType.INT);
}
private void transformLong(ClassHolder cls) {
transformCompareMethod(cls, ValueType.LONG, NumericOperandType.LONG);
}
private void transformFloat(ClassHolder cls) {
transformCompareMethod(cls, ValueType.FLOAT, NumericOperandType.FLOAT);
}
private void transformDouble(ClassHolder cls) {
transformCompareMethod(cls, ValueType.DOUBLE, NumericOperandType.DOUBLE);
}
private void transformCompareMethod(ClassHolder cls, ValueType type, NumericOperandType insnType) {
MethodHolder method = cls.getMethod(new MethodDescriptor("compare", type, type, ValueType.INTEGER));
Program program = new Program();
program.createVariable();
Variable firstArg = program.createVariable();
Variable secondArg = program.createVariable();
Variable result = program.createVariable();
BasicBlock block = program.createBasicBlock();
BinaryInstruction insn = new BinaryInstruction(BinaryOperation.COMPARE, insnType);
insn.setFirstOperand(firstArg);
insn.setSecondOperand(secondArg);
insn.setReceiver(result);
block.add(insn);
ExitInstruction exit = new ExitInstruction();
exit.setValueToReturn(result);
block.add(exit);
method.setProgram(program);
method.getModifiers().remove(ElementModifier.NATIVE);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2014 Alexey Andreev. * Copyright 2017 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,10 +21,6 @@ import org.teavm.backend.javascript.spi.Generator;
import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class LongNativeGenerator implements Generator { public class LongNativeGenerator implements Generator {
@Override @Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
@ -33,10 +29,6 @@ public class LongNativeGenerator implements Generator {
writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ") writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ")
.append(context.getParameterName(2)).append(");").softNewLine(); .append(context.getParameterName(2)).append(");").softNewLine();
break; break;
case "hashCode":
writer.append("return ").append(context.getParameterName(1)).append(".hi ^ ")
.append(context.getParameterName(1)).append(".lo;").softNewLine();
break;
} }
} }
} }

View File

@ -203,9 +203,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
return (int) (h >>> 32) ^ (int) h; return (int) (h >>> 32) ^ (int) h;
} }
public static int compare(double a, double b) { public static native int compare(double a, double b);
return a > b ? 1 : a < b ? -1 : 0;
}
@Override @Override
public int compareTo(TDouble other) { public int compareTo(TDouble other) {

View File

@ -230,9 +230,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
return isInfinite(value); return isInfinite(value);
} }
public static int compare(float f1, float f2) { public static native int compare(float f1, float f2);
return f1 > f2 ? 1 : f2 < f1 ? -1 : 0;
}
@Override @Override
public int compareTo(TFloat other) { public int compareTo(TFloat other) {

View File

@ -244,9 +244,7 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
return compare(value, other.value); return compare(value, other.value);
} }
public static int compare(int x, int y) { public static native int compare(int x, int y);
return x > y ? 1 : x < y ? -1 : 0;
}
public static int numberOfLeadingZeros(int i) { public static int numberOfLeadingZeros(int i) {
if (i == 0) { if (i == 0) {

View File

@ -15,8 +15,6 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.GeneratedBy;
public class TLong extends TNumber implements TComparable<TLong> { public class TLong extends TNumber implements TComparable<TLong> {
public static final long MIN_VALUE = -0x8000000000000000L; public static final long MIN_VALUE = -0x8000000000000000L;
public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL; public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL;
@ -199,8 +197,9 @@ public class TLong extends TNumber implements TComparable<TLong> {
return hashCode(value); return hashCode(value);
} }
@GeneratedBy(LongNativeGenerator.class) private static int hashCode(long value) {
private static native int hashCode(long value); return (int) (value ^ (value >>> 32));
}
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
@ -210,7 +209,6 @@ public class TLong extends TNumber implements TComparable<TLong> {
return other instanceof TLong && ((TLong) other).value == value; return other instanceof TLong && ((TLong) other).value == value;
} }
@GeneratedBy(LongNativeGenerator.class)
public static native int compare(long a, long b); public static native int compare(long a, long b);
@Override @Override

View File

@ -86,4 +86,11 @@ public class DoubleTest {
assertEquals("0x0.8p-1022", Double.toHexString(0x0.8p-1022)); assertEquals("0x0.8p-1022", Double.toHexString(0x0.8p-1022));
assertEquals("0x0.001p-1022", Double.toHexString(0x0.001p-1022)); assertEquals("0x0.001p-1022", Double.toHexString(0x0.001p-1022));
} }
@Test
public void compares() {
assertTrue(Double.compare(10, 5) > 0);
assertTrue(Double.compare(5, 10) < 0);
assertTrue(Double.compare(5, 5) == 0);
}
} }

View File

@ -85,4 +85,11 @@ public class FloatTest {
assertEquals("0x1.0p-126", Float.toHexString((float)Math.pow(2, -126))); assertEquals("0x1.0p-126", Float.toHexString((float)Math.pow(2, -126)));
assertEquals("0x0.001p-126", Float.toHexString(0x0.001p-126f)); assertEquals("0x0.001p-126", Float.toHexString(0x0.001p-126f));
} }
@Test
public void compares() {
assertTrue(Float.compare(10, 5) > 0);
assertTrue(Float.compare(5, 10) < 0);
assertTrue(Float.compare(5, 5) == 0);
}
} }

View File

@ -127,4 +127,13 @@ public class IntegerTest {
assertEquals(0xFFFFFFFF, Integer.reverse(0xFFFFFFFF)); assertEquals(0xFFFFFFFF, Integer.reverse(0xFFFFFFFF));
assertEquals(0xF63BA000, Integer.reverse(0x5DC6F)); assertEquals(0xF63BA000, Integer.reverse(0x5DC6F));
} }
@Test
public void compares() {
assertTrue(Integer.compare(10, 5) > 0);
assertTrue(Integer.compare(5, 10) < 0);
assertTrue(Integer.compare(5, 5) == 0);
assertTrue(Integer.compare(Integer.MAX_VALUE, Integer.MIN_VALUE) > 0);
assertTrue(Integer.compare(Integer.MIN_VALUE, Integer.MAX_VALUE) < 0);
}
} }

View File

@ -0,0 +1,33 @@
/*
* Copyright 2017 konsoletyper.
*
* 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 static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.teavm.junit.TeaVMTestRunner;
@RunWith(TeaVMTestRunner.class)
public class LongTest {
@Test
public void compares() {
assertTrue(Long.compare(10, 5) > 0);
assertTrue(Long.compare(5, 10) < 0);
assertTrue(Long.compare(5, 5) == 0);
assertTrue(Long.compare(Long.MAX_VALUE, Long.MIN_VALUE) > 0);
assertTrue(Long.compare(Long.MIN_VALUE, Long.MAX_VALUE) < 0);
}
}