Add support of virtual method invocation

This commit is contained in:
Alexey Andreev 2016-08-11 13:33:22 +03:00
parent 45993091e4
commit fe5aca5139
20 changed files with 870 additions and 50 deletions

View File

@ -0,0 +1,106 @@
/*
* Copyright 2016 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.model.classes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.ListableClassReaderSource;
public class InterfaceToClassMapping {
private Map<String, String> map = new HashMap<>();
public InterfaceToClassMapping(ListableClassReaderSource classSource) {
for (String className : classSource.getClassNames()) {
ClassReader cls = classSource.get(className);
if (cls.hasModifier(ElementModifier.INTERFACE)) {
continue;
}
map.put(className, className);
for (String iface : getInterfaces(classSource, className)) {
String existing = map.get(iface);
if (existing == null) {
map.put(iface, className);
} else {
map.put(iface, commonSuperClass(classSource, className, existing));
}
}
}
}
private static Set<String> getInterfaces(ClassReaderSource classSource, String className) {
Set<String> interfaces = new HashSet<>();
getInterfaces(classSource, className, interfaces);
return interfaces;
}
private static void getInterfaces(ClassReaderSource classSource, String className, Set<String> interfaces) {
if (!interfaces.add(className)) {
return;
}
ClassReader cls = classSource.get(className);
if (cls == null) {
return;
}
for (String iface : cls.getInterfaces()) {
getInterfaces(classSource, iface, interfaces);
}
}
private static String commonSuperClass(ClassReaderSource classSource, String a, String b) {
if (a.equals(b)) {
return a;
}
List<String> firstPath = pathToRoot(classSource, a);
List<String> secondPath = pathToRoot(classSource, b);
Collections.reverse(firstPath);
Collections.reverse(secondPath);
int min = Math.min(firstPath.size(), secondPath.size());
for (int i = 1; i < min; ++i) {
if (!firstPath.get(i).equals(secondPath.get(i))) {
return firstPath.get(i - 1);
}
}
return a;
}
private static List<String> pathToRoot(ClassReaderSource classSource, String className) {
List<String> path = new ArrayList<>();
while (true) {
path.add(className);
ClassReader cls = classSource.get(className);
if (cls == null || cls.getParent() == null) {
break;
}
className = cls.getParent();
}
return path;
}
public String mapClass(String className) {
return map.get(className);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2016 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.model.classes;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.teavm.model.MethodDescriptor;
public class VirtualTable {
private String className;
Map<MethodDescriptor, VirtualTableEntry> entries = new LinkedHashMap<>();
private Map<MethodDescriptor, VirtualTableEntry> readonlyEntries;
VirtualTable(String className) {
this.className = className;
}
public String getClassName() {
return className;
}
public Map<MethodDescriptor, VirtualTableEntry> getEntries() {
if (readonlyEntries == null) {
readonlyEntries = Collections.unmodifiableMap(entries);
}
return readonlyEntries;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2016 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.model.classes;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
public class VirtualTableEntry {
private VirtualTable virtualTable;
private MethodDescriptor method;
MethodReference implementor;
private int index;
VirtualTableEntry(VirtualTable virtualTable, MethodDescriptor method, MethodReference implementor, int index) {
this.virtualTable = virtualTable;
this.method = method;
this.implementor = implementor;
this.index = index;
}
public VirtualTable getVirtualTable() {
return virtualTable;
}
public MethodDescriptor getMethod() {
return method;
}
public MethodReference getImplementor() {
return implementor;
}
public int getIndex() {
return index;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2016 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.model.classes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
public class VirtualTableProvider {
private ClassReaderSource classSource;
private Map<String, Set<MethodDescriptor>> virtualMethodMap = new HashMap<>();
private Map<String, VirtualTable> virtualTables = new LinkedHashMap<>();
private InterfaceToClassMapping interfaceMapping;
public VirtualTableProvider(ListableClassReaderSource classSource, Set<MethodReference> virtualMethods) {
this.classSource = classSource;
interfaceMapping = new InterfaceToClassMapping(classSource);
for (MethodReference virtualMethod : virtualMethods) {
String cls = interfaceMapping.mapClass(virtualMethod.getClassName());
virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor());
}
for (String className : classSource.getClassNames()) {
fillClass(className);
}
}
private void fillClass(String className) {
if (virtualTables.containsKey(className)) {
return;
}
VirtualTable table = new VirtualTable(className);
virtualTables.put(className, table);
ClassReader cls = classSource.get(className);
if (cls.getParent() != null) {
fillClass(cls.getParent());
VirtualTable parentTable = virtualTables.get(cls.getParent());
for (VirtualTableEntry parentEntry : parentTable.entries.values()) {
VirtualTableEntry entry = new VirtualTableEntry(table, parentEntry.getMethod(),
parentEntry.getImplementor(), parentEntry.getIndex());
table.entries.put(entry.getMethod(), entry);
}
}
Set<MethodDescriptor> newDescriptors = virtualMethodMap.get(className);
if (newDescriptors != null) {
for (MethodDescriptor method : newDescriptors) {
table.entries.put(method, new VirtualTableEntry(table, method, null, table.entries.size()));
}
}
for (MethodReader method : cls.getMethods()) {
VirtualTableEntry entry = table.entries.get(method.getDescriptor());
if (entry != null) {
entry.implementor = method.getReference();
}
}
}
public VirtualTableEntry lookup(MethodReference method) {
VirtualTable vtable = virtualTables.get(interfaceMapping.mapClass(method.getClassName()));
if (vtable == null) {
return null;
}
return vtable.getEntries().get(method.getDescriptor());
}
public VirtualTable lookup(String className) {
return virtualTables.get(interfaceMapping.mapClass(className));
}
}

View File

@ -25,10 +25,6 @@ import org.teavm.model.*;
import org.teavm.model.instructions.*;
import org.teavm.model.util.ModelUtils;
/**
*
* @author Alexey Andreev
*/
public class ClassRefsRenamer implements InstructionVisitor {
private Mapper<String, String> classNameMapper;
@ -49,6 +45,9 @@ public class ClassRefsRenamer implements InstructionVisitor {
}
}
renamedCls.setParent(parent != null ? classNameMapper.map(parent) : null);
if (renamedCls.getName().equals(renamedCls.getParent())) {
renamedCls.setParent(null);
}
for (MethodHolder method : cls.getMethods()) {
if (method.getAnnotations().get(Remove.class.getName()) != null) {
continue;

View File

@ -28,7 +28,7 @@ public final class Allocator {
Address result = address;
address = result.add(tag.size);
RuntimeObject object = result.toStructure();
object.classInfo = tag;
object.classReference = tag.toAddress().toInt() >> 3;
return result;
}
}

View File

@ -18,5 +18,5 @@ package org.teavm.runtime;
import org.teavm.interop.Structure;
public class RuntimeObject extends Structure {
public RuntimeClass classInfo;
public int classReference;
}

View File

@ -29,6 +29,23 @@ public final class Example {
WasmRuntime.print(a);
}
WasmRuntime.print(new A(2).getValue() + new A(3).getValue());
for (int i = 0; i < 4; ++i) {
WasmRuntime.print(instance(i).foo());
}
}
private static Base instance(int index) {
switch (index) {
case 0:
return new Derived1();
case 1:
return new Derived2();
case 2:
return new Derived3();
default:
return new Derived4();
}
}
private static class A {
@ -42,4 +59,32 @@ public final class Example {
return value;
}
}
interface Base {
int foo();
}
static class Derived1 implements Base {
@Override
public int foo() {
return 234;
}
}
static class Derived2 implements Base {
@Override
public int foo() {
return 345;
}
}
static class Derived3 extends Derived2 {
}
static class Derived4 extends Derived1 {
@Override
public int foo() {
return 123;
}
}
}

View File

@ -24,22 +24,29 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.ast.decompilation.Decompiler;
import org.teavm.dependency.DependencyChecker;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeClass;
import org.teavm.vm.BuildTarget;
@ -109,7 +116,8 @@ public class WasmTarget implements TeaVMTarget {
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
int address = 256;
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, address);
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, address);
for (String className : classes.getClassNames()) {
classGenerator.addClass(className);
if (controller.wasCancelled()) {
@ -120,7 +128,7 @@ public class WasmTarget implements TeaVMTarget {
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
new HashSet<>());
WasmGenerationContext context = new WasmGenerationContext(classes);
WasmGenerationContext context = new WasmGenerationContext(classes, vtableProvider);
context.addIntrinsic(new WasmRuntimeIntrinsic());
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
@ -168,7 +176,7 @@ public class WasmTarget implements TeaVMTarget {
}
WasmFunction initFunction = new WasmFunction("__start__");
classGenerator.contributeToInitializer(initFunction.getBody());
classGenerator.contributeToInitializer(initFunction.getBody(), module);
for (String className : classes.getClassNames()) {
ClassReader cls = classes.get(className);
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) {
@ -181,6 +189,7 @@ public class WasmTarget implements TeaVMTarget {
initFunction.getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit.getReference())));
}
module.add(initFunction);
module.setStartFunction(initFunction);
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
@ -253,6 +262,33 @@ public class WasmTarget implements TeaVMTarget {
module.add(function);
}
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
Set<MethodReference> virtualMethods = new HashSet<>();
for (String className : classes.getClassNames()) {
ClassHolder cls = classes.get(className);
for (MethodHolder method : cls.getMethods()) {
Program program = method.getProgram();
if (program == null) {
continue;
}
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (Instruction insn : block.getInstructions()) {
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn;
if (invoke.getType() == InvocationType.VIRTUAL) {
virtualMethods.add(invoke.getMethod());
}
}
}
}
}
}
return new VirtualTableProvider(classes, virtualMethods);
}
public static void main(String[] args) throws IOException {
TeaVM vm = new TeaVMBuilder(new WasmTarget()).build();
vm.installPlugins();

View File

@ -17,6 +17,7 @@ package org.teavm.wasm.generate;
import com.carrotsearch.hppc.ObjectIntMap;
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -27,7 +28,12 @@ import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTable;
import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.wasm.model.WasmModule;
import org.teavm.wasm.model.expression.WasmExpression;
import org.teavm.wasm.model.expression.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype;
@ -37,9 +43,11 @@ public class WasmClassGenerator {
private ClassReaderSource classSource;
private int address;
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
private VirtualTableProvider vtableProvider;
public WasmClassGenerator(ClassReaderSource classSource, int address) {
public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider, int address) {
this.classSource = classSource;
this.vtableProvider = vtableProvider;
this.address = address;
}
@ -56,8 +64,11 @@ public class WasmClassGenerator {
if (binaryData.start < 0) {
return;
}
binaryData.start = align(address, 4);
binaryData.end = binaryData.start + 8;
binaryData.start = align(address, 8);
binaryData.vtable = vtableProvider.lookup(className);
int vtableSize = binaryData.vtable != null ? binaryData.vtable.getEntries().size() : 0;
binaryData.end = binaryData.start + 8 + vtableSize * 4;
address = binaryData.end;
}
@ -66,7 +77,9 @@ public class WasmClassGenerator {
return address;
}
public void contributeToInitializer(List<WasmExpression> initializer) {
public void contributeToInitializer(List<WasmExpression> initializer, WasmModule module) {
Map<MethodReference, Integer> functions = new HashMap<>();
for (ClassBinaryData binaryData : binaryDataMap.values()) {
if (binaryData.start < 0) {
continue;
@ -74,6 +87,26 @@ public class WasmClassGenerator {
WasmExpression index = new WasmInt32Constant(binaryData.start);
WasmExpression size = new WasmInt32Constant(binaryData.size);
initializer.add(new WasmStoreInt32(4, index, size, WasmInt32Subtype.INT32));
if (binaryData.vtable != null) {
for (VirtualTableEntry vtableEntry : binaryData.vtable.getEntries().values()) {
index = new WasmInt32Constant(binaryData.start + 8 + vtableEntry.getIndex() * 4);
int methodIndex;
if (vtableEntry.getImplementor() == null) {
methodIndex = -1;
} else {
methodIndex = functions.computeIfAbsent(vtableEntry.getImplementor(), implementor -> {
int result = module.getFunctionTable().size();
String name = WasmMangling.mangleMethod(implementor);
module.getFunctionTable().add(module.getFunctions().get(name));
return result;
});
}
WasmExpression methodIndexExpr = new WasmInt32Constant(methodIndex);
initializer.add(new WasmStoreInt32(4, index, methodIndexExpr, WasmInt32Subtype.INT32));
}
}
}
}
@ -122,6 +155,9 @@ public class WasmClassGenerator {
}
private static int align(int base, int alignment) {
if (base == 0) {
return 0;
}
return ((base - 1) / alignment + 1) * alignment;
}
@ -149,6 +185,7 @@ public class WasmClassGenerator {
int start;
int end;
VirtualTable vtable;
int size;
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
}

View File

@ -29,16 +29,19 @@ import org.teavm.model.FieldReference;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.wasm.intrinsics.WasmIntrinsic;
public class WasmGenerationContext {
private ClassReaderSource classSource;
private VirtualTableProvider vtableProvider;
private Map<MethodReference, ImportedMethod> importedMethods = new HashMap<>();
private List<WasmIntrinsic> intrinsics = new ArrayList<>();
private Map<MethodReference, WasmIntrinsic> intrinsicCache = new HashMap<>();
public WasmGenerationContext(ClassReaderSource classSource) {
public WasmGenerationContext(ClassReaderSource classSource, VirtualTableProvider vtableProvider) {
this.classSource = classSource;
this.vtableProvider = vtableProvider;
}
public void addIntrinsic(WasmIntrinsic intrinsic) {
@ -86,6 +89,10 @@ public class WasmGenerationContext {
return field.getType();
}
public VirtualTableProvider getVirtualTableProvider() {
return vtableProvider;
}
public class ImportedMethod {
public final String name;
public final String module;

View File

@ -15,10 +15,8 @@
*/
package org.teavm.wasm.generate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.ast.AssignmentStatement;
@ -60,13 +58,16 @@ import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement;
import org.teavm.interop.Address;
import org.teavm.interop.Structure;
import org.teavm.model.ClassReader;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeClass;
import org.teavm.wasm.WasmRuntime;
import org.teavm.wasm.intrinsics.WasmIntrinsic;
import org.teavm.wasm.intrinsics.WasmIntrinsicManager;
import org.teavm.wasm.model.WasmFunction;
@ -86,6 +87,7 @@ import org.teavm.wasm.model.expression.WasmFloatBinary;
import org.teavm.wasm.model.expression.WasmFloatBinaryOperation;
import org.teavm.wasm.model.expression.WasmFloatType;
import org.teavm.wasm.model.expression.WasmGetLocal;
import org.teavm.wasm.model.expression.WasmIndirectCall;
import org.teavm.wasm.model.expression.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype;
import org.teavm.wasm.model.expression.WasmInt64Constant;
@ -104,7 +106,6 @@ import org.teavm.wasm.model.expression.WasmStoreFloat64;
import org.teavm.wasm.model.expression.WasmStoreInt32;
import org.teavm.wasm.model.expression.WasmStoreInt64;
import org.teavm.wasm.model.expression.WasmSwitch;
import org.teavm.wasm.WasmRuntime;
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmGenerationContext context;
@ -116,6 +117,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private Map<IdentifiedStatement, WasmBlock> breakTargets = new HashMap<>();
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
private Set<WasmBlock> usedBlocks = new HashSet<>();
private int temporaryInt32 = -1;
WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
@ -486,36 +488,42 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override
public void visit(SwitchStatement statement) {
List<WasmBlock> wrappers = new ArrayList<>();
WasmBlock defaultBlock = new WasmBlock(false);
breakTargets.put(statement, defaultBlock);
IdentifiedStatement oldBreakTarget = currentBreakTarget;
currentBreakTarget = statement;
WasmBlock wrapper = new WasmBlock(false);
statement.getValue().acceptVisitor(this);
WasmSwitch wasmSwitch = new WasmSwitch(result, wrapper);
wrapper.getBody().add(wasmSwitch);
WasmBlock defaultBlock = new WasmBlock(false);
defaultBlock.getBody().add(wrapper);
for (Statement part : statement.getDefaultClause()) {
part.acceptVisitor(this);
defaultBlock.getBody().add(result);
}
wrapper = defaultBlock;
for (SwitchClause clause : statement.getClauses()) {
WasmBlock caseBlock = new WasmBlock(false);
caseBlock.getBody().add(wrapper);
wasmSwitch.getTargets().add(wrapper);
for (Statement part : clause.getBody()) {
part.acceptVisitor(this);
if (result != null) {
caseBlock.getBody().add(result);
}
wrappers.add(caseBlock);
}
wrapper = caseBlock;
}
for (WasmBlock nestedWrapper : wrappers) {
nestedWrapper.getBody().add(new WasmBreak(wrapper));
defaultBlock.getBody().add(wrapper);
for (Statement part : statement.getDefaultClause()) {
part.acceptVisitor(this);
if (result != null) {
defaultBlock.getBody().add(result);
}
}
wasmSwitch.setDefaultTarget(wrapper);
wrapper = defaultBlock;
breakTargets.remove(statement);
currentBreakTarget = oldBreakTarget;
result = wrapper;
}
@ -570,6 +578,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
generateAddressInvocation(expr);
return;
}
if (expr.getMethod().getClassName().equals(Structure.class.getName())) {
generateStructureInvocation(expr);
return;
}
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
if (intrinsic != null) {
@ -589,6 +601,40 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
call.getArguments().add(result);
}
result = call;
} else {
expr.getArguments().get(0).acceptVisitor(this);
WasmExpression instance = result;
WasmBlock block = new WasmBlock(false);
WasmLocal instanceVar = function.getLocalVariables().get(getTemporaryInt32());
block.getBody().add(new WasmSetLocal(instanceVar, instance));
instance = new WasmGetLocal(instanceVar);
WasmExpression classIndex = new WasmLoadInt32(4, instance, WasmInt32Subtype.INT32);
classIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classIndex,
new WasmInt32Constant(3));
VirtualTableEntry vtableEntry = context.getVirtualTableProvider().lookup(expr.getMethod());
WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
classIndex, new WasmInt32Constant(vtableEntry.getIndex() * 4 + 8));
methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32);
WasmIndirectCall call = new WasmIndirectCall(methodIndex);
call.getParameterTypes().add(WasmType.INT32);
for (int i = 0; i < expr.getMethod().parameterCount(); ++i) {
call.getParameterTypes().add(WasmGeneratorUtil.mapType(expr.getMethod().parameterType(i)));
}
if (expr.getMethod().getReturnType() != ValueType.VOID) {
call.setReturnType(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()));
}
call.getArguments().add(instance);
for (int i = 1; i < expr.getArguments().size(); ++i) {
expr.getArguments().get(i).acceptVisitor(this);
call.getArguments().add(result);
}
block.getBody().add(call);
result = block;
}
}
@ -700,6 +746,14 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
}
}
private void generateStructureInvocation(InvocationExpr expr) {
switch (expr.getMethod().getName()) {
case "toAddress":
expr.getArguments().get(0).acceptVisitor(this);
break;
}
}
@Override
public void visit(BlockStatement statement) {
WasmBlock block = new WasmBlock(false);
@ -990,4 +1044,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
return result;
}
};
private int getTemporaryInt32() {
if (temporaryInt32 < 0) {
temporaryInt32 = function.getLocalVariables().size();
function.add(new WasmLocal(WasmType.INT32));
}
return temporaryInt32;
}
}

View File

@ -17,6 +17,7 @@ package org.teavm.wasm.generate;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;

View File

@ -0,0 +1,183 @@
/*
* Copyright 2016 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.wasm.model.expression;
public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmBlock expression) {
for (WasmExpression part : expression.getBody()) {
part.acceptVisitor(this);
}
}
@Override
public void visit(WasmBranch expression) {
expression.getCondition().acceptVisitor(this);
if (expression.getResult() != null) {
expression.getResult().acceptVisitor(this);
}
}
@Override
public void visit(WasmBreak expression) {
if (expression.getResult() != null) {
expression.getResult().acceptVisitor(this);
}
}
@Override
public void visit(WasmSwitch expression) {
expression.getSelector().acceptVisitor(this);
}
@Override
public void visit(WasmConditional expression) {
expression.getCondition().acceptVisitor(this);
for (WasmExpression part : expression.getThenBlock().getBody()) {
part.acceptVisitor(this);
}
for (WasmExpression part : expression.getElseBlock().getBody()) {
part.acceptVisitor(this);
}
}
@Override
public void visit(WasmReturn expression) {
if (expression.getValue() != null) {
expression.getValue().acceptVisitor(this);
}
}
@Override
public void visit(WasmUnreachable expression) {
}
@Override
public void visit(WasmInt32Constant expression) {
}
@Override
public void visit(WasmInt64Constant expression) {
}
@Override
public void visit(WasmFloat32Constant expression) {
}
@Override
public void visit(WasmFloat64Constant expression) {
}
@Override
public void visit(WasmGetLocal expression) {
}
@Override
public void visit(WasmSetLocal expression) {
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmIntBinary expression) {
expression.getFirst().acceptVisitor(this);
expression.getSecond().acceptVisitor(this);
}
@Override
public void visit(WasmFloatBinary expression) {
expression.getFirst().acceptVisitor(this);
expression.getSecond().acceptVisitor(this);
}
@Override
public void visit(WasmIntUnary expression) {
expression.getOperand().acceptVisitor(this);
}
@Override
public void visit(WasmFloatUnary expression) {
expression.getOperand().acceptVisitor(this);
}
@Override
public void visit(WasmConversion expression) {
expression.getOperand().acceptVisitor(this);
}
@Override
public void visit(WasmCall expression) {
for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
}
@Override
public void visit(WasmIndirectCall expression) {
expression.getSelector().acceptVisitor(this);
for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this);
}
}
@Override
public void visit(WasmDrop expression) {
expression.getOperand().acceptVisitor(this);
}
@Override
public void visit(WasmLoadInt32 expression) {
expression.getIndex().acceptVisitor(this);
}
@Override
public void visit(WasmLoadInt64 expression) {
expression.getIndex().acceptVisitor(this);
}
@Override
public void visit(WasmLoadFloat32 expression) {
expression.getIndex().acceptVisitor(this);
}
@Override
public void visit(WasmLoadFloat64 expression) {
expression.getIndex().acceptVisitor(this);
}
@Override
public void visit(WasmStoreInt32 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmStoreInt64 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmStoreFloat32 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
}
@Override
public void visit(WasmStoreFloat64 expression) {
expression.getIndex().acceptVisitor(this);
expression.getValue().acceptVisitor(this);
}
}

View File

@ -18,16 +18,16 @@ package org.teavm.wasm.model.expression;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.teavm.wasm.model.WasmType;
public class WasmIndirectCall extends WasmExpression {
private String typeName;
private List<WasmType> parameterTypes = new ArrayList<>();
private WasmType returnType;
private WasmExpression selector;
private List<WasmExpression> arguments = new ArrayList<>();
public WasmIndirectCall(String typeName, WasmExpression selector) {
Objects.requireNonNull(typeName);
public WasmIndirectCall(WasmExpression selector) {
Objects.requireNonNull(selector);
this.typeName = typeName;
this.selector = selector;
}
@ -40,13 +40,16 @@ public class WasmIndirectCall extends WasmExpression {
this.selector = selector;
}
public String getTypeName() {
return typeName;
public List<WasmType> getParameterTypes() {
return parameterTypes;
}
public void setTypeName(String typeName) {
Objects.requireNonNull(typeName);
this.typeName = typeName;
public WasmType getReturnType() {
return returnType;
}
public void setReturnType(WasmType returnType) {
this.returnType = returnType;
}
public List<WasmExpression> getArguments() {

View File

@ -49,6 +49,8 @@ public class WasmRenderer {
public void render(WasmModule module) {
visitor.open().append("module");
renderMemory(module);
renderTypes(module);
for (WasmFunction function : module.getFunctions().values()) {
if (function.getImportName() == null) {
continue;
@ -67,10 +69,15 @@ public class WasmRenderer {
}
lf().renderExport(function);
}
renderTable(module);
if (module.getStartFunction() != null) {
visitor.lf().open().append("start $" + module.getStartFunction().getName()).close().lf();
}
visitor.close().lf();
}
public void renderMemory(WasmModule module) {
visitor.lf();
visitor.open().append("memory " + module.getMemorySize());
for (WasmMemorySegment segment : module.getSegments()) {
visitor.lf().open().append("segment " + segment.getLength());
@ -136,16 +143,65 @@ public class WasmRenderer {
}
private void renderSignature(WasmFunction function) {
if (!function.getParameters().isEmpty()) {
WasmSignature signature = signatureFromFunction(function);
visitor.append(" ").open().append("type $type" + visitor.getSignatureIndex(signature)).close();
}
private WasmSignature signatureFromFunction(WasmFunction function) {
WasmType[] types = new WasmType[function.getParameters().size() + 1];
types[0] = function.getResult();
for (int i = 0; i < function.getParameters().size(); ++i) {
types[i + 1] = function.getParameters().get(i);
}
return new WasmSignature(types);
}
private void renderTypes(WasmModule module) {
WasmSignatureCollector signatureCollector = new WasmSignatureCollector(visitor);
for (WasmFunction function : module.getFunctions().values()) {
visitor.getSignatureIndex(signatureFromFunction(function));
for (WasmExpression part : function.getBody()) {
part.acceptVisitor(signatureCollector);
}
}
if (visitor.signatureList.isEmpty()) {
return;
}
visitor.lf();
int index = 0;
for (WasmSignature signature : visitor.signatureList) {
visitor.open().append("type $type" + index++ + " ");
visitor.open().append("func");
if (signature.types.length > 1) {
visitor.append(" ").open().append("param");
for (WasmType type : function.getParameters()) {
visitor.append(" ").append(type);
for (int i = 1; i < signature.types.length; ++i) {
visitor.append(" ").append(signature.types[i]);
}
visitor.close();
}
if (function.getResult() != null) {
visitor.append(" ").open().append("result ").append(function.getResult()).close();
if (signature.types[0] != null) {
visitor.append(" ").open().append("result ");
visitor.append(signature.types[0]);
visitor.close();
}
visitor.close();
visitor.close();
visitor.lf();
}
}
private void renderTable(WasmModule module) {
if (module.getFunctionTable().isEmpty()) {
return;
}
visitor.lf().open().append("table");
for (WasmFunction function : module.getFunctionTable()) {
visitor.lf().append("$" + function.getName());
}
visitor.close().lf();
}
@Override

View File

@ -15,8 +15,10 @@
*/
package org.teavm.wasm.render;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.wasm.model.WasmLocal;
@ -65,6 +67,8 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
private Map<WasmBlock, String> blockIdentifiers = new HashMap<>();
private int indentLevel;
private boolean lfDeferred;
List<WasmSignature> signatureList = new ArrayList<>();
Map<WasmSignature, Integer> signatureMap = new HashMap<>();
void clear() {
blockIdentifiers.clear();
@ -341,7 +345,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override
public void visit(WasmIndirectCall expression) {
open().append("call_indirect").append(" " + expression.getTypeName());
WasmType[] types = new WasmType[expression.getParameterTypes().size() + 1];
types[0] = expression.getReturnType();
for (int i = 0; i < expression.getParameterTypes().size(); ++i) {
types[i + 1] = expression.getParameterTypes().get(i);
}
open().append("call_indirect").append(" $type" + getSignatureIndex(new WasmSignature(types)));
line(expression.getSelector());
for (WasmExpression argument : expression.getArguments()) {
line(argument);
@ -349,6 +359,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close();
}
int getSignatureIndex(WasmSignature signature) {
return signatureMap.computeIfAbsent(signature, key -> {
signatureList.add(key);
return signatureMap.size();
});
}
@Override
public void visit(WasmDrop expression) {
append(expression.getOperand());

View File

@ -0,0 +1,44 @@
/*
* Copyright 2016 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.wasm.render;
import java.util.Arrays;
import org.teavm.wasm.model.WasmType;
class WasmSignature {
WasmType[] types;
public WasmSignature(WasmType[] types) {
this.types = types;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
WasmSignature that = (WasmSignature) o;
return Arrays.equals(types, that.types);
}
@Override
public int hashCode() {
return Arrays.hashCode(types);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2016 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.wasm.render;
import org.teavm.wasm.model.WasmType;
import org.teavm.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.wasm.model.expression.WasmIndirectCall;
class WasmSignatureCollector extends WasmDefaultExpressionVisitor {
WasmRenderingVisitor renderingVisitor;
public WasmSignatureCollector(WasmRenderingVisitor renderingVisitor) {
this.renderingVisitor = renderingVisitor;
}
@Override
public void visit(WasmIndirectCall expression) {
WasmType[] types = new WasmType[expression.getParameterTypes().size() + 1];
types[0] = expression.getReturnType();
for (int i = 0; i < expression.getParameterTypes().size(); ++i) {
types[i + 1] = expression.getParameterTypes().get(i);
}
renderingVisitor.getSignatureIndex(new WasmSignature(types));
}
}

View File

@ -16,11 +16,11 @@
package org.teavm.interop;
public class Structure {
public native <T extends Structure> T cast();
public final native <T extends Structure> T cast();
public native Address toAddress();
public final native Address toAddress();
public native int sizeOf(Class<? extends Structure> type);
public final native int sizeOf(Class<? extends Structure> type);
public native <T extends Structure> T add(T base, int offset);
public final native <T extends Structure> T add(T base, int offset);
}