mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
Add support for Class.isAssignableFrom
This commit is contained in:
parent
bb4040af23
commit
26749150f6
|
@ -18,6 +18,7 @@ package org.teavm.backend.wasm;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public final class Example {
|
||||
private Example() {
|
||||
|
@ -34,6 +35,7 @@ public final class Example {
|
|||
testArrayList();
|
||||
testArrayCopy();
|
||||
testArrayIsObject();
|
||||
testIsAssignableFrom();
|
||||
}
|
||||
|
||||
private static void testFibonacci() {
|
||||
|
@ -131,6 +133,15 @@ public final class Example {
|
|||
println("array.equals(copy) = " + array.equals(copy));
|
||||
}
|
||||
|
||||
private static void testIsAssignableFrom() {
|
||||
println("Object.isAssignableFrom(byte[]) = " + Object.class.isAssignableFrom(new byte[0].getClass()));
|
||||
println("Object[].isAssignableFrom(Integer[]) = " + Object[].class.isAssignableFrom(new Integer[0].getClass()));
|
||||
println("Object[].isAssignableFrom(Integer) = " + Object[].class.isAssignableFrom(Integer.class));
|
||||
println("byte[].isAssignableFrom(Object) = " + byte[].class.isAssignableFrom(ValueType.Object.class));
|
||||
println("Base.isAssignableFrom(Derived1) = " + Base.class.isAssignableFrom(Derived1.class));
|
||||
println("Base.isAssignableFrom(A) = " + Base.class.isAssignableFrom(A.class));
|
||||
}
|
||||
|
||||
private static Base instance(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.Writer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -51,6 +52,7 @@ import org.teavm.backend.wasm.model.WasmType;
|
|||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBranch;
|
||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||
import org.teavm.backend.wasm.model.expression.WasmConditional;
|
||||
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
|
@ -61,6 +63,7 @@ import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
|||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
|
||||
import org.teavm.backend.wasm.patches.ClassPatch;
|
||||
import org.teavm.backend.wasm.render.WasmCRenderer;
|
||||
|
@ -216,60 +219,8 @@ public class WasmTarget implements TeaVMTarget {
|
|||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
|
||||
|
||||
module.setMemorySize(64);
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getOwnerName().equals(Allocator.class.getName())
|
||||
&& method.getName().equals("initialize")) {
|
||||
continue;
|
||||
}
|
||||
if (context.getIntrinsic(method.getReference()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MethodHolder implementor = method;
|
||||
AnnotationHolder delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateAnnot != null) {
|
||||
String methodName = delegateAnnot.getValue("value").getString();
|
||||
boolean found = false;
|
||||
for (MethodHolder candidate : cls.getMethods()) {
|
||||
if (candidate.getName().equals(methodName)) {
|
||||
if (found) {
|
||||
controller.getDiagnostics().error(new CallLocation(method.getReference()),
|
||||
"Method is delegated to " + methodName + " but several implementations "
|
||||
+ "found");
|
||||
break;
|
||||
}
|
||||
implementor = candidate;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (implementor.hasModifier(ElementModifier.NATIVE)) {
|
||||
if (context.getImportedMethod(method.getReference()) == null) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
controller.getDiagnostics().error(location, "Method {{m0}} is native but "
|
||||
+ "has no {{c1}} annotation on it", method.getReference(), Import.class.getName());
|
||||
}
|
||||
module.add(generator.generateNative(method.getReference()));
|
||||
continue;
|
||||
}
|
||||
if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (method == implementor) {
|
||||
module.add(generator.generate(method.getReference(), implementor));
|
||||
} else {
|
||||
module.add(generateStub(method, implementor));
|
||||
}
|
||||
if (controller.wasCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generateMethods(classes, context, generator, module);
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
classGenerator.postProcess();
|
||||
|
||||
WasmMemorySegment dataSegment = new WasmMemorySegment();
|
||||
|
@ -334,6 +285,163 @@ public class WasmTarget implements TeaVMTarget {
|
|||
}
|
||||
}
|
||||
|
||||
private void generateMethods(ListableClassHolderSource classes, WasmGenerationContext context,
|
||||
WasmGenerator generator, WasmModule module) {
|
||||
for (String className : classes.getClassNames()) {
|
||||
ClassHolder cls = classes.get(className);
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (method.getOwnerName().equals(Allocator.class.getName())
|
||||
&& method.getName().equals("initialize")) {
|
||||
continue;
|
||||
}
|
||||
if (context.getIntrinsic(method.getReference()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MethodHolder implementor = method;
|
||||
AnnotationHolder delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
|
||||
if (delegateAnnot != null) {
|
||||
String methodName = delegateAnnot.getValue("value").getString();
|
||||
boolean found = false;
|
||||
for (MethodHolder candidate : cls.getMethods()) {
|
||||
if (candidate.getName().equals(methodName)) {
|
||||
if (found) {
|
||||
controller.getDiagnostics().error(new CallLocation(method.getReference()),
|
||||
"Method is delegated to " + methodName + " but several implementations "
|
||||
+ "found");
|
||||
break;
|
||||
}
|
||||
implementor = candidate;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (implementor.hasModifier(ElementModifier.NATIVE)) {
|
||||
if (context.getImportedMethod(method.getReference()) == null) {
|
||||
CallLocation location = new CallLocation(method.getReference());
|
||||
controller.getDiagnostics().error(location, "Method {{m0}} is native but "
|
||||
+ "has no {{c1}} annotation on it", method.getReference(), Import.class.getName());
|
||||
}
|
||||
module.add(generator.generateNative(method.getReference()));
|
||||
continue;
|
||||
}
|
||||
if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (method == implementor) {
|
||||
module.add(generator.generate(method.getReference(), implementor));
|
||||
} else {
|
||||
module.add(generateStub(method, implementor));
|
||||
}
|
||||
if (controller.wasCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateIsSupertypeFunctions(TagRegistry tagRegistry, WasmModule module,
|
||||
WasmClassGenerator classGenerator) {
|
||||
for (ValueType type : classGenerator.getRegisteredClasses()) {
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangeIsSupertype(type));
|
||||
function.getParameters().add(WasmType.INT32);
|
||||
function.setResult(WasmType.INT32);
|
||||
module.add(function);
|
||||
|
||||
WasmLocal subtypeVar = new WasmLocal(WasmType.INT32, "subtype");
|
||||
function.add(subtypeVar);
|
||||
|
||||
if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
generateIsClass(subtypeVar, classGenerator, tagRegistry, className, function.getBody());
|
||||
} else if (type instanceof ValueType.Array) {
|
||||
ValueType itemType = ((ValueType.Array) type).getItemType();
|
||||
generateIsArray(subtypeVar, classGenerator, itemType, function.getBody());
|
||||
} else {
|
||||
int expected = classGenerator.getClassPointer(type);
|
||||
WasmExpression condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(expected));
|
||||
function.getBody().add(new WasmReturn(condition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateIsClass(WasmLocal subtypeVar, WasmClassGenerator classGenerator, TagRegistry tagRegistry,
|
||||
String className, List<WasmExpression> body) {
|
||||
List<TagRegistry.Range> ranges = tagRegistry.getRanges(className);
|
||||
if (ranges.isEmpty()) {
|
||||
body.add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
return;
|
||||
}
|
||||
|
||||
int tagOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "tag"));
|
||||
|
||||
WasmExpression tagExpression = new WasmGetLocal(subtypeVar);
|
||||
tagExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, tagExpression,
|
||||
new WasmInt32Constant(tagOffset));
|
||||
tagExpression = new WasmLoadInt32(4, tagExpression, WasmInt32Subtype.INT32);
|
||||
body.add(new WasmSetLocal(subtypeVar, tagExpression));
|
||||
|
||||
Collections.sort(ranges, Comparator.comparingInt(range -> range.lower));
|
||||
|
||||
int lower = ranges.get(0).lower;
|
||||
int upper = ranges.get(ranges.size() - 1).upper;
|
||||
|
||||
WasmExpression lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower));
|
||||
WasmConditional testLower = new WasmConditional(lowerCondition);
|
||||
testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
body.add(testLower);
|
||||
|
||||
WasmExpression upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper));
|
||||
WasmConditional testUpper = new WasmConditional(upperCondition);
|
||||
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
body.add(testUpper);
|
||||
|
||||
for (int i = 1; i < ranges.size(); ++i) {
|
||||
int lowerHole = ranges.get(i - 1).upper;
|
||||
int upperHole = ranges.get(i).lower;
|
||||
|
||||
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole));
|
||||
testLower = new WasmConditional(lowerCondition);
|
||||
body.add(testLower);
|
||||
|
||||
upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upperHole));
|
||||
testUpper = new WasmConditional(upperCondition);
|
||||
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
|
||||
testLower.getThenBlock().getBody().add(testUpper);
|
||||
}
|
||||
|
||||
body.add(new WasmReturn(new WasmInt32Constant(1)));
|
||||
}
|
||||
|
||||
private void generateIsArray(WasmLocal subtypeVar, WasmClassGenerator classGenerator, ValueType itemType,
|
||||
List<WasmExpression> body) {
|
||||
int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType"));
|
||||
|
||||
WasmExpression itemExpression = new WasmGetLocal(subtypeVar);
|
||||
itemExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, itemExpression,
|
||||
new WasmInt32Constant(itemOffset));
|
||||
itemExpression = new WasmLoadInt32(4, itemExpression, WasmInt32Subtype.INT32);
|
||||
body.add(new WasmSetLocal(subtypeVar, itemExpression));
|
||||
|
||||
WasmExpression itemCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(0));
|
||||
WasmConditional itemTest = new WasmConditional(itemCondition);
|
||||
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
|
||||
|
||||
WasmCall delegateToItem = new WasmCall(WasmMangling.mangeIsSupertype(itemType));
|
||||
delegateToItem.getArguments().add(new WasmGetLocal(subtypeVar));
|
||||
itemTest.getElseBlock().getBody().add(delegateToItem);
|
||||
|
||||
body.add(new WasmReturn(itemTest));
|
||||
}
|
||||
|
||||
private WasmFunction generateStub(MethodHolder method, MethodHolder implementor) {
|
||||
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(method.getReference()));
|
||||
if (!method.hasModifier(ElementModifier.STATIC)) {
|
||||
|
@ -363,7 +471,11 @@ public class WasmTarget implements TeaVMTarget {
|
|||
|
||||
private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator,
|
||||
WasmModule module) {
|
||||
for (String className : classes.getClassNames()) {
|
||||
for (ValueType type : classGenerator.getRegisteredClasses()) {
|
||||
if (!(type instanceof ValueType.Object)) {
|
||||
continue;
|
||||
}
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
if (classGenerator.isStructure(className)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.backend.wasm.generate;
|
|||
import com.carrotsearch.hppc.ObjectIntMap;
|
||||
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
@ -53,6 +54,7 @@ public class WasmClassGenerator {
|
|||
private List<String> functionTable = new ArrayList<>();
|
||||
private VirtualTableProvider vtableProvider;
|
||||
private TagRegistry tagRegistry;
|
||||
private boolean isSubtypeGenerated;
|
||||
private DataStructure objectStructure = new DataStructure((byte) 0,
|
||||
DataPrimitives.INT, /* class */
|
||||
DataPrimitives.ADDRESS /* monitor/hash code */);
|
||||
|
@ -105,10 +107,10 @@ public class WasmClassGenerator {
|
|||
break;
|
||||
}
|
||||
|
||||
binaryData.data = createPrimitiveClassData(size);
|
||||
binaryData.data = createPrimitiveClassData(size, type);
|
||||
binaryData.start = binaryWriter.append(binaryData.data);
|
||||
} else if (type == ValueType.VOID) {
|
||||
binaryData.data = createPrimitiveClassData(0);
|
||||
binaryData.data = createPrimitiveClassData(0, type);
|
||||
binaryData.start = binaryWriter.append(binaryData.data);
|
||||
} else if (type instanceof ValueType.Object) {
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
|
@ -136,16 +138,20 @@ public class WasmClassGenerator {
|
|||
binaryData.data = wrapper.getValue(0);
|
||||
binaryData.data.setInt(1, 4);
|
||||
binaryData.data.setAddress(5, itemBinaryData.start);
|
||||
binaryData.data.setInt(7, functionTable.size());
|
||||
functionTable.add(WasmMangling.mangeIsSupertype(type));
|
||||
binaryData.start = binaryWriter.append(vtableSize > 0 ? wrapper : binaryData.data);
|
||||
|
||||
itemBinaryData.data.setAddress(6, binaryData.start);
|
||||
}
|
||||
}
|
||||
|
||||
private DataValue createPrimitiveClassData(int size) {
|
||||
private DataValue createPrimitiveClassData(int size, ValueType type) {
|
||||
DataValue value = classStructure.createValue();
|
||||
value.setInt(1, size);
|
||||
value.setInt(2, RuntimeClass.PRIMITIVE);
|
||||
value.setInt(7, functionTable.size());
|
||||
functionTable.add(WasmMangling.mangeIsSupertype(type));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -170,6 +176,8 @@ public class WasmClassGenerator {
|
|||
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
||||
header.setInt(3, tag);
|
||||
header.setInt(4, RuntimeClass.computeCanary(binaryData.size, tag));
|
||||
header.setInt(7, functionTable.size());
|
||||
functionTable.add(WasmMangling.mangeIsSupertype(ValueType.object(name)));
|
||||
|
||||
if (vtable != null) {
|
||||
fillVirtualTable(vtable, array);
|
||||
|
@ -196,6 +204,10 @@ public class WasmClassGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
public Collection<ValueType> getRegisteredClasses() {
|
||||
return binaryDataMap.keySet();
|
||||
}
|
||||
|
||||
public int getClassPointer(ValueType type) {
|
||||
addClass(type);
|
||||
ClassBinaryData data = binaryDataMap.get(type);
|
||||
|
|
|
@ -24,6 +24,10 @@ public final class WasmMangling {
|
|||
private WasmMangling() {
|
||||
}
|
||||
|
||||
public static String mangeIsSupertype(ValueType type) {
|
||||
return "isSupertype$" + mangleType(type);
|
||||
}
|
||||
|
||||
public static String mangleMethod(MethodReference method) {
|
||||
String className = method.getClassName().length() + mangleString(method.getClassName());
|
||||
StringBuilder sb = new StringBuilder("method$" + className + "_");
|
||||
|
|
|
@ -64,6 +64,7 @@ public class WasmCRenderer {
|
|||
|
||||
renderFunctionDeclarations(module);
|
||||
line("static int8_t *wasm_heap;");
|
||||
line("static int32_t wasm_heap_size;");
|
||||
renderFunctionTable(module);
|
||||
|
||||
for (WasmFunction function : module.getFunctions().values()) {
|
||||
|
@ -91,7 +92,8 @@ public class WasmCRenderer {
|
|||
}
|
||||
|
||||
private void renderHeap(WasmModule module) {
|
||||
line("wasm_heap = malloc(" + 65535 * module.getMemorySize() + ");");
|
||||
line("wasm_heap_size = " + 65536 * module.getMemorySize() + ";");
|
||||
line("wasm_heap = malloc(" + 65536 * module.getMemorySize() + ");");
|
||||
for (WasmMemorySegment segment : module.getSegments()) {
|
||||
line("memcpy(wasm_heap + " + segment.getOffset() + ",");
|
||||
indent();
|
||||
|
|
Loading…
Reference in New Issue
Block a user