Add support for Class.isAssignableFrom

This commit is contained in:
Alexey Andreev 2016-09-01 11:59:56 +03:00
parent bb4040af23
commit 26749150f6
5 changed files with 200 additions and 59 deletions

View File

@ -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:

View File

@ -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;
}

View File

@ -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);

View File

@ -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 + "_");

View File

@ -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();