mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Support Java 17 records
This commit is contained in:
parent
29fd95be95
commit
f0d805fda8
|
@ -27,6 +27,7 @@ import org.teavm.classlib.impl.currency.CountriesGenerator;
|
||||||
import org.teavm.classlib.impl.currency.CurrenciesGenerator;
|
import org.teavm.classlib.impl.currency.CurrenciesGenerator;
|
||||||
import org.teavm.classlib.impl.currency.CurrencyHelper;
|
import org.teavm.classlib.impl.currency.CurrencyHelper;
|
||||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||||
|
import org.teavm.classlib.impl.record.ObjectMethodsSubstitutor;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProvider;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProvider;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderPatch;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProviderPatch;
|
||||||
|
@ -104,6 +105,13 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
ValueType.object("java.lang.String"), ValueType.object("java.lang.invoke.MethodType"),
|
ValueType.object("java.lang.String"), ValueType.object("java.lang.invoke.MethodType"),
|
||||||
ValueType.arrayOf(ValueType.object("java.lang.Object")),
|
ValueType.arrayOf(ValueType.object("java.lang.Object")),
|
||||||
ValueType.object("java.lang.invoke.CallSite")), lms);
|
ValueType.object("java.lang.invoke.CallSite")), lms);
|
||||||
|
host.add(new MethodReference("java.lang.runtime.ObjectMethods", "bootstrap",
|
||||||
|
ValueType.object("java.lang.invoke.MethodHandles$Lookup"), ValueType.object("java.lang.String"),
|
||||||
|
ValueType.object("java.lang.invoke.TypeDescriptor"), ValueType.object("java.lang.Class"),
|
||||||
|
ValueType.object("java.lang.String"),
|
||||||
|
ValueType.arrayOf(ValueType.object("java.lang.invoke.MethodHandle")),
|
||||||
|
ValueType.object("java.lang.Object")),
|
||||||
|
new ObjectMethodsSubstitutor());
|
||||||
|
|
||||||
StringConcatFactorySubstitutor stringConcatSubstitutor = new StringConcatFactorySubstitutor();
|
StringConcatFactorySubstitutor stringConcatSubstitutor = new StringConcatFactorySubstitutor();
|
||||||
host.add(new MethodReference("java.lang.invoke.StringConcatFactory", "makeConcat",
|
host.add(new MethodReference("java.lang.invoke.StringConcatFactory", "makeConcat",
|
||||||
|
|
|
@ -43,7 +43,7 @@ import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.emit.ProgramEmitter;
|
import org.teavm.model.emit.ProgramEmitter;
|
||||||
import org.teavm.model.emit.ValueEmitter;
|
import org.teavm.model.emit.ValueEmitter;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.util.InvokeDynamicUtil;
|
||||||
|
|
||||||
public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor {
|
public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor {
|
||||||
private static final int FLAG_SERIALIZABLE = 1;
|
private static final int FLAG_SERIALIZABLE = 1;
|
||||||
|
@ -114,7 +114,7 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
implementorSignature[i + capturedVarCount]);
|
implementorSignature[i + capturedVarCount]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueEmitter result = invoke(pe, implMethod, passedArguments);
|
ValueEmitter result = InvokeDynamicUtil.invoke(pe, implMethod, passedArguments);
|
||||||
ValueType expectedResult = instantiatedMethodType[instantiatedMethodType.length - 1];
|
ValueType expectedResult = instantiatedMethodType[instantiatedMethodType.length - 1];
|
||||||
if (result != null && expectedResult != ValueType.VOID) {
|
if (result != null && expectedResult != ValueType.VOID) {
|
||||||
ValueType actualResult = implementorSignature[implementorSignature.length - 1];
|
ValueType actualResult = implementorSignature[implementorSignature.length - 1];
|
||||||
|
@ -179,43 +179,6 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
return callerPe.construct(ctor.getOwnerName(), callSite.getArguments().toArray(new ValueEmitter[0]));
|
return callerPe.construct(ctor.getOwnerName(), callSite.getArguments().toArray(new ValueEmitter[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueEmitter invoke(ProgramEmitter pe, MethodHandle handle, ValueEmitter[] arguments) {
|
|
||||||
switch (handle.getKind()) {
|
|
||||||
case GET_FIELD:
|
|
||||||
return arguments[0].getField(handle.getName(), handle.getValueType());
|
|
||||||
case GET_STATIC_FIELD:
|
|
||||||
return pe.getField(handle.getClassName(), handle.getName(), handle.getValueType());
|
|
||||||
case PUT_FIELD:
|
|
||||||
arguments[0].setField(handle.getName(), arguments[0].cast(handle.getValueType()));
|
|
||||||
return null;
|
|
||||||
case PUT_STATIC_FIELD:
|
|
||||||
pe.setField(handle.getClassName(), handle.getName(), arguments[0].cast(handle.getValueType()));
|
|
||||||
return null;
|
|
||||||
case INVOKE_VIRTUAL:
|
|
||||||
case INVOKE_INTERFACE:
|
|
||||||
case INVOKE_SPECIAL: {
|
|
||||||
for (int i = 1; i < arguments.length; ++i) {
|
|
||||||
arguments[i] = arguments[i].cast(handle.getArgumentType(i - 1));
|
|
||||||
}
|
|
||||||
arguments[0] = arguments[0].cast(ValueType.object(handle.getClassName()));
|
|
||||||
InvocationType type = handle.getKind() == MethodHandleType.INVOKE_SPECIAL
|
|
||||||
? InvocationType.SPECIAL
|
|
||||||
: InvocationType.VIRTUAL;
|
|
||||||
return arguments[0].invoke(type, handle.getName(), handle.getValueType(),
|
|
||||||
Arrays.copyOfRange(arguments, 1, arguments.length));
|
|
||||||
}
|
|
||||||
case INVOKE_STATIC:
|
|
||||||
for (int i = 0; i < arguments.length; ++i) {
|
|
||||||
arguments[i] = arguments[i].cast(handle.getArgumentType(i));
|
|
||||||
}
|
|
||||||
return pe.invoke(handle.getClassName(), handle.getName(), handle.getValueType(), arguments);
|
|
||||||
case INVOKE_CONSTRUCTOR:
|
|
||||||
return pe.construct(handle.getClassName(), arguments);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unexpected handle type: " + handle.getKind());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ValueEmitter tryConvertArgument(ValueEmitter arg, ValueType from, ValueType to) {
|
private ValueEmitter tryConvertArgument(ValueEmitter arg, ValueType from, ValueType to) {
|
||||||
if (from.equals(to)) {
|
if (from.equals(to)) {
|
||||||
return arg;
|
return arg;
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.impl.record;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||||
|
import org.teavm.dependency.DynamicCallSite;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.emit.ConditionEmitter;
|
||||||
|
import org.teavm.model.emit.ConditionProducer;
|
||||||
|
import org.teavm.model.emit.PhiEmitter;
|
||||||
|
import org.teavm.model.emit.ProgramEmitter;
|
||||||
|
import org.teavm.model.emit.ValueEmitter;
|
||||||
|
import org.teavm.model.util.InvokeDynamicUtil;
|
||||||
|
|
||||||
|
public class ObjectMethodsSubstitutor implements BootstrapMethodSubstitutor {
|
||||||
|
@Override
|
||||||
|
public ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter pe) {
|
||||||
|
switch (callSite.getCalledMethod().getName()) {
|
||||||
|
case "equals":
|
||||||
|
return substituteEquals(callSite, pe);
|
||||||
|
case "hashCode":
|
||||||
|
return substituteHashCode(callSite, pe);
|
||||||
|
case "toString":
|
||||||
|
return substituteToString(callSite, pe);
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unexpected method: " + callSite.getCalledMethod().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueEmitter substituteEquals(DynamicCallSite callSite, ProgramEmitter pe) {
|
||||||
|
ValueType type = callSite.getBootstrapArguments().get(0).getValueType();
|
||||||
|
|
||||||
|
ValueEmitter thisVar = callSite.getArguments().get(0);
|
||||||
|
ValueEmitter thatVar = callSite.getArguments().get(1);
|
||||||
|
BasicBlock joint = pe.prepareBlock();
|
||||||
|
PhiEmitter result = pe.phi(ValueType.INTEGER, joint);
|
||||||
|
pe.when(thisVar.isSame(thatVar)).thenDo(() -> {
|
||||||
|
pe.constant(1).propagateTo(result);
|
||||||
|
pe.jump(joint);
|
||||||
|
});
|
||||||
|
ConditionProducer classCondition = () -> thisVar.isNull()
|
||||||
|
.or(() -> thatVar.invokeVirtual("getClass", Class.class).isNotSame(pe.constant(type)));
|
||||||
|
pe.when(classCondition).thenDo(() -> {
|
||||||
|
pe.constant(0).propagateTo(result);
|
||||||
|
pe.jump(joint);
|
||||||
|
});
|
||||||
|
|
||||||
|
ValueEmitter castThatVar = thatVar.cast(type);
|
||||||
|
|
||||||
|
String names = callSite.getBootstrapArguments().get(1).getString();
|
||||||
|
int argIndex = 2;
|
||||||
|
int index = 0;
|
||||||
|
while (index < names.length()) {
|
||||||
|
int next = names.indexOf(';', index);
|
||||||
|
if (next < 0) {
|
||||||
|
next = names.length();
|
||||||
|
}
|
||||||
|
index = next + 1;
|
||||||
|
MethodHandle getter = callSite.getBootstrapArguments().get(argIndex++).getMethodHandle();
|
||||||
|
ValueEmitter thisField = InvokeDynamicUtil.invoke(pe, getter, thisVar);
|
||||||
|
ValueEmitter thatField = InvokeDynamicUtil.invoke(pe, getter, castThatVar);
|
||||||
|
pe.when(compareEquality(pe, getter.getValueType(), thisField, thatField).not()).thenDo(() -> {
|
||||||
|
pe.constant(0).propagateTo(result);
|
||||||
|
pe.jump(joint);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pe.constant(1).propagateTo(result);
|
||||||
|
pe.jump(joint);
|
||||||
|
pe.enter(joint);
|
||||||
|
|
||||||
|
return result.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConditionEmitter compareEquality(ProgramEmitter pe, ValueType type, ValueEmitter a, ValueEmitter b) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case CHARACTER:
|
||||||
|
case INTEGER:
|
||||||
|
case LONG:
|
||||||
|
return a.isEqualTo(b);
|
||||||
|
case FLOAT:
|
||||||
|
return pe.invoke(Float.class, "compare", int.class, a, b).isEqualTo(pe.constant(0));
|
||||||
|
case DOUBLE:
|
||||||
|
return pe.invoke(Double.class, "compare", int.class, a, b).isEqualTo(pe.constant(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pe.invoke(Objects.class, "equals", boolean.class, a.cast(Object.class), b.cast(Object.class)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueEmitter substituteHashCode(DynamicCallSite callSite, ProgramEmitter pe) {
|
||||||
|
ValueEmitter thisVar = callSite.getArguments().get(0);
|
||||||
|
String names = callSite.getBootstrapArguments().get(1).getString();
|
||||||
|
ValueEmitter resultVar = pe.constant(1);
|
||||||
|
|
||||||
|
int argIndex = 2;
|
||||||
|
int index = 0;
|
||||||
|
while (index < names.length()) {
|
||||||
|
int next = names.indexOf(';', index);
|
||||||
|
if (next < 0) {
|
||||||
|
next = names.length();
|
||||||
|
}
|
||||||
|
index = next + 1;
|
||||||
|
MethodHandle getter = callSite.getBootstrapArguments().get(argIndex++).getMethodHandle();
|
||||||
|
resultVar = resultVar.mul(31);
|
||||||
|
ValueEmitter thisField = InvokeDynamicUtil.invoke(pe, getter, thisVar);
|
||||||
|
resultVar = resultVar.add(hash(pe, getter.getValueType(), thisField));
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueEmitter hash(ProgramEmitter pe, ValueType type, ValueEmitter a) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return pe.invoke(Boolean.class, "hashCode", int.class, a);
|
||||||
|
case BYTE:
|
||||||
|
return pe.invoke(Byte.class, "hashCode", int.class, a);
|
||||||
|
case SHORT:
|
||||||
|
return pe.invoke(Short.class, "hashCode", int.class, a);
|
||||||
|
case CHARACTER:
|
||||||
|
return pe.invoke(Character.class, "hashCode", int.class, a);
|
||||||
|
case INTEGER:
|
||||||
|
return pe.invoke(Integer.class, "hashCode", int.class, a);
|
||||||
|
case LONG:
|
||||||
|
return pe.invoke(Long.class, "hashCode", int.class, a);
|
||||||
|
case FLOAT:
|
||||||
|
return pe.invoke(Float.class, "hashCode", int.class, a);
|
||||||
|
case DOUBLE:
|
||||||
|
return pe.invoke(Double.class, "hashCode", int.class, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pe.invoke(Objects.class, "hashCode", int.class, a.cast(Object.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueEmitter substituteToString(DynamicCallSite callSite, ProgramEmitter pe) {
|
||||||
|
ValueEmitter thisVar = callSite.getArguments().get(0);
|
||||||
|
String names = callSite.getBootstrapArguments().get(1).getString();
|
||||||
|
ValueEmitter resultVar = pe.construct(StringBuilder.class, pe.constant("["));
|
||||||
|
|
||||||
|
int argIndex = 2;
|
||||||
|
int index = 0;
|
||||||
|
while (index < names.length()) {
|
||||||
|
int next = names.indexOf(';', index);
|
||||||
|
if (next < 0) {
|
||||||
|
next = names.length();
|
||||||
|
}
|
||||||
|
String fieldName = names.substring(index, next);
|
||||||
|
MethodHandle getter = callSite.getBootstrapArguments().get(argIndex++).getMethodHandle();
|
||||||
|
|
||||||
|
String fieldTitle = (index == 0 ? "" : ", ") + fieldName + "=";
|
||||||
|
resultVar = resultVar.invokeVirtual("append", StringBuilder.class, pe.constant(fieldTitle));
|
||||||
|
ValueEmitter thisField = InvokeDynamicUtil.invoke(pe, getter, thisVar);
|
||||||
|
resultVar = resultVar.invokeVirtual("append", StringBuilder.class, thisField);
|
||||||
|
|
||||||
|
index = next + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar.invokeVirtual("append", StringBuilder.class, pe.constant("]"))
|
||||||
|
.invokeVirtual("toString", String.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
public abstract class TRecord {
|
||||||
|
}
|
|
@ -114,7 +114,8 @@ public class BasicBlockSplitter {
|
||||||
Map<BasicBlock, List<Incoming>> incomingsBySource = new LinkedHashMap<>();
|
Map<BasicBlock, List<Incoming>> incomingsBySource = new LinkedHashMap<>();
|
||||||
for (Phi phi : block.getPhis()) {
|
for (Phi phi : block.getPhis()) {
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
if (mappings[incoming.getSource().getIndex()] == incoming.getSource().getIndex()) {
|
if (incoming.getSource().getIndex() >= mappings.length
|
||||||
|
|| mappings[incoming.getSource().getIndex()] == incoming.getSource().getIndex()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
incomingsBySource.computeIfAbsent(incoming.getSource(), b -> new ArrayList<>()).add(incoming);
|
incomingsBySource.computeIfAbsent(incoming.getSource(), b -> new ArrayList<>()).add(incoming);
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.model.util;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.MethodHandleType;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.emit.ProgramEmitter;
|
||||||
|
import org.teavm.model.emit.ValueEmitter;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
|
||||||
|
public final class InvokeDynamicUtil {
|
||||||
|
private InvokeDynamicUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValueEmitter invoke(ProgramEmitter pe, MethodHandle handle, ValueEmitter... arguments) {
|
||||||
|
switch (handle.getKind()) {
|
||||||
|
case GET_FIELD:
|
||||||
|
return arguments[0].getField(handle.getName(), handle.getValueType());
|
||||||
|
case GET_STATIC_FIELD:
|
||||||
|
return pe.getField(handle.getClassName(), handle.getName(), handle.getValueType());
|
||||||
|
case PUT_FIELD:
|
||||||
|
arguments[0].setField(handle.getName(), arguments[0].cast(handle.getValueType()));
|
||||||
|
return null;
|
||||||
|
case PUT_STATIC_FIELD:
|
||||||
|
pe.setField(handle.getClassName(), handle.getName(), arguments[0].cast(handle.getValueType()));
|
||||||
|
return null;
|
||||||
|
case INVOKE_VIRTUAL:
|
||||||
|
case INVOKE_INTERFACE:
|
||||||
|
case INVOKE_SPECIAL: {
|
||||||
|
for (int i = 1; i < arguments.length; ++i) {
|
||||||
|
arguments[i] = arguments[i].cast(handle.getArgumentType(i - 1));
|
||||||
|
}
|
||||||
|
arguments[0] = arguments[0].cast(ValueType.object(handle.getClassName()));
|
||||||
|
InvocationType type = handle.getKind() == MethodHandleType.INVOKE_SPECIAL
|
||||||
|
? InvocationType.SPECIAL
|
||||||
|
: InvocationType.VIRTUAL;
|
||||||
|
return arguments[0].invoke(type, handle.getName(), handle.getValueType(),
|
||||||
|
Arrays.copyOfRange(arguments, 1, arguments.length));
|
||||||
|
}
|
||||||
|
case INVOKE_STATIC:
|
||||||
|
for (int i = 0; i < arguments.length; ++i) {
|
||||||
|
arguments[i] = arguments[i].cast(handle.getArgumentType(i));
|
||||||
|
}
|
||||||
|
return pe.invoke(handle.getClassName(), handle.getName(), handle.getValueType(), arguments);
|
||||||
|
case INVOKE_CONSTRUCTOR:
|
||||||
|
return pe.construct(handle.getClassName(), arguments);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unexpected handle type: " + handle.getKind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
tests/src/test/java/org/teavm/vm/RecordTest.java
Normal file
63
tests/src/test/java/org/teavm/vm/RecordTest.java
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.vm;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
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 RecordTest {
|
||||||
|
@Test
|
||||||
|
public void equalsMethod() {
|
||||||
|
assertEquals(new A(2, "q"), new A(2, "q"));
|
||||||
|
assertNotEquals(new A(2, "q"), new A(3, "q"));
|
||||||
|
assertNotEquals(new A(2, "q"), new A(2, "w"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hashCodeMethod() {
|
||||||
|
assertEquals(new A(2, "q").hashCode(), new A(2, "q").hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toStringMethod() {
|
||||||
|
String s = new A(2, "q").toString();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
index = s.indexOf("x", index);
|
||||||
|
assertTrue(index > 0);
|
||||||
|
++index;
|
||||||
|
|
||||||
|
index = s.indexOf("2", index);
|
||||||
|
assertTrue(index > 0);
|
||||||
|
++index;
|
||||||
|
|
||||||
|
index = s.indexOf("y", index);
|
||||||
|
assertTrue(index > 0);
|
||||||
|
++index;
|
||||||
|
|
||||||
|
index = s.indexOf("q", index);
|
||||||
|
assertTrue(index > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
record A(int x, String y) {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user