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

View File

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

View File

@ -18,5 +18,5 @@ package org.teavm.runtime;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
public class RuntimeObject extends 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(a);
} }
WasmRuntime.print(new A(2).getValue() + new A(3).getValue()); 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 { private static class A {
@ -42,4 +59,32 @@ public final class Example {
return value; 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.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Structure; import org.teavm.interop.Structure;
import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.Instruction;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; 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.Allocator;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
import org.teavm.vm.BuildTarget; import org.teavm.vm.BuildTarget;
@ -109,7 +116,8 @@ public class WasmTarget implements TeaVMTarget {
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) { public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
int address = 256; 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()) { for (String className : classes.getClassNames()) {
classGenerator.addClass(className); classGenerator.addClass(className);
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
@ -120,7 +128,7 @@ public class WasmTarget implements TeaVMTarget {
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
new HashSet<>()); new HashSet<>());
WasmGenerationContext context = new WasmGenerationContext(classes); WasmGenerationContext context = new WasmGenerationContext(classes, vtableProvider);
context.addIntrinsic(new WasmRuntimeIntrinsic()); context.addIntrinsic(new WasmRuntimeIntrinsic());
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator); WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
@ -168,7 +176,7 @@ public class WasmTarget implements TeaVMTarget {
} }
WasmFunction initFunction = new WasmFunction("__start__"); WasmFunction initFunction = new WasmFunction("__start__");
classGenerator.contributeToInitializer(initFunction.getBody()); classGenerator.contributeToInitializer(initFunction.getBody(), module);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
ClassReader cls = classes.get(className); ClassReader cls = classes.get(className);
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) { 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()))); initFunction.getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit.getReference())));
} }
module.add(initFunction); module.add(initFunction);
module.setStartFunction(initFunction);
for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) { for (TeaVMEntryPoint entryPoint : controller.getEntryPoints().values()) {
String mangledName = WasmMangling.mangleMethod(entryPoint.getReference()); String mangledName = WasmMangling.mangleMethod(entryPoint.getReference());
@ -253,6 +262,33 @@ public class WasmTarget implements TeaVMTarget {
module.add(function); 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 { public static void main(String[] args) throws IOException {
TeaVM vm = new TeaVMBuilder(new WasmTarget()).build(); TeaVM vm = new TeaVMBuilder(new WasmTarget()).build();
vm.installPlugins(); vm.installPlugins();

View File

@ -17,6 +17,7 @@ package org.teavm.wasm.generate;
import com.carrotsearch.hppc.ObjectIntMap; import com.carrotsearch.hppc.ObjectIntMap;
import com.carrotsearch.hppc.ObjectIntOpenHashMap; import com.carrotsearch.hppc.ObjectIntOpenHashMap;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -27,7 +28,12 @@ import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReader; import org.teavm.model.FieldReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; 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.WasmExpression;
import org.teavm.wasm.model.expression.WasmInt32Constant; import org.teavm.wasm.model.expression.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype; import org.teavm.wasm.model.expression.WasmInt32Subtype;
@ -37,9 +43,11 @@ public class WasmClassGenerator {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private int address; private int address;
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>(); 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.classSource = classSource;
this.vtableProvider = vtableProvider;
this.address = address; this.address = address;
} }
@ -56,8 +64,11 @@ public class WasmClassGenerator {
if (binaryData.start < 0) { if (binaryData.start < 0) {
return; 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; address = binaryData.end;
} }
@ -66,7 +77,9 @@ public class WasmClassGenerator {
return address; 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()) { for (ClassBinaryData binaryData : binaryDataMap.values()) {
if (binaryData.start < 0) { if (binaryData.start < 0) {
continue; continue;
@ -74,6 +87,26 @@ public class WasmClassGenerator {
WasmExpression index = new WasmInt32Constant(binaryData.start); WasmExpression index = new WasmInt32Constant(binaryData.start);
WasmExpression size = new WasmInt32Constant(binaryData.size); WasmExpression size = new WasmInt32Constant(binaryData.size);
initializer.add(new WasmStoreInt32(4, index, size, WasmInt32Subtype.INT32)); 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) { private static int align(int base, int alignment) {
if (base == 0) {
return 0;
}
return ((base - 1) / alignment + 1) * alignment; return ((base - 1) / alignment + 1) * alignment;
} }
@ -149,6 +185,7 @@ public class WasmClassGenerator {
int start; int start;
int end; int end;
VirtualTable vtable;
int size; int size;
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>(); 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.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTableProvider;
import org.teavm.wasm.intrinsics.WasmIntrinsic; import org.teavm.wasm.intrinsics.WasmIntrinsic;
public class WasmGenerationContext { public class WasmGenerationContext {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private VirtualTableProvider vtableProvider;
private Map<MethodReference, ImportedMethod> importedMethods = new HashMap<>(); private Map<MethodReference, ImportedMethod> importedMethods = new HashMap<>();
private List<WasmIntrinsic> intrinsics = new ArrayList<>(); private List<WasmIntrinsic> intrinsics = new ArrayList<>();
private Map<MethodReference, WasmIntrinsic> intrinsicCache = new HashMap<>(); private Map<MethodReference, WasmIntrinsic> intrinsicCache = new HashMap<>();
public WasmGenerationContext(ClassReaderSource classSource) { public WasmGenerationContext(ClassReaderSource classSource, VirtualTableProvider vtableProvider) {
this.classSource = classSource; this.classSource = classSource;
this.vtableProvider = vtableProvider;
} }
public void addIntrinsic(WasmIntrinsic intrinsic) { public void addIntrinsic(WasmIntrinsic intrinsic) {
@ -86,6 +89,10 @@ public class WasmGenerationContext {
return field.getType(); return field.getType();
} }
public VirtualTableProvider getVirtualTableProvider() {
return vtableProvider;
}
public class ImportedMethod { public class ImportedMethod {
public final String name; public final String name;
public final String module; public final String module;

View File

@ -15,10 +15,8 @@
*/ */
package org.teavm.wasm.generate; package org.teavm.wasm.generate;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.ast.AssignmentStatement; import org.teavm.ast.AssignmentStatement;
@ -60,13 +58,16 @@ import org.teavm.ast.UnwrapArrayExpr;
import org.teavm.ast.VariableExpr; import org.teavm.ast.VariableExpr;
import org.teavm.ast.WhileStatement; import org.teavm.ast.WhileStatement;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Structure;
import org.teavm.model.ClassReader; import org.teavm.model.ClassReader;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.model.classes.VirtualTableEntry;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
import org.teavm.wasm.WasmRuntime;
import org.teavm.wasm.intrinsics.WasmIntrinsic; import org.teavm.wasm.intrinsics.WasmIntrinsic;
import org.teavm.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.wasm.intrinsics.WasmIntrinsicManager;
import org.teavm.wasm.model.WasmFunction; 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.WasmFloatBinaryOperation;
import org.teavm.wasm.model.expression.WasmFloatType; import org.teavm.wasm.model.expression.WasmFloatType;
import org.teavm.wasm.model.expression.WasmGetLocal; 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.WasmInt32Constant;
import org.teavm.wasm.model.expression.WasmInt32Subtype; import org.teavm.wasm.model.expression.WasmInt32Subtype;
import org.teavm.wasm.model.expression.WasmInt64Constant; 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.WasmStoreInt32;
import org.teavm.wasm.model.expression.WasmStoreInt64; import org.teavm.wasm.model.expression.WasmStoreInt64;
import org.teavm.wasm.model.expression.WasmSwitch; import org.teavm.wasm.model.expression.WasmSwitch;
import org.teavm.wasm.WasmRuntime;
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmGenerationContext context; private WasmGenerationContext context;
@ -116,6 +117,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private Map<IdentifiedStatement, WasmBlock> breakTargets = new HashMap<>(); private Map<IdentifiedStatement, WasmBlock> breakTargets = new HashMap<>();
private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>(); private Map<IdentifiedStatement, WasmBlock> continueTargets = new HashMap<>();
private Set<WasmBlock> usedBlocks = new HashSet<>(); private Set<WasmBlock> usedBlocks = new HashSet<>();
private int temporaryInt32 = -1;
WasmExpression result; WasmExpression result;
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
@ -486,36 +488,42 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(SwitchStatement statement) { 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); WasmBlock wrapper = new WasmBlock(false);
statement.getValue().acceptVisitor(this); statement.getValue().acceptVisitor(this);
WasmSwitch wasmSwitch = new WasmSwitch(result, wrapper); WasmSwitch wasmSwitch = new WasmSwitch(result, wrapper);
wrapper.getBody().add(wasmSwitch); 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()) { for (SwitchClause clause : statement.getClauses()) {
WasmBlock caseBlock = new WasmBlock(false); WasmBlock caseBlock = new WasmBlock(false);
caseBlock.getBody().add(wrapper); caseBlock.getBody().add(wrapper);
wasmSwitch.getTargets().add(wrapper); wasmSwitch.getTargets().add(wrapper);
for (Statement part : clause.getBody()) { for (Statement part : clause.getBody()) {
part.acceptVisitor(this); part.acceptVisitor(this);
if (result != null) {
caseBlock.getBody().add(result); caseBlock.getBody().add(result);
} }
wrappers.add(caseBlock); }
wrapper = caseBlock; wrapper = caseBlock;
} }
for (WasmBlock nestedWrapper : wrappers) { defaultBlock.getBody().add(wrapper);
nestedWrapper.getBody().add(new WasmBreak(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; result = wrapper;
} }
@ -570,6 +578,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
generateAddressInvocation(expr); generateAddressInvocation(expr);
return; return;
} }
if (expr.getMethod().getClassName().equals(Structure.class.getName())) {
generateStructureInvocation(expr);
return;
}
WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod()); WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod());
if (intrinsic != null) { if (intrinsic != null) {
@ -589,6 +601,40 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
call.getArguments().add(result); call.getArguments().add(result);
} }
result = call; 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 @Override
public void visit(BlockStatement statement) { public void visit(BlockStatement statement) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
@ -990,4 +1044,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
return result; 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.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.teavm.wasm.model.WasmType;
public class WasmIndirectCall extends WasmExpression { public class WasmIndirectCall extends WasmExpression {
private String typeName; private List<WasmType> parameterTypes = new ArrayList<>();
private WasmType returnType;
private WasmExpression selector; private WasmExpression selector;
private List<WasmExpression> arguments = new ArrayList<>(); private List<WasmExpression> arguments = new ArrayList<>();
public WasmIndirectCall(String typeName, WasmExpression selector) { public WasmIndirectCall(WasmExpression selector) {
Objects.requireNonNull(typeName);
Objects.requireNonNull(selector); Objects.requireNonNull(selector);
this.typeName = typeName;
this.selector = selector; this.selector = selector;
} }
@ -40,13 +40,16 @@ public class WasmIndirectCall extends WasmExpression {
this.selector = selector; this.selector = selector;
} }
public String getTypeName() { public List<WasmType> getParameterTypes() {
return typeName; return parameterTypes;
} }
public void setTypeName(String typeName) { public WasmType getReturnType() {
Objects.requireNonNull(typeName); return returnType;
this.typeName = typeName; }
public void setReturnType(WasmType returnType) {
this.returnType = returnType;
} }
public List<WasmExpression> getArguments() { public List<WasmExpression> getArguments() {

View File

@ -49,6 +49,8 @@ public class WasmRenderer {
public void render(WasmModule module) { public void render(WasmModule module) {
visitor.open().append("module"); visitor.open().append("module");
renderMemory(module); renderMemory(module);
renderTypes(module);
for (WasmFunction function : module.getFunctions().values()) { for (WasmFunction function : module.getFunctions().values()) {
if (function.getImportName() == null) { if (function.getImportName() == null) {
continue; continue;
@ -67,10 +69,15 @@ public class WasmRenderer {
} }
lf().renderExport(function); lf().renderExport(function);
} }
renderTable(module);
if (module.getStartFunction() != null) {
visitor.lf().open().append("start $" + module.getStartFunction().getName()).close().lf();
}
visitor.close().lf(); visitor.close().lf();
} }
public void renderMemory(WasmModule module) { public void renderMemory(WasmModule module) {
visitor.lf();
visitor.open().append("memory " + module.getMemorySize()); visitor.open().append("memory " + module.getMemorySize());
for (WasmMemorySegment segment : module.getSegments()) { for (WasmMemorySegment segment : module.getSegments()) {
visitor.lf().open().append("segment " + segment.getLength()); visitor.lf().open().append("segment " + segment.getLength());
@ -136,16 +143,65 @@ public class WasmRenderer {
} }
private void renderSignature(WasmFunction function) { 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"); visitor.append(" ").open().append("param");
for (WasmType type : function.getParameters()) { for (int i = 1; i < signature.types.length; ++i) {
visitor.append(" ").append(type); visitor.append(" ").append(signature.types[i]);
} }
visitor.close(); visitor.close();
} }
if (function.getResult() != null) { if (signature.types[0] != null) {
visitor.append(" ").open().append("result ").append(function.getResult()).close(); 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 @Override

View File

@ -15,8 +15,10 @@
*/ */
package org.teavm.wasm.render; package org.teavm.wasm.render;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.teavm.wasm.model.WasmLocal; import org.teavm.wasm.model.WasmLocal;
@ -65,6 +67,8 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
private Map<WasmBlock, String> blockIdentifiers = new HashMap<>(); private Map<WasmBlock, String> blockIdentifiers = new HashMap<>();
private int indentLevel; private int indentLevel;
private boolean lfDeferred; private boolean lfDeferred;
List<WasmSignature> signatureList = new ArrayList<>();
Map<WasmSignature, Integer> signatureMap = new HashMap<>();
void clear() { void clear() {
blockIdentifiers.clear(); blockIdentifiers.clear();
@ -341,7 +345,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmIndirectCall expression) { 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()); line(expression.getSelector());
for (WasmExpression argument : expression.getArguments()) { for (WasmExpression argument : expression.getArguments()) {
line(argument); line(argument);
@ -349,6 +359,13 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
close(); close();
} }
int getSignatureIndex(WasmSignature signature) {
return signatureMap.computeIfAbsent(signature, key -> {
signatureList.add(key);
return signatureMap.size();
});
}
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
append(expression.getOperand()); 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; package org.teavm.interop;
public class Structure { 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);
} }