From a84c5fc77fa4a85616fd8f0cc0d2f5b29eaee1e1 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 13 Aug 2024 19:57:48 +0200 Subject: [PATCH] wasm gc: fix issues related to virtual calls --- .../java/org/teavm/backend/c/CTarget.java | 2 +- .../org/teavm/backend/wasm/WasmTarget.java | 2 +- .../org/teavm/backend/wasm/gc/WasmGCUtil.java | 6 +- .../wasm/generate/WasmGenerationVisitor.java | 8 +-- .../methods/BaseWasmGenerationVisitor.java | 30 +++++++--- .../gc/WasmGCDeclarationsGenerator.java | 2 +- .../gc/classes/WasmGCClassGenerator.java | 41 +++++++------- .../gc/classes/WasmGCClassInfoProvider.java | 3 +- .../gc/methods/WasmGCGenerationContext.java | 45 ++++++++++++++- .../gc/methods/WasmGCGenerationVisitor.java | 56 +++++++++++++------ .../gc/methods/WasmGCMethodGenerator.java | 6 +- .../model/classes/VirtualTableBuilder.java | 27 ++++----- 12 files changed, 150 insertions(+), 78 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 22da2f497..c0c8c6aa9 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -617,7 +617,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { VirtualTableBuilder builder = new VirtualTableBuilder(classes); - builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, true)); builder.setMethodCalledVirtually(controller::isVirtual); return builder.build(); } 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 61dc1d1e8..57d633c03 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -1070,7 +1070,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) { var builder = new VirtualTableBuilder(classes); - builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, true)); builder.setMethodCalledVirtually(controller::isVirtual); return builder.build(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCUtil.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCUtil.java index ac01761b7..dfbee2dea 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCUtil.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCUtil.java @@ -34,12 +34,12 @@ public final class WasmGCUtil { if (firstPath.get(0) != secondPath.get(0)) { return "java.lang.Object"; } - var max = Math.max(firstPath.size(), secondPath.size()); + var min = Math.min(firstPath.size(), secondPath.size()); var index = 1; - while (index < max && firstPath.get(index) == secondPath.get(index)) { + while (index < min && firstPath.get(index) == secondPath.get(index)) { ++index; } - return firstPath.get(index).getName(); + return index < firstPath.size() ? firstPath.get(index).getName() : secondPath.get(index).getName(); } private static List findPathToRoot(ClassHierarchy hierarchy, ClassReader cls) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 172367757..c4de914d7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -29,7 +29,6 @@ import org.teavm.ast.QualificationExpr; import org.teavm.ast.Statement; import org.teavm.ast.SubscriptExpr; import org.teavm.ast.TryCatchStatement; -import org.teavm.ast.UnwrapArrayExpr; import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmHeap; @@ -299,7 +298,7 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected WasmExpression nullLiteral() { + protected WasmExpression nullLiteral(Expr expr) { return new WasmInt32Constant(0); } @@ -371,11 +370,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, index); } - @Override - public void visit(UnwrapArrayExpr expr) { - accept(expr.getArray()); - } - @Override protected WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { if (expr.getMethod().getClassName().equals(ShadowStack.class.getName())) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 780f2d2a9..20dab9800 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -60,6 +60,7 @@ import org.teavm.ast.SwitchStatement; import org.teavm.ast.ThrowStatement; import org.teavm.ast.TryCatchStatement; import org.teavm.ast.UnaryExpr; +import org.teavm.ast.UnwrapArrayExpr; import org.teavm.ast.VariableExpr; import org.teavm.ast.WhileStatement; import org.teavm.backend.wasm.WasmRuntime; @@ -453,7 +454,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp accept(value); result.acceptVisitor(typeInference); block.setType(typeInference.getResult()); - var cachedValue = exprCache.create(result, WasmType.INT32, location, block.getBody()); + var cachedValue = exprCache.create(result, typeInference.getResult(), location, block.getBody()); var check = new WasmBranch(cachedValue.expr(), block); check.setResult(cachedValue.expr()); @@ -555,7 +556,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp @Override public void visit(ConstantExpr expr) { if (expr.getValue() == null) { - result = nullLiteral(); + result = nullLiteral(expr); } else if (expr.getValue() instanceof Integer) { result = new WasmInt32Constant((Integer) expr.getValue()); } else if (expr.getValue() instanceof Long) { @@ -574,7 +575,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp result.setLocation(expr.getLocation()); } - protected abstract WasmExpression nullLiteral(); + protected abstract WasmExpression nullLiteral(Expr expr); protected abstract WasmExpression stringLiteral(String s); @@ -894,12 +895,14 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp return block; } else { var reference = expr.getMethod(); - acceptWithType(expr.getArguments().get(0), ValueType.object(expr.getMethod().getClassName())); + var instanceType = ValueType.object(expr.getMethod().getClassName()); + acceptWithType(expr.getArguments().get(0), instanceType); + var instanceWasmType = mapType(instanceType); var instance = result; var block = new WasmBlock(false); block.setType(mapType(reference.getReturnType())); - var instanceVar = tempVars.acquire(WasmType.INT32); + var instanceVar = tempVars.acquire(instanceWasmType); block.getBody().add(new WasmSetLocal(instanceVar, instance)); instance = new WasmGetLocal(instanceVar); @@ -1066,7 +1069,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp for (int i = 0; i < expr.getData().size(); ++i) { expr.getData().get(i).acceptVisitor(this); - block.getBody().add(storeArrayItem(new WasmGetLocal(array), new WasmInt32Constant(i), result, arrayType)); + var arrayData = unwrapArray(new WasmGetLocal(array)); + block.getBody().add(storeArrayItem(arrayData, new WasmInt32Constant(i), result, arrayType)); } block.getBody().add(new WasmGetLocal(array)); @@ -1126,7 +1130,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp block.setType(WasmType.INT32); block.setLocation(expr.getLocation()); - var cachedObject = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); + result.acceptVisitor(typeInference); + var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody()); var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); ifNull.setResult(new WasmInt32Constant(0)); @@ -1545,6 +1550,17 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected abstract WasmType mapType(ValueType type); + protected WasmExpression unwrapArray(WasmExpression array) { + return array; + } + + @Override + public void visit(UnwrapArrayExpr expr) { + accept(expr.getArray()); + result = unwrapArray(result); + result.setLocation(expr.getLocation()); + } + protected abstract class CallSiteIdentifier { public abstract void generateRegister(List consumer, TextLocation location); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java index ad777268c..4d8d6463c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java @@ -140,7 +140,7 @@ public class WasmGCDeclarationsGenerator { private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes, Predicate virtualMethods) { var builder = new VirtualTableBuilder(classes); - builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes)); + builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false)); builder.setMethodCalledVirtually(virtualMethods); return builder.build(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index c643d9844..7e7820203 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -81,7 +81,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private Map classInfoMap = new LinkedHashMap<>(); private Queue classInfoQueue = new ArrayDeque<>(); private ObjectIntMap fieldIndexes = new ObjectIntHashMap<>(); - private ObjectIntMap methodIndexes = new ObjectIntHashMap<>(); private Map staticFieldLocations = new HashMap<>(); private List> staticFieldInitializers = new ArrayList<>(); private ClassInitializerInfo classInitializerInfo; @@ -195,8 +194,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null); if (name != null) { var classReader = classSource.get(name); - if (classReader == null || !classReader.hasModifier(ElementModifier.ABSTRACT) - && !classReader.hasModifier(ElementModifier.INTERFACE)) { + if (classReader == null || !classReader.hasModifier(ElementModifier.INTERFACE)) { virtualTable = virtualTables.lookup(name); } if (classReader != null && classReader.getParent() != null) { @@ -237,6 +235,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classArrayItemOffset; } + @Override + public int getVirtualMethodsOffset() { + return virtualTableFieldOffset; + } + private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) { classInfo.initializer = target -> { int kind; @@ -327,6 +330,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, VirtualTable virtualTable, int index, String origin, Set filled) { + if (virtualTable.getParent() != null) { + index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, + filled); + } for (var method : virtualTable.getMethods()) { var entry = virtualTable.getEntry(method); if (entry != null && entry.getImplementor() != null && filled.add(method)) { @@ -337,24 +344,21 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit module.functions.add(wrapperFunction); var call = new WasmCall(function); var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType()); - wrapperFunction.getLocalVariables().add(instanceParam); + wrapperFunction.add(instanceParam); var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType(); call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget)); var params = new WasmLocal[method.parameterCount()]; for (var i = 0; i < method.parameterCount(); ++i) { params[i] = new WasmLocal(typeMapper.mapType(method.parameterType(i))); call.getArguments().add(new WasmGetLocal(params[i])); + wrapperFunction.add(params[i]); } - wrapperFunction.getLocalVariables().addAll(List.of(params)); } function.setReferenced(true); var ref = new WasmFunctionReference(function); target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref)); } - } - if (virtualTable.getParent() != null) { - index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, - filled); + ++index; } return index; } @@ -376,10 +380,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit addVirtualTableFields(structure, virtualTable.getParent()); } for (var methodDesc : virtualTable.getMethods()) { - var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); - var methodRef = new MethodReference(virtualTable.getClassName(), methodDesc); - methodIndexes.put(methodRef, structure.getFields().size()); - structure.getFields().add(functionType.getReference().asStorage()); + if (methodDesc == null) { + structure.getFields().add(WasmType.Reference.FUNC.asStorage()); + } else { + var functionType = getFunctionType(virtualTable.getClassName(), methodDesc); + structure.getFields().add(functionType.getReference().asStorage()); + } } } @@ -454,15 +460,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return global; } - @Override - public int getVirtualMethodIndex(MethodReference methodRef) { - var result = methodIndexes.getOrDefault(methodRef, -1); - if (result < 0) { - throw new IllegalStateException("Can't get offset of method " + methodRef); - } - return result; - } - private void fillFields(WasmGCClassInfo classInfo, ValueType type) { var fields = classInfo.structure.getFields(); fields.add(standardClasses.classClass().getType().asStorage()); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java index ce34f79e3..411cab08c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -17,7 +17,6 @@ package org.teavm.backend.wasm.generate.gc.classes; import org.teavm.backend.wasm.model.WasmGlobal; import org.teavm.model.FieldReference; -import org.teavm.model.MethodReference; import org.teavm.model.ValueType; public interface WasmGCClassInfoProvider { @@ -32,7 +31,7 @@ public interface WasmGCClassInfoProvider { WasmGlobal getStaticFieldLocation(FieldReference fieldRef); - int getVirtualMethodIndex(MethodReference methodRef); + int getVirtualMethodsOffset(); default WasmGCClassInfo getClassInfo(String name) { return getClassInfo(ValueType.object(name)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java index 8d0d6e1d2..871374562 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java @@ -15,6 +15,12 @@ */ package org.teavm.backend.wasm.generate.gc.methods; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.WasmGCMethodReturnTypes; @@ -32,6 +38,8 @@ import org.teavm.backend.wasm.model.expression.WasmNullConstant; import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.classes.VirtualTableProvider; @@ -43,7 +51,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private VirtualTableProvider virtualTables; private WasmGCTypeMapper typeMapper; private WasmFunctionTypes functionTypes; - private ClassReaderSource classes; + private ListableClassReaderSource classes; private ClassHierarchy hierarchy; private BaseWasmFunctionRepository functions; private WasmGCSupertypeFunctionProvider supertypeFunctions; @@ -55,9 +63,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private WasmFunction cceMethod; private WasmGlobal exceptionGlobal; private WasmTag exceptionTag; + private Map> interfaceImplementors; public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables, - WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes, + WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes, ClassHierarchy hierarchy, BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, @@ -180,4 +189,36 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { public WasmGCMethodReturnTypes returnTypes() { return returnTypes; } + + public Collection getInterfaceImplementors(String className) { + if (interfaceImplementors == null) { + fillInterfaceImplementors(); + } + var result = interfaceImplementors.get(className); + return result != null ? result : List.of(); + } + + private void fillInterfaceImplementors() { + interfaceImplementors = new HashMap<>(); + for (var className : classes.getClassNames()) { + var cls = classes.get(className); + if (!cls.hasModifier(ElementModifier.INTERFACE)) { + for (var itf : cls.getInterfaces()) { + addInterfaceImplementor(className, itf); + } + } + } + } + + private void addInterfaceImplementor(String implementorName, String interfaceName) { + var implementorsByKey = interfaceImplementors.computeIfAbsent(interfaceName, k -> new LinkedHashSet<>()); + if (implementorsByKey.add(implementorName)) { + var itf = classes.get(implementorName); + if (itf != null) { + for (var parentItf : itf.getInterfaces()) { + addInterfaceImplementor(implementorName, parentItf); + } + } + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 30a006670..6de26a952 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -23,7 +23,6 @@ import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationType; import org.teavm.ast.QualificationExpr; import org.teavm.ast.SubscriptExpr; -import org.teavm.ast.UnwrapArrayExpr; import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.PreciseTypeInference; @@ -63,10 +62,13 @@ import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.model.expression.WasmThrow; import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.model.ClassHierarchy; +import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; +import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; +import org.teavm.model.classes.VirtualTable; public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { private WasmGCGenerationContext context; @@ -136,13 +138,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - public void visit(UnwrapArrayExpr expr) { - accept(expr.getArray()); - result = unwrapArray(result); - result.setLocation(expr.getLocation()); - } - - private WasmExpression unwrapArray(WasmExpression array) { + protected WasmExpression unwrapArray(WasmExpression array) { array.acceptVisitor(typeInference); var arrayType = (WasmType.CompositeReference) typeInference.getResult(); var arrayStruct = (WasmStructure) arrayType.composite; @@ -202,9 +198,16 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { } @Override - protected WasmExpression nullLiteral() { - return new WasmNullConstant(expectedType instanceof WasmType.Reference - ? (WasmType.Reference) expectedType + protected WasmExpression nullLiteral(Expr expr) { + var type = expectedType; + if (expr.getVariableIndex() >= 0) { + var javaType = types.typeOf(expr.getVariableIndex()); + if (javaType != null) { + type = mapType(javaType.valueType); + } + } + return new WasmNullConstant(type instanceof WasmType.Reference + ? (WasmType.Reference) type : WasmType.Reference.STRUCT); } @@ -252,15 +255,22 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { if (vtable == null) { return new WasmUnreachable(); } - method = new MethodReference(vtable.getClassName(), method.getDescriptor()); + var cls = context.classes().get(method.getClassName()); + assert cls != null : "Virtual table can't be generated for absent class"; - arguments.get(0).acceptVisitor(typeInference); - var instanceType = (WasmType.CompositeReference) typeInference.getResult(); - var instanceStruct = (WasmStructure) instanceType.composite; + if (cls.hasModifier(ElementModifier.INTERFACE)) { + vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor()); + } + + int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor()); + if (vtable.getParent() != null) { + vtableIndex += vtable.getParent().size(); + } + var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure(); WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); - var index = context.classInfoProvider().getVirtualMethodIndex(method); + var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex; var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()) .getVirtualTableStructure(); classRef = new WasmCast(classRef, vtableStruct.getReference()); @@ -272,6 +282,20 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { return invoke; } + private VirtualTable pickVirtualTableForInterfaceCall(VirtualTable virtualTable, MethodDescriptor descriptor) { + var implementors = context.getInterfaceImplementors(virtualTable.getClassName()); + for (var implementor : implementors) { + var implementorVtable = context.virtualTables().lookup(implementor); + if (implementorVtable != null && implementorVtable.hasMethod(descriptor)) { + while (implementorVtable.getParent() != null && implementorVtable.getParent().hasMethod(descriptor)) { + implementorVtable = implementorVtable.getParent(); + } + return implementorVtable; + } + } + throw new IllegalStateException(); + } + @Override protected void allocateObject(String className, TextLocation location, WasmLocal local, List target) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 32aabdd1e..08c0a4943 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -45,8 +45,8 @@ import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Import; import org.teavm.model.CallLocation; import org.teavm.model.ClassHierarchy; -import org.teavm.model.ClassHolderSource; import org.teavm.model.ElementModifier; +import org.teavm.model.ListableClassHolderSource; import org.teavm.model.MethodHolder; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; @@ -58,7 +58,7 @@ import org.teavm.model.util.RegisterAllocator; public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { private WasmModule module; private ClassHierarchy hierarchy; - private ClassHolderSource classes; + private ListableClassHolderSource classes; private VirtualTableProvider virtualTables; private ClassInitializerInfo classInitInfo; private WasmFunctionTypes functionTypes; @@ -83,7 +83,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { public WasmGCMethodGenerator( WasmModule module, ClassHierarchy hierarchy, - ClassHolderSource classes, + ListableClassHolderSource classes, VirtualTableProvider virtualTables, ClassInitializerInfo classInitInfo, WasmFunctionTypes functionTypes, diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java index 09f197332..8ab98d3cc 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTableBuilder.java @@ -127,7 +127,7 @@ public class VirtualTableBuilder { } } - List methodsAtCallSites = methodsUsedAtCallSites.get(className); + var methodsAtCallSites = methodsUsedAtCallSites.get(className); if (methodsAtCallSites != null) { for (MethodDescriptor methodDesc : methodsAtCallSites) { if (cls.hasModifier(ElementModifier.FINAL) && !table.entries.containsKey(methodDesc)) { @@ -162,7 +162,7 @@ public class VirtualTableBuilder { } private void copyEntries(TableBuilder source, TableBuilder target) { - for (Map.Entry entry : source.entries.entrySet()) { + for (var entry : source.entries.entrySet()) { EntryBuilder targetEntry = target.entries.computeIfAbsent(entry.getKey(), k -> new EntryBuilder()); targetEntry.addParent(entry.getValue()); if (entry.getValue().implementor != null && targetEntry.implementor == null) { @@ -197,7 +197,7 @@ public class VirtualTableBuilder { private void liftEntries() { buildClassTree(); - for (Map.Entry> group : groupMethods().entrySet()) { + for (var group : groupMethods().entrySet()) { String commonSuperclass = commonSuperclass(group.getValue()); Set visited = new HashSet<>(); for (String cls : group.getValue()) { @@ -397,17 +397,16 @@ public class VirtualTableBuilder { } } - List newMethods = context.methods.subList(methodsStart, context.methods.size()); + var newMethods = context.methods.subList(methodsStart, context.methods.size()); Set methodSet = new HashSet<>(); for (MethodDescriptor method : newMethods) { if (method != null) { methodSet.add(method); } } - List readonlyNewMethods = Collections.unmodifiableList( - Arrays.asList(newMethods.toArray(new MethodDescriptor[0]))); - VirtualTable resultTable = new VirtualTable(className, parent, readonlyNewMethods, - methodSet, resultEntries); + var readonlyNewMethods = Collections.unmodifiableList(Arrays.asList( + newMethods.toArray(new MethodDescriptor[0]))); + var resultTable = new VirtualTable(className, parent, readonlyNewMethods, methodSet, resultEntries); result.virtualTables.put(className, resultTable); List children = classChildren.get(className); @@ -489,10 +488,9 @@ public class VirtualTableBuilder { methodSet.add(method); } - List readonlyNewMethods = Collections.unmodifiableList( + var readonlyMethods = Collections.unmodifiableList( Arrays.asList(methods.toArray(new MethodDescriptor[0]))); - VirtualTable resultTable = new VirtualTable(className, null, readonlyNewMethods, - methodSet, Collections.emptyMap()); + var resultTable = new VirtualTable(className, null, readonlyMethods, methodSet, Map.of()); result.virtualTables.put(className, resultTable); } } @@ -523,7 +521,8 @@ public class VirtualTableBuilder { List methods = new ArrayList<>(); } - public static Set getMethodsUsedOnCallSites(ListableClassHolderSource classes) { + public static Set getMethodsUsedOnCallSites(ListableClassHolderSource classes, + boolean withCloneArray) { var virtualMethods = new HashSet(); for (var className : classes.getClassNames()) { @@ -542,7 +541,9 @@ public class VirtualTableBuilder { virtualMethods.add(invoke.getMethod()); } } else if (insn instanceof CloneArrayInstruction) { - virtualMethods.add(new MethodReference(Object.class, "clone", Object.class)); + if (withCloneArray) { + virtualMethods.add(new MethodReference(Object.class, "clone", Object.class)); + } } } }