wasm gc: implement backend-specific virtual table builder

This commit is contained in:
Alexey Andreev 2024-08-20 21:03:51 +02:00
parent eb0eb1f146
commit 1aebe51256
12 changed files with 436 additions and 90 deletions

View File

@ -118,7 +118,6 @@ public class WasmGCTarget implements TeaVMTarget {
var declarationsGenerator = new WasmGCDeclarationsGenerator(
module,
classes,
controller::isVirtual,
controller.getClassInitializerInfo(),
controller.getDependencyInfo(),
controller.getDiagnostics(),

View File

@ -0,0 +1,61 @@
/*
* Copyright 2024 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.backend.wasm.gc.vtable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class WasmGCVirtualTable {
private WasmGCVirtualTable parent;
private String className;
List<WasmGCVirtualTableEntry> entries;
MethodReference[] implementors;
private Map<MethodDescriptor, WasmGCVirtualTableEntry> entryMap;
WasmGCVirtualTable(WasmGCVirtualTable parent, String className) {
this.parent = parent;
this.className = className;
}
public WasmGCVirtualTable getParent() {
return parent;
}
public String getClassName() {
return className;
}
public List<? extends WasmGCVirtualTableEntry> getEntries() {
return entries;
}
public MethodReference implementor(WasmGCVirtualTableEntry entry) {
return implementors[entry.getIndex()];
}
public WasmGCVirtualTableEntry entry(MethodDescriptor method) {
if (entryMap == null) {
entryMap = new HashMap<>();
for (var entry : entries) {
entryMap.put(entry.getMethod(), entry);
}
}
return entryMap.get(method);
}
}

View File

@ -0,0 +1,243 @@
/*
* Copyright 2024 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.backend.wasm.gc.vtable;
import com.carrotsearch.hppc.ObjectIntHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.common.LCATree;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
class WasmGCVirtualTableBuilder {
ListableClassReaderSource classes;
Collection<MethodReference> methodsAtCallSites;
private Map<String, Set<MethodDescriptor>> groupedMethodsAtCallSites = new HashMap<>();
private List<Table> tables = new ArrayList<>();
private Map<String, Table> tableMap = new HashMap<>();
private LCATree lcaTree;
private Map<String, Table> interfaceImplementors = new HashMap<>();
Map<String, WasmGCVirtualTable> result = new HashMap<>();
void build() {
initTables();
buildLCA();
fillInterfaceImplementors();
groupMethodsFromCallSites();
fillTables();
buildResult();
}
private void initTables() {
for (var className : classes.getClassNames()) {
initTable(className);
}
}
private void initTable(String className) {
var cls = classes.get(className);
if (!cls.hasModifier(ElementModifier.INTERFACE) && !tableMap.containsKey(className)) {
if (cls.getParent() != null) {
initTable(cls.getParent());
}
var table = new Table(cls, tables.size());
tables.add(table);
tableMap.put(className, table);
}
}
private void buildLCA() {
lcaTree = new LCATree(tables.size() + 1);
for (var i = 0; i < tables.size(); i++) {
var table = tables.get(i);
var parentTable = table.cls.getParent() != null ? tableMap.get(table.cls.getParent()) : null;
lcaTree.addNode(parentTable != null ? parentTable.index + 1 : 0);
}
}
private void fillInterfaceImplementors() {
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
if (!cls.hasModifier(ElementModifier.INTERFACE)) {
var visited = new HashSet<String>();
do {
var table = tableMap.get(cls.getName());
for (var itfName : cls.getInterfaces()) {
addImplementorToInterface(itfName, table, visited);
}
cls = cls.getParent() != null ? classes.get(cls.getParent()) : null;
} while (cls != null);
}
}
}
private void addImplementorToInterface(String interfaceName, Table newImplementor, Set<String> visited) {
if (!visited.add(interfaceName)) {
return;
}
var knownImplementor = interfaceImplementors.get(interfaceName);
if (knownImplementor == null) {
interfaceImplementors.put(interfaceName, newImplementor);
} else {
var lcaIndex = lcaTree.lcaOf(newImplementor.index + 1, knownImplementor.index + 1);
if (lcaIndex > 0) {
interfaceImplementors.put(interfaceName, tables.get(lcaIndex - 1));
}
}
var cls = classes.get(interfaceName);
if (cls != null) {
for (var superInterface : cls.getInterfaces()) {
addImplementorToInterface(superInterface, newImplementor, visited);
}
}
}
private void groupMethodsFromCallSites() {
for (var methodRef : methodsAtCallSites) {
var className = mapInterface(methodRef.getClassName());
var group = groupedMethodsAtCallSites.computeIfAbsent(className, k -> new LinkedHashSet<>());
group.add(methodRef.getDescriptor());
}
}
private String mapInterface(String name) {
var cls = classes.get(name);
if (cls == null || !cls.hasModifier(ElementModifier.INTERFACE)) {
return name;
}
var implementor = interfaceImplementors.get(cls.getName());
if (implementor == null) {
return name;
}
return implementor.cls.getName();
}
private void fillTables() {
for (var className : classes.getClassNames()) {
var table = tableMap.get(className);
if (table != null) {
fillTable(table);
}
}
}
private void fillTable(Table table) {
if (table.filled) {
return;
}
table.filled = true;
var parent = table.cls.getParent() != null ? tableMap.get(table.cls.getParent()) : null;
table.parent = parent;
var indexes = new ObjectIntHashMap<MethodDescriptor>();
if (parent != null) {
fillTable(parent);
table.entries.addAll(parent.entries);
table.implementors.addAll(parent.implementors);
for (var entry : table.entries) {
indexes.put(entry.method, entry.index);
}
}
var group = groupedMethodsAtCallSites.get(table.cls.getName());
if (group != null) {
for (var method : group) {
if (indexes.getOrDefault(method, -1) < 0) {
var entry = new Entry(method, table, table.entries.size());
table.entries.add(entry);
indexes.put(method, entry.index);
table.implementors.add(null);
}
}
}
for (var method : table.cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC) && !method.hasModifier(ElementModifier.ABSTRACT)) {
var index = indexes.getOrDefault(method.getDescriptor(), -1);
if (index >= 0) {
table.implementors.set(index, method.getReference());
}
}
}
}
private void buildResult() {
for (var className : classes.getClassNames()) {
var cls = classes.get(className);
var table = !cls.hasModifier(ElementModifier.INTERFACE)
? tableMap.get(className)
: interfaceImplementors.get(className);
if (table != null) {
result.put(className, table.getBuildResult());
}
}
}
private static class Table {
final ClassReader cls;
int index;
boolean filled;
Table parent;
List<Entry> entries = new ArrayList<>();
List<MethodReference> implementors = new ArrayList<>();
private WasmGCVirtualTable buildResult;
Table(ClassReader cls, int index) {
this.cls = cls;
this.index = index;
}
WasmGCVirtualTable getBuildResult() {
if (buildResult == null) {
buildResult = new WasmGCVirtualTable(parent != null ? parent.getBuildResult() : null, cls.getName());
buildResult.entries = entries.stream()
.map(Entry::getBuildResult)
.collect(Collectors.toList());
buildResult.implementors = implementors.toArray(new MethodReference[0]);
}
return buildResult;
}
}
private static class Entry {
MethodDescriptor method;
Table origin;
int index;
private WasmGCVirtualTableEntry buildResult;
Entry(MethodDescriptor method, Table origin, int index) {
this.method = method;
this.origin = origin;
this.index = index;
}
WasmGCVirtualTableEntry getBuildResult() {
if (buildResult == null) {
buildResult = new WasmGCVirtualTableEntry(origin.getBuildResult(), method, index);
}
return buildResult;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2024 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.backend.wasm.gc.vtable;
import org.teavm.model.MethodDescriptor;
public class WasmGCVirtualTableEntry {
private WasmGCVirtualTable origin;
private MethodDescriptor method;
private int index;
WasmGCVirtualTableEntry(WasmGCVirtualTable origin, MethodDescriptor method, int index) {
this.origin = origin;
this.method = method;
this.index = index;
}
public WasmGCVirtualTable getOrigin() {
return origin;
}
public MethodDescriptor getMethod() {
return method;
}
public int getIndex() {
return index;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2024 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.backend.wasm.gc.vtable;
import java.util.Collection;
import java.util.Map;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference;
public class WasmGCVirtualTableProvider {
private Map<String, WasmGCVirtualTable> virtualTables;
public WasmGCVirtualTableProvider(ListableClassReaderSource classes,
Collection<MethodReference> methodsAtCallSites) {
var builder = new WasmGCVirtualTableBuilder();
builder.classes = classes;
builder.methodsAtCallSites = methodsAtCallSites;
builder.build();
virtualTables = builder.result;
}
public WasmGCVirtualTable lookup(String name) {
return virtualTables.get(name);
}
}

View File

@ -16,9 +16,9 @@
package org.teavm.backend.wasm.generate.gc;
import java.util.List;
import java.util.function.Predicate;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.WasmNameProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassGenerator;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
@ -33,12 +33,10 @@ import org.teavm.dependency.DependencyInfo;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.MethodReference;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTableBuilder;
import org.teavm.model.classes.VirtualTableProvider;
public class WasmGCDeclarationsGenerator {
public final ClassHierarchy hierarchy;
@ -50,7 +48,6 @@ public class WasmGCDeclarationsGenerator {
public WasmGCDeclarationsGenerator(
WasmModule module,
ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods,
ClassInitializerInfo classInitializerInfo,
DependencyInfo dependencyInfo,
Diagnostics diagnostics,
@ -59,7 +56,7 @@ public class WasmGCDeclarationsGenerator {
) {
this.module = module;
hierarchy = new ClassHierarchy(classes);
var virtualTables = createVirtualTableProvider(classes, virtualMethods);
var virtualTables = createVirtualTableProvider(classes);
functionTypes = new WasmFunctionTypes(module);
var names = new WasmNameProvider();
methodGenerator = new WasmGCMethodGenerator(
@ -133,12 +130,8 @@ public class WasmGCDeclarationsGenerator {
}
}
private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes,
Predicate<MethodReference> virtualMethods) {
var builder = new VirtualTableBuilder(classes);
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false));
builder.setMethodCalledVirtually(virtualMethods);
return builder.build();
private static WasmGCVirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
return new WasmGCVirtualTableProvider(classes, VirtualTableBuilder.getMethodsUsedOnCallSites(classes, false));
}
public WasmFunction dummyInitializer() {

View File

@ -20,16 +20,16 @@ import com.carrotsearch.hppc.ObjectIntMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.function.Consumer;
import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTable;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray;
@ -66,8 +66,6 @@ import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.analysis.ClassMetadataRequirements;
import org.teavm.model.classes.TagRegistry;
import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.ReflectionUtil;
public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInitializerContributor {
@ -82,7 +80,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunctionTypes functionTypes;
private TagRegistry tagRegistry;
private ClassMetadataRequirements metadataRequirements;
private VirtualTableProvider virtualTables;
private WasmGCVirtualTableProvider virtualTables;
private BaseWasmFunctionRepository functionProvider;
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>();
@ -113,7 +111,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables,
BaseWasmFunctionRepository functionProvider, NameProvider names,
ClassInitializerInfo classInitializerInfo) {
this.module = module;
@ -236,7 +234,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
classInfo = new WasmGCClassInfo(type);
classInfoQueue.add(classInfo);
classInfoMap.put(type, classInfo);
VirtualTable virtualTable = null;
WasmGCVirtualTable virtualTable = null;
if (!(type instanceof ValueType.Primitive)) {
var name = type instanceof ValueType.Object
? ((ValueType.Object) type).getClassName()
@ -265,7 +263,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
var pointerName = names.forClassInstance(type);
classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries();
classInfo.hasOwnVirtualTable = virtualTable != null && !virtualTable.getEntries().isEmpty();
var classStructure = classInfo.hasOwnVirtualTable
? initRegularClassStructure(((ValueType.Object) type).getClassName())
: standardClasses.classClass().getStructure();
@ -366,8 +364,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
};
}
private void initRegularClass(WasmGCClassInfo classInfo, VirtualTable virtualTable, WasmStructure classStructure,
String name) {
private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virtualTable,
WasmStructure classStructure, String name) {
var cls = classSource.get(name);
if (classInitializerInfo.isDynamicInitializer(name)) {
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
@ -396,39 +394,34 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
}
}
if (virtualTable != null && virtualTable.hasValidEntries()) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTable,
new HashSet<>());
if (virtualTable != null) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable);
}
};
}
private void fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, VirtualTable original, Set<MethodDescriptor> filled) {
if (virtualTable.getParent() != null) {
fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), original, filled);
}
for (var method : virtualTable.getMethods()) {
var entry = original.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method)
&& !method.equals(GET_CLASS_METHOD)) {
WasmGCVirtualTable virtualTable) {
for (var entry : virtualTable.getEntries()) {
var implementor = virtualTable.implementor(entry);
if (implementor != null && !entry.getMethod().equals(GET_CLASS_METHOD)) {
var fieldIndex = virtualTableFieldOffset + entry.getIndex();
var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex)
.getUnpackedType();
var expectedFunctionType = (WasmFunctionType) expectedType.composite;
var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName())
var function = functionProvider.forInstanceMethod(implementor);
if (entry.getOrigin().getClassName().equals(implementor.getClassName())
|| expectedFunctionType != function.getType()) {
var wrapperFunction = new WasmFunction(expectedFunctionType);
module.functions.add(wrapperFunction);
var call = new WasmCall(function);
var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType());
wrapperFunction.add(instanceParam);
var castTarget = getClassInfo(entry.getImplementor().getClassName()).getType();
var castTarget = getClassInfo(implementor.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)));
var params = new WasmLocal[entry.getMethod().parameterCount()];
for (var i = 0; i < entry.getMethod().parameterCount(); ++i) {
params[i] = new WasmLocal(typeMapper.mapType(entry.getMethod().parameterType(i)));
call.getArguments().add(new WasmGetLocal(params[i]));
wrapperFunction.add(params[i]);
}
@ -462,22 +455,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fields.add(monitorField);
}
private void addVirtualTableFields(WasmStructure structure, VirtualTable virtualTable) {
if (virtualTable.getParent() != null) {
addVirtualTableFields(structure, virtualTable.getParent());
}
for (var methodDesc : virtualTable.getMethods()) {
if (methodDesc == null) {
structure.getFields().add(new WasmField(WasmType.Reference.FUNC.asStorage()));
} else {
var originalVirtualTable = virtualTable.findMethodContainer(methodDesc);
var functionType = typeMapper.getFunctionType(originalVirtualTable.getClassName(), methodDesc, false);
private void addVirtualTableFields(WasmStructure structure, WasmGCVirtualTable virtualTable) {
for (var entry : virtualTable.getEntries()) {
var functionType = typeMapper.getFunctionType(entry.getOrigin().getClassName(), entry.getMethod(), false);
var field = new WasmField(functionType.getReference().asStorage());
field.setName(names.forVirtualMethod(methodDesc));
field.setName(names.forVirtualMethod(entry.getMethod()));
structure.getFields().add(field);
}
}
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> {

View File

@ -23,6 +23,7 @@ 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.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
@ -40,14 +41,13 @@ 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;
public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmModule module;
private WasmGCClassInfoProvider classInfoProvider;
private WasmGCStandardClasses standardClasses;
private WasmGCStringProvider strings;
private VirtualTableProvider virtualTables;
private WasmGCVirtualTableProvider virtualTables;
private WasmGCTypeMapper typeMapper;
private WasmFunctionTypes functionTypes;
private ListableClassReaderSource classes;
@ -63,7 +63,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
private WasmTag exceptionTag;
private Map<String, Set<String>> interfaceImplementors;
public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables,
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
ClassHierarchy hierarchy, BaseWasmFunctionRepository functions,
WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider,
@ -96,7 +96,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
return strings;
}
public VirtualTableProvider virtualTables() {
public WasmGCVirtualTableProvider virtualTables() {
return virtualTables;
}

View File

@ -62,13 +62,10 @@ 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;
@ -259,26 +256,18 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
protected WasmExpression generateVirtualCall(WasmLocal instance, MethodReference method,
List<WasmExpression> arguments) {
var vtable = context.virtualTables().lookup(method.getClassName());
if (vtable != null) {
var cls = context.classes().get(method.getClassName());
assert cls != null : "Virtual table can't be generated for absent class";
if (cls.hasModifier(ElementModifier.INTERFACE)) {
vtable = pickVirtualTableForInterfaceCall(vtable, method.getDescriptor());
}
vtable = vtable.findMethodContainer(method.getDescriptor());
}
if (vtable == null) {
return new WasmUnreachable();
}
int vtableIndex = vtable.getMethods().indexOf(method.getDescriptor());
if (vtable.getParent() != null) {
vtableIndex += vtable.getParent().size();
var entry = vtable.entry(method.getDescriptor());
if (entry == null) {
return new WasmUnreachable();
}
WasmExpression classRef = new WasmStructGet(context.standardClasses().objectClass().getStructure(),
new WasmGetLocal(instance), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex;
var index = context.classInfoProvider().getVirtualMethodsOffset() + entry.getIndex();
var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName());
var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure();
classRef = new WasmCast(classRef, vtableStruct.getReference());
@ -298,20 +287,6 @@ 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<WasmExpression> target) {

View File

@ -26,6 +26,7 @@ import org.teavm.backend.lowlevel.generate.NameProvider;
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
@ -51,14 +52,13 @@ import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.analysis.ClassInitializerInfo;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.util.RegisterAllocator;
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
private WasmModule module;
private ClassHierarchy hierarchy;
private ListableClassHolderSource classes;
private VirtualTableProvider virtualTables;
private WasmGCVirtualTableProvider virtualTables;
private ClassInitializerInfo classInitInfo;
private WasmFunctionTypes functionTypes;
private WasmGCSupertypeFunctionProvider supertypeFunctions;
@ -82,7 +82,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
WasmModule module,
ClassHierarchy hierarchy,
ListableClassHolderSource classes,
VirtualTableProvider virtualTables,
WasmGCVirtualTableProvider virtualTables,
ClassInitializerInfo classInitInfo,
WasmFunctionTypes functionTypes,
NameProvider names,

View File

@ -25,7 +25,7 @@ public class LCATree {
pathsToRoot = new int[capacity][];
sz = 1;
depths[0] = 0;
pathsToRoot[0] = new int[0];
pathsToRoot[0] = new int[] { 0 };
}
public int size() {

View File

@ -15,11 +15,21 @@
*/
package org.teavm.junit;
import org.teavm.classlib.impl.console.JSStdoutPrintStream;
final class TestWasmGCEntryPoint {
private TestWasmGCEntryPoint() {
}
public static void main(String[] args) throws Throwable {
try {
TestEntryPoint.run(args.length > 0 ? args[0] : null);
new JSStdoutPrintStream().println("SUCCESS");
} catch (Throwable e) {
var out = new JSStdoutPrintStream();
e.printStackTrace(out);
out.println("FAILURE");
}
TestEntryPoint.run(args.length > 0 ? args[0] : null);
}
}