From 26749150f6d707690e98002b0b4609dd6acc5ea5 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 1 Sep 2016 11:59:56 +0300 Subject: [PATCH] Add support for Class.isAssignableFrom --- .../java/org/teavm/backend/wasm/Example.java | 11 + .../org/teavm/backend/wasm/WasmTarget.java | 222 +++++++++++++----- .../wasm/generate/WasmClassGenerator.java | 18 +- .../backend/wasm/generate/WasmMangling.java | 4 + .../backend/wasm/render/WasmCRenderer.java | 4 +- 5 files changed, 200 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 5583d9d64..59124d4c6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -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: diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 5c67eafd1..6d8f4f1d3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -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 body) { + List 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 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; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 5d1d9b650..8ac87f8bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -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 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 getRegisteredClasses() { + return binaryDataMap.keySet(); + } + public int getClassPointer(ValueType type) { addClass(type); ClassBinaryData data = binaryDataMap.get(type); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmMangling.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmMangling.java index 26dff00f9..74717feb1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmMangling.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmMangling.java @@ -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 + "_"); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 4ac54e2c2..22f99ee18 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -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();