mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Start supporting classes in WASM
This commit is contained in:
parent
081efd2d60
commit
675abe8740
34
core/src/main/java/org/teavm/runtime/Allocator.java
Normal file
34
core/src/main/java/org/teavm/runtime/Allocator.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
|
||||||
|
@StaticInit
|
||||||
|
public final class Allocator {
|
||||||
|
private static Address address = initialize();
|
||||||
|
|
||||||
|
private static native Address initialize();
|
||||||
|
|
||||||
|
public static Address allocate(RuntimeClass tag) {
|
||||||
|
Address result = address;
|
||||||
|
address = result.add(tag.size);
|
||||||
|
RuntimeObject object = result.toStructure();
|
||||||
|
object.classInfo = tag;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
24
core/src/main/java/org/teavm/runtime/RuntimeClass.java
Normal file
24
core/src/main/java/org/teavm/runtime/RuntimeClass.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
|
||||||
|
public class RuntimeClass extends Structure {
|
||||||
|
public static int INITIALIZED = 1;
|
||||||
|
public int size;
|
||||||
|
public int flags;
|
||||||
|
}
|
22
core/src/main/java/org/teavm/runtime/RuntimeObject.java
Normal file
22
core/src/main/java/org/teavm/runtime/RuntimeObject.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
|
||||||
|
public class RuntimeObject extends Structure {
|
||||||
|
public RuntimeClass classInfo;
|
||||||
|
}
|
|
@ -30,5 +30,18 @@ public final class Example {
|
||||||
b = c;
|
b = c;
|
||||||
WasmRuntime.print(a);
|
WasmRuntime.print(a);
|
||||||
}
|
}
|
||||||
|
WasmRuntime.print(new A(2).getValue() + new A(3).getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class A {
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public A(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,22 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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.Import;
|
import org.teavm.interop.Import;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
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.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.vm.BuildTarget;
|
import org.teavm.vm.BuildTarget;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
import org.teavm.vm.TeaVMBuilder;
|
import org.teavm.vm.TeaVMBuilder;
|
||||||
|
@ -40,11 +49,25 @@ import org.teavm.vm.TeaVMEntryPoint;
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
import org.teavm.vm.TeaVMTargetController;
|
import org.teavm.vm.TeaVMTargetController;
|
||||||
import org.teavm.vm.spi.TeaVMHostExtension;
|
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
import org.teavm.wasm.generate.WasmClassGenerator;
|
||||||
import org.teavm.wasm.generate.WasmGenerationContext;
|
import org.teavm.wasm.generate.WasmGenerationContext;
|
||||||
import org.teavm.wasm.generate.WasmGenerator;
|
import org.teavm.wasm.generate.WasmGenerator;
|
||||||
import org.teavm.wasm.generate.WasmMangling;
|
import org.teavm.wasm.generate.WasmMangling;
|
||||||
import org.teavm.wasm.model.WasmFunction;
|
import org.teavm.wasm.model.WasmFunction;
|
||||||
import org.teavm.wasm.model.WasmModule;
|
import org.teavm.wasm.model.WasmModule;
|
||||||
|
import org.teavm.wasm.model.WasmType;
|
||||||
|
import org.teavm.wasm.model.expression.WasmBlock;
|
||||||
|
import org.teavm.wasm.model.expression.WasmBranch;
|
||||||
|
import org.teavm.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt32Subtype;
|
||||||
|
import org.teavm.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.wasm.model.expression.WasmLoadInt32;
|
||||||
|
import org.teavm.wasm.model.expression.WasmReturn;
|
||||||
|
import org.teavm.wasm.model.expression.WasmStoreInt32;
|
||||||
import org.teavm.wasm.render.WasmRenderer;
|
import org.teavm.wasm.render.WasmRenderer;
|
||||||
import org.teavm.wasm.runtime.WasmRuntime;
|
import org.teavm.wasm.runtime.WasmRuntime;
|
||||||
|
|
||||||
|
@ -76,19 +99,46 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
MethodReference method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
||||||
dependencyChecker.linkMethod(method, null).use();
|
dependencyChecker.linkMethod(method, null).use();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
|
RuntimeClass.class, Address.class), null).use();
|
||||||
|
dependencyChecker.linkMethod(new MethodReference(Allocator.class, "<clinit>", void.class), null).use();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
|
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) {
|
||||||
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), new HashSet<>());
|
int address = 256;
|
||||||
|
|
||||||
|
WasmClassGenerator classGenerator = new WasmClassGenerator(classes, address);
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
|
classGenerator.addClass(className);
|
||||||
|
if (controller.wasCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
address = classGenerator.getAddress();
|
||||||
|
|
||||||
|
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
|
||||||
|
new HashSet<>());
|
||||||
WasmGenerationContext context = new WasmGenerationContext(classes);
|
WasmGenerationContext context = new WasmGenerationContext(classes);
|
||||||
WasmGenerator generator = new WasmGenerator(decompiler, classes, context);
|
WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator);
|
||||||
|
|
||||||
WasmModule module = new WasmModule();
|
WasmModule module = new WasmModule();
|
||||||
|
module.setMemorySize(64);
|
||||||
|
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
|
if (method.getOwnerName().equals(Allocator.class.getName())
|
||||||
|
&& method.getName().equals("initialize")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (method.hasModifier(ElementModifier.NATIVE)) {
|
if (method.hasModifier(ElementModifier.NATIVE)) {
|
||||||
|
if (method.getOwnerName().equals(Structure.class.getName())
|
||||||
|
|| method.getOwnerName().equals(Address.class.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (context.getImportedMethod(method.getReference()) == null) {
|
if (context.getImportedMethod(method.getReference()) == null) {
|
||||||
CallLocation location = new CallLocation(method.getReference());
|
CallLocation location = new CallLocation(method.getReference());
|
||||||
controller.getDiagnostics().error(location, "Method {{m0}} is native but "
|
controller.getDiagnostics().error(location, "Method {{m0}} is native but "
|
||||||
|
@ -107,6 +157,27 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAllocatorInit(module, address);
|
||||||
|
renderClinit(classes, classGenerator, module);
|
||||||
|
if (controller.wasCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmFunction initFunction = new WasmFunction("__start__");
|
||||||
|
classGenerator.contributeToInitializer(initFunction.getBody());
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
|
ClassReader cls = classes.get(className);
|
||||||
|
if (cls.getAnnotations().get(StaticInit.class.getName()) == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MethodReader clinit = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||||
|
if (clinit == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
initFunction.getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit.getReference())));
|
||||||
|
}
|
||||||
|
module.add(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());
|
||||||
WasmFunction function = module.getFunctions().get(mangledName);
|
WasmFunction function = module.getFunctions().get(mangledName);
|
||||||
|
@ -127,6 +198,57 @@ public class WasmTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renderClinit(ListableClassReaderSource classes, WasmClassGenerator classGenerator,
|
||||||
|
WasmModule module) {
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
|
if (classGenerator.isStructure(className)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classes.get(className);
|
||||||
|
MethodReader method = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
|
||||||
|
if (method == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WasmFunction initFunction = new WasmFunction(WasmMangling.mangleInitializer(className));
|
||||||
|
module.add(initFunction);
|
||||||
|
|
||||||
|
WasmBlock block = new WasmBlock(false);
|
||||||
|
|
||||||
|
int index = classGenerator.getClassPointer(className);
|
||||||
|
WasmExpression initFlag = new WasmLoadInt32(4, new WasmInt32Constant(index), WasmInt32Subtype.INT32);
|
||||||
|
initFlag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, initFlag,
|
||||||
|
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
||||||
|
block.getBody().add(new WasmBranch(initFlag, block));
|
||||||
|
initFunction.getBody().add(block);
|
||||||
|
|
||||||
|
initFlag = new WasmLoadInt32(4, new WasmInt32Constant(index), WasmInt32Subtype.INT32);
|
||||||
|
initFlag = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.OR, initFlag,
|
||||||
|
new WasmInt32Constant(RuntimeClass.INITIALIZED));
|
||||||
|
block.getBody().add(new WasmStoreInt32(4, new WasmInt32Constant(index), initFlag,
|
||||||
|
WasmInt32Subtype.INT32));
|
||||||
|
|
||||||
|
if (method != null) {
|
||||||
|
block.getBody().add(new WasmCall(WasmMangling.mangleMethod(method.getReference())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.wasCancelled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderAllocatorInit(WasmModule module, int address) {
|
||||||
|
address = (((address - 1) / 4096) + 1) * 4096;
|
||||||
|
|
||||||
|
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(new MethodReference(
|
||||||
|
Allocator.class, "initialize", Address.class)));
|
||||||
|
function.setResult(WasmType.INT32);
|
||||||
|
function.getBody().add(new WasmReturn(new WasmInt32Constant(address)));
|
||||||
|
module.add(function);
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* 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.generate;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectIntOpenHashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReader;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt32Subtype;
|
||||||
|
import org.teavm.wasm.model.expression.WasmStoreInt32;
|
||||||
|
|
||||||
|
public class WasmClassGenerator {
|
||||||
|
private ClassReaderSource classSource;
|
||||||
|
private int address;
|
||||||
|
private Map<String, ClassBinaryData> binaryDataMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public WasmClassGenerator(ClassReaderSource classSource, int address) {
|
||||||
|
this.classSource = classSource;
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addClass(String className) {
|
||||||
|
if (binaryDataMap.containsKey(className)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classSource.get(className);
|
||||||
|
ClassBinaryData binaryData = new ClassBinaryData();
|
||||||
|
binaryDataMap.put(className, binaryData);
|
||||||
|
|
||||||
|
calculateLayout(cls, binaryData);
|
||||||
|
if (binaryData.start < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
binaryData.start = align(address, 4);
|
||||||
|
binaryData.end = binaryData.start + 8;
|
||||||
|
|
||||||
|
address = binaryData.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contributeToInitializer(List<WasmExpression> initializer) {
|
||||||
|
for (ClassBinaryData binaryData : binaryDataMap.values()) {
|
||||||
|
if (binaryData.start < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
WasmExpression index = new WasmInt32Constant(binaryData.start);
|
||||||
|
WasmExpression size = new WasmInt32Constant(binaryData.size);
|
||||||
|
initializer.add(new WasmStoreInt32(4, index, size, WasmInt32Subtype.INT32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getClassPointer(String className) {
|
||||||
|
ClassBinaryData data = binaryDataMap.get(className);
|
||||||
|
return data.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFieldOffset(FieldReference field) {
|
||||||
|
ClassBinaryData data = binaryDataMap.get(field.getClassName());
|
||||||
|
return data.fieldLayout.get(field.getFieldName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStructure(String className) {
|
||||||
|
ClassBinaryData data = binaryDataMap.get(className);
|
||||||
|
return data.start < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateLayout(ClassReader cls, ClassBinaryData data) {
|
||||||
|
if (cls.getName().equals(Structure.class.getName()) || cls.getName().equals(Address.class.getName())) {
|
||||||
|
data.size = 0;
|
||||||
|
data.start = -1;
|
||||||
|
} else if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
|
||||||
|
addClass(cls.getParent());
|
||||||
|
ClassBinaryData parentData = binaryDataMap.get(cls.getParent());
|
||||||
|
data.size = parentData.size;
|
||||||
|
if (parentData.start == -1) {
|
||||||
|
data.start = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data.size = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (FieldReader field : cls.getFields()) {
|
||||||
|
int desiredAlignment = getDesiredAlignment(field.getType());
|
||||||
|
if (field.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
int offset = align(address, desiredAlignment);
|
||||||
|
data.fieldLayout.put(field.getName(), offset);
|
||||||
|
address = offset + desiredAlignment;
|
||||||
|
} else {
|
||||||
|
int offset = align(data.size, desiredAlignment);
|
||||||
|
data.fieldLayout.put(field.getName(), offset);
|
||||||
|
data.size = offset + desiredAlignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int align(int base, int alignment) {
|
||||||
|
return ((base - 1) / alignment + 1) * alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDesiredAlignment(ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
return 1;
|
||||||
|
case SHORT:
|
||||||
|
case CHARACTER:
|
||||||
|
return 2;
|
||||||
|
case INTEGER:
|
||||||
|
case FLOAT:
|
||||||
|
return 4;
|
||||||
|
case LONG:
|
||||||
|
case DOUBLE:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClassBinaryData {
|
||||||
|
int start;
|
||||||
|
int end;
|
||||||
|
|
||||||
|
int size;
|
||||||
|
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,8 +22,11 @@ import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.FieldReader;
|
||||||
|
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;
|
||||||
|
|
||||||
public class WasmGenerationContext {
|
public class WasmGenerationContext {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
|
@ -58,6 +61,16 @@ public class WasmGenerationContext {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return classSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueType getFieldType(FieldReference fieldReference) {
|
||||||
|
ClassReader cls = classSource.get(fieldReference.getClassName());
|
||||||
|
FieldReader field = cls.getField(fieldReference.getFieldName());
|
||||||
|
return field.getType();
|
||||||
|
}
|
||||||
|
|
||||||
public class ImportedMethod {
|
public class ImportedMethod {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final String module;
|
public final String module;
|
||||||
|
|
|
@ -59,9 +59,17 @@ import org.teavm.ast.UnaryExpr;
|
||||||
import org.teavm.ast.UnwrapArrayExpr;
|
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.model.ClassReader;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.wasm.model.WasmFunction;
|
import org.teavm.wasm.model.WasmFunction;
|
||||||
import org.teavm.wasm.model.WasmLocal;
|
import org.teavm.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.wasm.model.WasmType;
|
||||||
import org.teavm.wasm.model.expression.WasmBlock;
|
import org.teavm.wasm.model.expression.WasmBlock;
|
||||||
import org.teavm.wasm.model.expression.WasmBranch;
|
import org.teavm.wasm.model.expression.WasmBranch;
|
||||||
import org.teavm.wasm.model.expression.WasmBreak;
|
import org.teavm.wasm.model.expression.WasmBreak;
|
||||||
|
@ -77,17 +85,28 @@ 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.WasmInt32Constant;
|
import org.teavm.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt32Subtype;
|
||||||
import org.teavm.wasm.model.expression.WasmInt64Constant;
|
import org.teavm.wasm.model.expression.WasmInt64Constant;
|
||||||
|
import org.teavm.wasm.model.expression.WasmInt64Subtype;
|
||||||
import org.teavm.wasm.model.expression.WasmIntBinary;
|
import org.teavm.wasm.model.expression.WasmIntBinary;
|
||||||
import org.teavm.wasm.model.expression.WasmIntBinaryOperation;
|
import org.teavm.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
import org.teavm.wasm.model.expression.WasmIntType;
|
import org.teavm.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.wasm.model.expression.WasmLoadFloat32;
|
||||||
|
import org.teavm.wasm.model.expression.WasmLoadFloat64;
|
||||||
|
import org.teavm.wasm.model.expression.WasmLoadInt32;
|
||||||
|
import org.teavm.wasm.model.expression.WasmLoadInt64;
|
||||||
import org.teavm.wasm.model.expression.WasmReturn;
|
import org.teavm.wasm.model.expression.WasmReturn;
|
||||||
import org.teavm.wasm.model.expression.WasmSetLocal;
|
import org.teavm.wasm.model.expression.WasmSetLocal;
|
||||||
|
import org.teavm.wasm.model.expression.WasmStoreFloat32;
|
||||||
|
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.model.expression.WasmSwitch;
|
||||||
import org.teavm.wasm.runtime.WasmRuntime;
|
import org.teavm.wasm.runtime.WasmRuntime;
|
||||||
|
|
||||||
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
private WasmGenerationContext context;
|
private WasmGenerationContext context;
|
||||||
|
private WasmClassGenerator classGenerator;
|
||||||
private WasmFunction function;
|
private WasmFunction function;
|
||||||
private int firstVariable;
|
private int firstVariable;
|
||||||
private IdentifiedStatement currentContinueTarget;
|
private IdentifiedStatement currentContinueTarget;
|
||||||
|
@ -97,8 +116,10 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
private Set<WasmBlock> usedBlocks = new HashSet<>();
|
private Set<WasmBlock> usedBlocks = new HashSet<>();
|
||||||
WasmExpression result;
|
WasmExpression result;
|
||||||
|
|
||||||
WasmGenerationVisitor(WasmGenerationContext context, WasmFunction function, int firstVariable) {
|
WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
||||||
|
WasmFunction function, int firstVariable) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.classGenerator = classGenerator;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.firstVariable = firstVariable;
|
this.firstVariable = firstVariable;
|
||||||
}
|
}
|
||||||
|
@ -350,11 +371,48 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable);
|
WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable);
|
||||||
statement.getRightValue().acceptVisitor(this);
|
statement.getRightValue().acceptVisitor(this);
|
||||||
result = new WasmSetLocal(local, result);
|
result = new WasmSetLocal(local, result);
|
||||||
|
} else if (left instanceof QualificationExpr) {
|
||||||
|
QualificationExpr lhs = (QualificationExpr) left;
|
||||||
|
storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue());
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("This expression is not supported yet");
|
throw new UnsupportedOperationException("This expression is not supported yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void storeField(Expr qualified, FieldReference field, Expr value) {
|
||||||
|
WasmExpression address = getAddress(qualified, field);
|
||||||
|
ValueType type = context.getFieldType(field);
|
||||||
|
value.acceptVisitor(this);
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
result = new WasmStoreInt32(1, address, result, WasmInt32Subtype.INT8);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
result = new WasmStoreInt32(2, address, result, WasmInt32Subtype.INT16);
|
||||||
|
break;
|
||||||
|
case CHARACTER:
|
||||||
|
result = new WasmStoreInt32(2, address, result, WasmInt32Subtype.UINT16);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
result = new WasmStoreInt32(4, address, result, WasmInt32Subtype.INT32);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
result = new WasmStoreInt64(8, address, result, WasmInt64Subtype.INT64);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
result = new WasmStoreFloat32(4, address, result);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
result = new WasmStoreFloat64(8, address, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = new WasmStoreInt32(4, address, result, WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConditionalExpr expr) {
|
public void visit(ConditionalExpr expr) {
|
||||||
expr.getCondition().acceptVisitor(this);
|
expr.getCondition().acceptVisitor(this);
|
||||||
|
@ -371,7 +429,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
WasmBlock block = new WasmBlock(false);
|
WasmBlock block = new WasmBlock(false);
|
||||||
for (Statement part : statement.getSequence()) {
|
for (Statement part : statement.getSequence()) {
|
||||||
part.acceptVisitor(this);
|
part.acceptVisitor(this);
|
||||||
block.getBody().add(result);
|
if (result != null) {
|
||||||
|
block.getBody().add(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result = block;
|
result = block;
|
||||||
}
|
}
|
||||||
|
@ -399,11 +459,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
WasmConditional conditional = new WasmConditional(result);
|
WasmConditional conditional = new WasmConditional(result);
|
||||||
for (Statement part : statement.getConsequent()) {
|
for (Statement part : statement.getConsequent()) {
|
||||||
part.acceptVisitor(this);
|
part.acceptVisitor(this);
|
||||||
conditional.getThenBlock().getBody().add(result);
|
if (result != null) {
|
||||||
|
conditional.getThenBlock().getBody().add(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (Statement part : statement.getAlternative()) {
|
for (Statement part : statement.getAlternative()) {
|
||||||
part.acceptVisitor(this);
|
part.acceptVisitor(this);
|
||||||
conditional.getElseBlock().getBody().add(result);
|
if (result != null) {
|
||||||
|
conditional.getElseBlock().getBody().add(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result = conditional;
|
result = conditional;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +543,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
for (Statement part : statement.getBody()) {
|
for (Statement part : statement.getBody()) {
|
||||||
part.acceptVisitor(this);
|
part.acceptVisitor(this);
|
||||||
loop.getBody().add(result);
|
if (result != null) {
|
||||||
|
loop.getBody().add(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loop.getBody().add(new WasmBreak(loop));
|
loop.getBody().add(new WasmBreak(loop));
|
||||||
|
|
||||||
|
@ -498,6 +564,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvocationExpr expr) {
|
public void visit(InvocationExpr expr) {
|
||||||
|
if (expr.getMethod().getClassName().equals(Address.class.getName())) {
|
||||||
|
generateAddressInvocation(expr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
||||||
String methodName = WasmMangling.mangleMethod(expr.getMethod());
|
String methodName = WasmMangling.mangleMethod(expr.getMethod());
|
||||||
|
|
||||||
|
@ -513,6 +584,114 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateAddressInvocation(InvocationExpr expr) {
|
||||||
|
switch (expr.getMethod().getName()) {
|
||||||
|
case "toInt":
|
||||||
|
case "toStructure":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
break;
|
||||||
|
case "toLong":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmConversion(WasmType.INT32, WasmType.INT64, false, result);
|
||||||
|
break;
|
||||||
|
case "fromInt":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
break;
|
||||||
|
case "fromLong":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmConversion(WasmType.INT64, WasmType.INT32, false, result);
|
||||||
|
break;
|
||||||
|
case "add": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression base = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
WasmExpression offset = result;
|
||||||
|
if (expr.getMethod().parameterType(0) == ValueType.LONG) {
|
||||||
|
offset = new WasmConversion(WasmType.INT64, WasmType.INT32, false, offset);
|
||||||
|
}
|
||||||
|
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "getByte":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadInt32(1, result, WasmInt32Subtype.INT8);
|
||||||
|
break;
|
||||||
|
case "getShort":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadInt32(2, result, WasmInt32Subtype.INT16);
|
||||||
|
break;
|
||||||
|
case "getChar":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadInt32(2, result, WasmInt32Subtype.UINT16);
|
||||||
|
break;
|
||||||
|
case "getInt":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadInt32(4, result, WasmInt32Subtype.INT32);
|
||||||
|
break;
|
||||||
|
case "getLong":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadInt64(8, result, WasmInt64Subtype.INT64);
|
||||||
|
break;
|
||||||
|
case "getFloat":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadFloat32(4, result);
|
||||||
|
break;
|
||||||
|
case "getDouble":
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
result = new WasmLoadFloat64(8, result);
|
||||||
|
break;
|
||||||
|
case "putByte": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreInt32(1, address, result, WasmInt32Subtype.INT8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putShort": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreInt32(2, address, result, WasmInt32Subtype.INT16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putChar": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreInt32(2, address, result, WasmInt32Subtype.UINT16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putInt": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreInt32(4, address, result, WasmInt32Subtype.INT32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putLong": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreInt64(8, address, result, WasmInt64Subtype.INT64);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putFloat": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreFloat32(4, address, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "putDouble": {
|
||||||
|
expr.getArguments().get(0).acceptVisitor(this);
|
||||||
|
WasmExpression address = result;
|
||||||
|
expr.getArguments().get(1).acceptVisitor(this);
|
||||||
|
result = new WasmStoreFloat64(8, address, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BlockStatement statement) {
|
public void visit(BlockStatement statement) {
|
||||||
WasmBlock block = new WasmBlock(false);
|
WasmBlock block = new WasmBlock(false);
|
||||||
|
@ -523,7 +702,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
for (Statement part : statement.getBody()) {
|
for (Statement part : statement.getBody()) {
|
||||||
part.acceptVisitor(this);
|
part.acceptVisitor(this);
|
||||||
block.getBody().add(result);
|
if (result != null) {
|
||||||
|
block.getBody().add(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statement.getId() != null) {
|
if (statement.getId() != null) {
|
||||||
|
@ -535,6 +716,48 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(QualificationExpr expr) {
|
public void visit(QualificationExpr expr) {
|
||||||
|
WasmExpression address = getAddress(expr.getQualified(), expr.getField());
|
||||||
|
|
||||||
|
ValueType type = context.getFieldType(expr.getField());
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
result = new WasmLoadInt32(1, address, WasmInt32Subtype.INT8);
|
||||||
|
break;
|
||||||
|
case SHORT:
|
||||||
|
result = new WasmLoadInt32(2, address, WasmInt32Subtype.INT16);
|
||||||
|
break;
|
||||||
|
case CHARACTER:
|
||||||
|
result = new WasmLoadInt32(2, address, WasmInt32Subtype.UINT16);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
result = new WasmLoadInt32(4, address, WasmInt32Subtype.INT32);
|
||||||
|
break;
|
||||||
|
case LONG:
|
||||||
|
result = new WasmLoadInt64(8, address, WasmInt64Subtype.INT64);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
result = new WasmLoadFloat32(4, address);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
result = new WasmLoadFloat64(8, address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = new WasmLoadInt32(4, address, WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmExpression getAddress(Expr qualified, FieldReference field) {
|
||||||
|
int offset = classGenerator.getFieldOffset(field);
|
||||||
|
if (qualified == null) {
|
||||||
|
return new WasmInt32Constant(offset);
|
||||||
|
} else {
|
||||||
|
qualified.acceptVisitor(this);
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, result,
|
||||||
|
new WasmInt32Constant(offset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -550,6 +773,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(NewExpr expr) {
|
public void visit(NewExpr expr) {
|
||||||
|
int tag = classGenerator.getClassPointer(expr.getConstructedClass());
|
||||||
|
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
|
RuntimeClass.class, Address.class));
|
||||||
|
WasmCall call = new WasmCall(allocName);
|
||||||
|
call.getArguments().add(new WasmInt32Constant(tag));
|
||||||
|
result = call;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -598,6 +827,22 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InitClassStatement statement) {
|
public void visit(InitClassStatement statement) {
|
||||||
|
if (hasClinit(statement.getClassName())) {
|
||||||
|
result = new WasmCall(WasmMangling.mangleInitializer(statement.getClassName()));
|
||||||
|
} else {
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasClinit(String className) {
|
||||||
|
if (classGenerator.isStructure(className)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ClassReader cls = context.getClassSource().get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,11 +35,14 @@ public class WasmGenerator {
|
||||||
private Decompiler decompiler;
|
private Decompiler decompiler;
|
||||||
private ClassHolderSource classSource;
|
private ClassHolderSource classSource;
|
||||||
private WasmGenerationContext context;
|
private WasmGenerationContext context;
|
||||||
|
private WasmClassGenerator classGenerator;
|
||||||
|
|
||||||
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context) {
|
public WasmGenerator(Decompiler decompiler, ClassHolderSource classSource, WasmGenerationContext context,
|
||||||
|
WasmClassGenerator classGenerator) {
|
||||||
this.decompiler = decompiler;
|
this.decompiler = decompiler;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.classGenerator = classGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WasmFunction generate(MethodReference methodReference) {
|
public WasmFunction generate(MethodReference methodReference) {
|
||||||
|
@ -79,7 +82,7 @@ public class WasmGenerator {
|
||||||
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
|
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, function, firstVariable);
|
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, function, firstVariable);
|
||||||
methodAst.getBody().acceptVisitor(visitor);
|
methodAst.getBody().acceptVisitor(visitor);
|
||||||
function.getBody().add(visitor.result);
|
function.getBody().add(visitor.result);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,10 @@ public final class WasmMangling {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String mangleInitializer(String className) {
|
||||||
|
return "clinit$" + mangleString(className);
|
||||||
|
}
|
||||||
|
|
||||||
private static String mangleString(String string) {
|
private static String mangleString(String string) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int i = 0; i < string.length(); ++i) {
|
for (int i = 0; i < string.length(); ++i) {
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class WasmMemorySegment {
|
||||||
|
private int offset;
|
||||||
|
private byte[] data = new byte[0];
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOffset(int offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return data.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(byte[] data) {
|
||||||
|
this.data = data.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int value) {
|
||||||
|
data = Arrays.copyOf(data, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData(int offset, int size) {
|
||||||
|
return Arrays.copyOfRange(data, offset, size);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,12 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class WasmModule {
|
public class WasmModule {
|
||||||
|
private int memorySize;
|
||||||
|
private List<WasmMemorySegment> segments = new ArrayList<>();
|
||||||
private Map<String, WasmFunction> functions = new LinkedHashMap<>();
|
private Map<String, WasmFunction> functions = new LinkedHashMap<>();
|
||||||
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
|
private Map<String, WasmFunction> readonlyFunctions = Collections.unmodifiableMap(functions);
|
||||||
private List<WasmFunction> functionTable = new ArrayList<>();
|
private List<WasmFunction> functionTable = new ArrayList<>();
|
||||||
|
private WasmFunction startFunction;
|
||||||
|
|
||||||
public void add(WasmFunction function) {
|
public void add(WasmFunction function) {
|
||||||
if (functions.containsKey(function.getName())) {
|
if (functions.containsKey(function.getName())) {
|
||||||
|
@ -44,4 +47,24 @@ public class WasmModule {
|
||||||
public List<WasmFunction> getFunctionTable() {
|
public List<WasmFunction> getFunctionTable() {
|
||||||
return functionTable;
|
return functionTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<WasmMemorySegment> getSegments() {
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMemorySize() {
|
||||||
|
return memorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemorySize(int memorySize) {
|
||||||
|
this.memorySize = memorySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmFunction getStartFunction() {
|
||||||
|
return startFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartFunction(WasmFunction startFunction) {
|
||||||
|
this.startFunction = startFunction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.wasm.render;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.wasm.model.WasmFunction;
|
import org.teavm.wasm.model.WasmFunction;
|
||||||
import org.teavm.wasm.model.WasmLocal;
|
import org.teavm.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.wasm.model.WasmMemorySegment;
|
||||||
import org.teavm.wasm.model.WasmModule;
|
import org.teavm.wasm.model.WasmModule;
|
||||||
import org.teavm.wasm.model.WasmType;
|
import org.teavm.wasm.model.WasmType;
|
||||||
import org.teavm.wasm.model.expression.WasmExpression;
|
import org.teavm.wasm.model.expression.WasmExpression;
|
||||||
|
@ -47,6 +48,7 @@ public class WasmRenderer {
|
||||||
|
|
||||||
public void render(WasmModule module) {
|
public void render(WasmModule module) {
|
||||||
visitor.open().append("module");
|
visitor.open().append("module");
|
||||||
|
renderMemory(module);
|
||||||
for (WasmFunction function : module.getFunctions().values()) {
|
for (WasmFunction function : module.getFunctions().values()) {
|
||||||
if (function.getImportName() == null) {
|
if (function.getImportName() == null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -68,6 +70,35 @@ public class WasmRenderer {
|
||||||
visitor.close().lf();
|
visitor.close().lf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void renderMemory(WasmModule module) {
|
||||||
|
visitor.open().append("memory " + module.getMemorySize());
|
||||||
|
for (WasmMemorySegment segment : module.getSegments()) {
|
||||||
|
visitor.lf().open().append("segment " + segment.getLength());
|
||||||
|
visitor.indent();
|
||||||
|
for (int i = 0; i < segment.getLength(); i += 256) {
|
||||||
|
visitor.lf().append("\"");
|
||||||
|
byte[] part = segment.getData(i, Math.max(segment.getLength(), i + 256) - i);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int j = 0; j < part.length; ++j) {
|
||||||
|
int b = part[j] << 24 >>> 24;
|
||||||
|
if (b < ' ' || b > 126) {
|
||||||
|
sb.append("\\0x" + Character.forDigit(b >> 4, 16) + Character.forDigit(b & 0xF, 16));
|
||||||
|
} else if (b == '\\') {
|
||||||
|
sb.append("\\\\");
|
||||||
|
} else if (b == '"') {
|
||||||
|
sb.append("\\\"");
|
||||||
|
} else {
|
||||||
|
sb.append((char) b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitor.append(sb.toString()).append("\"");
|
||||||
|
}
|
||||||
|
visitor.outdent();
|
||||||
|
visitor.close();
|
||||||
|
}
|
||||||
|
visitor.close().lf();
|
||||||
|
}
|
||||||
|
|
||||||
public void renderImport(WasmFunction function) {
|
public void renderImport(WasmFunction function) {
|
||||||
String importModule = function.getImportModule();
|
String importModule = function.getImportModule();
|
||||||
if (importModule == null) {
|
if (importModule == null) {
|
||||||
|
|
|
@ -356,42 +356,134 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmLoadInt32 expression) {
|
public void visit(WasmLoadInt32 expression) {
|
||||||
|
open();
|
||||||
|
switch (expression.getConvertFrom()) {
|
||||||
|
case INT8:
|
||||||
|
append("i32.load8_s");
|
||||||
|
break;
|
||||||
|
case UINT8:
|
||||||
|
append("i32.load8_u");
|
||||||
|
break;
|
||||||
|
case INT16:
|
||||||
|
append("i32.load16_s");
|
||||||
|
break;
|
||||||
|
case UINT16:
|
||||||
|
append("i32.load16_u");
|
||||||
|
break;
|
||||||
|
case INT32:
|
||||||
|
append("i32.load");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
append(" align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmLoadInt64 expression) {
|
public void visit(WasmLoadInt64 expression) {
|
||||||
|
open();
|
||||||
|
switch (expression.getConvertFrom()) {
|
||||||
|
case INT8:
|
||||||
|
append("i64.load8_s");
|
||||||
|
break;
|
||||||
|
case UINT8:
|
||||||
|
append("i64.load8_u");
|
||||||
|
break;
|
||||||
|
case INT16:
|
||||||
|
append("i64.load16_s");
|
||||||
|
break;
|
||||||
|
case UINT16:
|
||||||
|
append("i64.load16_u");
|
||||||
|
break;
|
||||||
|
case INT32:
|
||||||
|
append("i64.load32_s");
|
||||||
|
break;
|
||||||
|
case UINT32:
|
||||||
|
append("i64.load32_u");
|
||||||
|
break;
|
||||||
|
case INT64:
|
||||||
|
append("i64.load");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
append(" align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmLoadFloat32 expression) {
|
public void visit(WasmLoadFloat32 expression) {
|
||||||
|
open().append("f32.load align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmLoadFloat64 expression) {
|
public void visit(WasmLoadFloat64 expression) {
|
||||||
|
open().append("f64.load align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStoreInt32 expression) {
|
public void visit(WasmStoreInt32 expression) {
|
||||||
|
open();
|
||||||
|
switch (expression.getConvertTo()) {
|
||||||
|
case INT8:
|
||||||
|
case UINT8:
|
||||||
|
append("i32.store8");
|
||||||
|
case INT16:
|
||||||
|
case UINT16:
|
||||||
|
append("i32.store16");
|
||||||
|
break;
|
||||||
|
case INT32:
|
||||||
|
append("i32.store");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
append(" align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
line(expression.getValue());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStoreInt64 expression) {
|
public void visit(WasmStoreInt64 expression) {
|
||||||
|
open();
|
||||||
|
switch (expression.getConvertTo()) {
|
||||||
|
case INT8:
|
||||||
|
case UINT8:
|
||||||
|
append("i64.store8");
|
||||||
|
case INT16:
|
||||||
|
case UINT16:
|
||||||
|
append("i64.store16");
|
||||||
|
break;
|
||||||
|
case INT32:
|
||||||
|
case UINT32:
|
||||||
|
append("i64.store32");
|
||||||
|
break;
|
||||||
|
case INT64:
|
||||||
|
append("i64.store");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
append(" align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
line(expression.getValue());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStoreFloat32 expression) {
|
public void visit(WasmStoreFloat32 expression) {
|
||||||
|
open().append("f32.store align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
line(expression.getValue());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStoreFloat64 expression) {
|
public void visit(WasmStoreFloat64 expression) {
|
||||||
|
open().append("f64.store align=" + expression.getAlignment());
|
||||||
|
line(expression.getIndex());
|
||||||
|
line(expression.getValue());
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getIdentifier(String suggested) {
|
private String getIdentifier(String suggested) {
|
||||||
|
|
60
interop/core/src/main/java/org/teavm/interop/Address.java
Normal file
60
interop/core/src/main/java/org/teavm/interop/Address.java
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.interop;
|
||||||
|
|
||||||
|
public final class Address {
|
||||||
|
public final native Address add(int offset);
|
||||||
|
|
||||||
|
public final native Address add(long offset);
|
||||||
|
|
||||||
|
public final native int toInt();
|
||||||
|
|
||||||
|
public final native long toLong();
|
||||||
|
|
||||||
|
public final native <T extends Structure> T toStructure();
|
||||||
|
|
||||||
|
public final native byte getByte();
|
||||||
|
|
||||||
|
public final native void putByte(byte value);
|
||||||
|
|
||||||
|
public final native char getChar();
|
||||||
|
|
||||||
|
public final native void putChar(char value);
|
||||||
|
|
||||||
|
public final native short getShort();
|
||||||
|
|
||||||
|
public final native void putShort(short value);
|
||||||
|
|
||||||
|
public final native int getInt();
|
||||||
|
|
||||||
|
public final native void putInt(int value);
|
||||||
|
|
||||||
|
public final native long getLong();
|
||||||
|
|
||||||
|
public final native void putLong(long value);
|
||||||
|
|
||||||
|
public final native float getFloat();
|
||||||
|
|
||||||
|
public final native void putFloat(float value);
|
||||||
|
|
||||||
|
public final native double getDouble();
|
||||||
|
|
||||||
|
public final native void putDouble(double value);
|
||||||
|
|
||||||
|
public static native Address fromInt(int value);
|
||||||
|
|
||||||
|
public static native Address fromLong(long value);
|
||||||
|
}
|
26
interop/core/src/main/java/org/teavm/interop/StaticInit.java
Normal file
26
interop/core/src/main/java/org/teavm/interop/StaticInit.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.interop;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface StaticInit {
|
||||||
|
}
|
26
interop/core/src/main/java/org/teavm/interop/Structure.java
Normal file
26
interop/core/src/main/java/org/teavm/interop/Structure.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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.interop;
|
||||||
|
|
||||||
|
public class Structure {
|
||||||
|
public native <T extends Structure> T cast();
|
||||||
|
|
||||||
|
public native Address toAddress();
|
||||||
|
|
||||||
|
public native int sizeOf(Class<? extends Structure> type);
|
||||||
|
|
||||||
|
public native <T extends Structure> T add(T base, int offset);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user