WASM: implementing support of 0xC binary version

This commit is contained in:
Alexey Andreev 2016-10-06 19:56:45 +03:00
parent 499ce8c029
commit 386c703459
14 changed files with 608 additions and 104 deletions

View File

@ -32,7 +32,6 @@ import org.teavm.backend.wasm.generate.WasmClassGenerator;
import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmDependencyListener;
import org.teavm.backend.wasm.generate.WasmGenerationContext; import org.teavm.backend.wasm.generate.WasmGenerationContext;
import org.teavm.backend.wasm.generate.WasmGenerator; import org.teavm.backend.wasm.generate.WasmGenerator;
import org.teavm.backend.wasm.generate.WasmGeneratorUtil;
import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.generate.WasmMangling;
import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.generate.WasmStringPool;
import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic;
@ -57,7 +56,6 @@ import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch; import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConditional; import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetLocal; import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant; import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
@ -72,6 +70,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.optimization.UnusedFunctionElimination; import org.teavm.backend.wasm.optimization.UnusedFunctionElimination;
import org.teavm.backend.wasm.patches.ClassPatch; import org.teavm.backend.wasm.patches.ClassPatch;
import org.teavm.backend.wasm.render.WasmBinaryRenderer; import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.render.WasmCRenderer; import org.teavm.backend.wasm.render.WasmCRenderer;
import org.teavm.backend.wasm.render.WasmRenderer; import org.teavm.backend.wasm.render.WasmRenderer;
@ -131,6 +130,7 @@ public class WasmTarget implements TeaVMTarget {
private ClassInitializerTransformer classInitializerTransformer; private ClassInitializerTransformer classInitializerTransformer;
private ShadowStackTransformer shadowStackTransformer; private ShadowStackTransformer shadowStackTransformer;
private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class); private MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
private WasmBinaryVersion version = WasmBinaryVersion.V_0xC;
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
@ -189,6 +189,14 @@ public class WasmTarget implements TeaVMTarget {
this.cEmitted = cEmitted; this.cEmitted = cEmitted;
} }
public WasmBinaryVersion getVersion() {
return version;
}
public void setVersion(WasmBinaryVersion version) {
this.version = version;
}
@Override @Override
public void contributeDependencies(DependencyChecker dependencyChecker) { public void contributeDependencies(DependencyChecker dependencyChecker) {
for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) { for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) {
@ -291,7 +299,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<>());
WasmStringPool stringPool = new WasmStringPool(classGenerator, binaryWriter); WasmStringPool stringPool = new WasmStringPool(classGenerator, binaryWriter);
WasmGenerationContext context = new WasmGenerationContext(classes, controller.getDiagnostics(), WasmGenerationContext context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
vtableProvider, tagRegistry, stringPool); vtableProvider, tagRegistry, stringPool);
context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new AddressIntrinsic(classGenerator));
@ -369,7 +377,7 @@ public class WasmTarget implements TeaVMTarget {
} }
WasmBinaryWriter writer = new WasmBinaryWriter(); WasmBinaryWriter writer = new WasmBinaryWriter();
WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer); WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer, version);
renderer.render(module); renderer.render(module);
try (OutputStream output = buildTarget.createResource(outputName)) { try (OutputStream output = buildTarget.createResource(outputName)) {
@ -413,52 +421,59 @@ public class WasmTarget implements TeaVMTarget {
private void generateMethods(ListableClassHolderSource classes, WasmGenerationContext context, private void generateMethods(ListableClassHolderSource classes, WasmGenerationContext context,
WasmGenerator generator, WasmModule module) { WasmGenerator generator, WasmModule module) {
List<MethodHolder> methods = new ArrayList<>();
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 (context.getIntrinsic(method.getReference()) != null) { if (context.getIntrinsic(method.getReference()) != null) {
continue; continue;
} }
module.add(generator.generateDefinition(method.getReference()));
methods.add(method);
}
}
MethodHolder implementor = method; for (MethodHolder method : methods) {
AnnotationHolder delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName()); ClassHolder cls = classes.get(method.getOwnerName());
if (delegateAnnot != null) {
String methodName = delegateAnnot.getValue("value").getString(); MethodHolder implementor = method;
boolean found = false; AnnotationHolder delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
for (MethodHolder candidate : cls.getMethods()) { if (delegateAnnot != null) {
if (candidate.getName().equals(methodName)) { String methodName = delegateAnnot.getValue("value").getString();
if (found) { boolean found = false;
controller.getDiagnostics().error(new CallLocation(method.getReference()), for (MethodHolder candidate : cls.getMethods()) {
"Method is delegated to " + methodName + " but several implementations " if (candidate.getName().equals(methodName)) {
+ "found"); if (found) {
break; controller.getDiagnostics().error(new CallLocation(method.getReference()),
} "Method is delegated to " + methodName + " but several implementations "
implementor = candidate; + "found");
found = true; break;
} }
implementor = candidate;
found = true;
} }
} }
}
if (implementor.hasModifier(ElementModifier.NATIVE)) { if (implementor.hasModifier(ElementModifier.NATIVE)) {
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 "
+ "has no {{c1}} annotation on it", method.getReference(), Import.class.getName()); + "has no {{c1}} annotation on it", method.getReference(), Import.class.getName());
}
module.add(generator.generateNative(method.getReference()));
continue;
}
if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) {
continue;
}
if (method == implementor) {
module.add(generator.generate(method.getReference(), implementor));
} else {
module.add(generateStub(method, implementor));
}
if (controller.wasCancelled()) {
return;
} }
generator.generateNative(method.getReference());
continue;
}
if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) {
continue;
}
if (method == implementor) {
generator.generate(method.getReference(), implementor);
} else {
generateStub(module, method, implementor);
}
if (controller.wasCancelled()) {
return;
} }
} }
} }
@ -513,12 +528,14 @@ public class WasmTarget implements TeaVMTarget {
WasmExpression lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, WasmExpression lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower)); new WasmGetLocal(subtypeVar), new WasmInt32Constant(lower));
WasmConditional testLower = new WasmConditional(lowerCondition); WasmConditional testLower = new WasmConditional(lowerCondition);
testLower.setType(WasmType.INT32);
testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); testLower.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testLower); body.add(testLower);
WasmExpression upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, WasmExpression upperCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper)); new WasmGetLocal(subtypeVar), new WasmInt32Constant(upper));
WasmConditional testUpper = new WasmConditional(upperCondition); WasmConditional testUpper = new WasmConditional(upperCondition);
testLower.setType(WasmType.INT32);
testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0))); testUpper.getThenBlock().getBody().add(new WasmReturn(new WasmInt32Constant(0)));
body.add(testUpper); body.add(testUpper);
@ -555,6 +572,7 @@ public class WasmTarget implements TeaVMTarget {
WasmExpression itemCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, WasmExpression itemCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
new WasmGetLocal(subtypeVar), new WasmInt32Constant(0)); new WasmGetLocal(subtypeVar), new WasmInt32Constant(0));
WasmConditional itemTest = new WasmConditional(itemCondition); WasmConditional itemTest = new WasmConditional(itemCondition);
itemTest.setType(WasmType.INT32);
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
WasmCall delegateToItem = new WasmCall(WasmMangling.mangleIsSupertype(itemType)); WasmCall delegateToItem = new WasmCall(WasmMangling.mangleIsSupertype(itemType));
@ -564,15 +582,8 @@ public class WasmTarget implements TeaVMTarget {
body.add(new WasmReturn(itemTest)); body.add(new WasmReturn(itemTest));
} }
private WasmFunction generateStub(MethodHolder method, MethodHolder implementor) { private WasmFunction generateStub(WasmModule module, MethodHolder method, MethodHolder implementor) {
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(method.getReference())); WasmFunction function = module.getFunctions().get(WasmMangling.mangleMethod(method.getReference()));
if (!method.hasModifier(ElementModifier.STATIC)) {
function.getParameters().add(WasmType.INT32);
}
ValueType[] parameterTypes = method.getParameterTypes();
for (ValueType parameterType : parameterTypes) {
function.getParameters().add(WasmGeneratorUtil.mapType(parameterType));
}
WasmCall call = new WasmCall(WasmMangling.mangleMethod(implementor.getReference())); WasmCall call = new WasmCall(WasmMangling.mangleMethod(implementor.getReference()));
for (WasmType param : function.getParameters()) { for (WasmType param : function.getParameters()) {
@ -581,10 +592,8 @@ public class WasmTarget implements TeaVMTarget {
call.getArguments().add(new WasmGetLocal(local)); call.getArguments().add(new WasmGetLocal(local));
} }
function.setResult(WasmGeneratorUtil.mapType(method.getResultType()));
if (method.getResultType() == ValueType.VOID) { if (method.getResultType() == ValueType.VOID) {
function.getBody().add(new WasmDrop(call)); function.getBody().add(call);
} else { } else {
function.getBody().add(new WasmReturn(call)); function.getBody().add(new WasmReturn(call));
} }

View File

@ -20,6 +20,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
@ -36,6 +38,7 @@ import org.teavm.model.classes.VirtualTableProvider;
public class WasmGenerationContext { public class WasmGenerationContext {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private WasmModule module;
private Diagnostics diagnostics; private Diagnostics diagnostics;
private VirtualTableProvider vtableProvider; private VirtualTableProvider vtableProvider;
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
@ -44,9 +47,10 @@ public class WasmGenerationContext {
private List<WasmIntrinsic> intrinsics = new ArrayList<>(); private List<WasmIntrinsic> intrinsics = new ArrayList<>();
private Map<MethodReference, WasmIntrinsicHolder> intrinsicCache = new HashMap<>(); private Map<MethodReference, WasmIntrinsicHolder> intrinsicCache = new HashMap<>();
public WasmGenerationContext(ClassReaderSource classSource, Diagnostics diagnostics, public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics,
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool) { VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool) {
this.classSource = classSource; this.classSource = classSource;
this.module = module;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
this.vtableProvider = vtableProvider; this.vtableProvider = vtableProvider;
this.tagRegistry = tagRegistry; this.tagRegistry = tagRegistry;
@ -97,6 +101,10 @@ public class WasmGenerationContext {
}); });
} }
public WasmFunction getFunction(String name) {
return module.getFunctions().get(name);
}
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {
return classSource; return classSource;
} }

View File

@ -106,6 +106,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.backend.wasm.render.WasmTypeInference;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
@ -122,6 +123,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private static FieldReference tagField = new FieldReference(RuntimeClass.class.getName(), "tag"); private static FieldReference tagField = new FieldReference(RuntimeClass.class.getName(), "tag");
private WasmGenerationContext context; private WasmGenerationContext context;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmTypeInference typeInference;
private WasmFunction function; private WasmFunction function;
private int firstVariable; private int firstVariable;
private IdentifiedStatement currentContinueTarget; private IdentifiedStatement currentContinueTarget;
@ -145,6 +147,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
for (int i = 0; i < typeCount; ++i) { for (int i = 0; i < typeCount; ++i) {
temporaryVariablesByType.add(new ArrayDeque<>()); temporaryVariablesByType.add(new ArrayDeque<>());
} }
typeInference = new WasmTypeInference(context);
} }
private void accept(Expr expr) { private void accept(Expr expr) {
@ -330,6 +333,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private void generateAnd(BinaryExpr expr) { private void generateAnd(BinaryExpr expr) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32);
accept(expr.getFirstOperand()); accept(expr.getFirstOperand());
WasmBranch branch = new WasmBranch(negate(result), block); WasmBranch branch = new WasmBranch(negate(result), block);
@ -348,6 +352,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private void generateOr(BinaryExpr expr) { private void generateOr(BinaryExpr expr) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32);
accept(expr.getFirstOperand()); accept(expr.getFirstOperand());
WasmBranch branch = new WasmBranch(result, block); WasmBranch branch = new WasmBranch(result, block);
@ -450,8 +455,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
Expr left = statement.getLeftValue(); Expr left = statement.getLeftValue();
if (left == null) { if (left == null) {
accept(statement.getRightValue()); accept(statement.getRightValue());
result = new WasmDrop(result); result.acceptVisitor(typeInference);
result.setLocation(statement.getLocation()); if (typeInference.getResult() != null) {
result = new WasmDrop(result);
result.setLocation(statement.getLocation());
}
} else if (left instanceof VariableExpr) { } else if (left instanceof VariableExpr) {
VariableExpr varExpr = (VariableExpr) left; VariableExpr varExpr = (VariableExpr) left;
WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable); WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable);
@ -552,6 +560,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
accept(expr.getAlternative()); accept(expr.getAlternative());
conditional.getElseBlock().getBody().add(result); conditional.getElseBlock().getBody().add(result);
conditional.getThenBlock().acceptVisitor(typeInference);
WasmType thenType = typeInference.getResult();
conditional.getElseBlock().acceptVisitor(typeInference);
WasmType elseType = typeInference.getResult();
assert thenType == elseType;
conditional.setType(thenType);
result = conditional; result = conditional;
} }
@ -833,6 +848,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
result = call; result = call;
} else if (expr.getType() == InvocationType.CONSTRUCTOR) { } else if (expr.getType() == InvocationType.CONSTRUCTOR) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32);
WasmLocal tmp = getTemporary(WasmType.INT32); WasmLocal tmp = getTemporary(WasmType.INT32);
block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(),
expr.getLocation()))); expr.getLocation())));
@ -854,6 +871,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
accept(expr.getArguments().get(0)); accept(expr.getArguments().get(0));
WasmExpression instance = result; WasmExpression instance = result;
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()));
WasmLocal instanceVar = getTemporary(WasmType.INT32); WasmLocal instanceVar = getTemporary(WasmType.INT32);
block.getBody().add(new WasmSetLocal(instanceVar, instance)); block.getBody().add(new WasmSetLocal(instanceVar, instance));
@ -1135,6 +1153,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
ValueType type = expr.getType(); ValueType type = expr.getType();
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32);
int dimensionList = -1; int dimensionList = -1;
for (Expr dimension : expr.getDimensions()) { for (Expr dimension : expr.getDimensions()) {
int dimensionAddress = binaryWriter.append(DataPrimitives.INT.createValue()); int dimensionAddress = binaryWriter.append(DataPrimitives.INT.createValue());
@ -1180,7 +1200,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
ranges.sort(Comparator.comparingInt(range -> range.lower)); ranges.sort(Comparator.comparingInt(range -> range.lower));
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setType(WasmType.INT32);
block.setLocation(expr.getLocation()); block.setLocation(expr.getLocation());
WasmLocal tagVar = getTemporary(WasmType.INT32); WasmLocal tagVar = getTemporary(WasmType.INT32);
int tagOffset = classGenerator.getFieldOffset(tagField); int tagOffset = classGenerator.getFieldOffset(tagField);
WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
@ -1204,6 +1226,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar), WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar),
new WasmInt32Constant(ranges.get(i - 1).upper)); new WasmInt32Constant(ranges.get(i - 1).upper));
WasmConditional conditional = new WasmConditional(upperThanExcluded); WasmConditional conditional = new WasmConditional(upperThanExcluded);
conditional.setType(WasmType.INT32);
WasmExpression lowerThanExcluded = new WasmIntBinary(WasmIntType.INT32, WasmExpression lowerThanExcluded = new WasmIntBinary(WasmIntType.INT32,
WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar), WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar),
new WasmInt32Constant(ranges.get(i).lower)); new WasmInt32Constant(ranges.get(i).lower));

View File

@ -47,12 +47,30 @@ public class WasmGenerator {
this.binaryWriter = binaryWriter; this.binaryWriter = binaryWriter;
} }
public WasmFunction generateDefinition(MethodReference methodReference) {
ClassHolder cls = classSource.get(methodReference.getClassName());
MethodHolder method = cls.getMethod(methodReference.getDescriptor());
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(method.getReference()));
if (!method.hasModifier(ElementModifier.STATIC)) {
function.getParameters().add(WasmType.INT32);
}
for (int i = 0; i < method.parameterCount(); ++i) {
function.getParameters().add(WasmGeneratorUtil.mapType(method.parameterType(i)));
}
if (method.getResultType() != ValueType.VOID) {
function.setResult(WasmGeneratorUtil.mapType(method.getResultType()));
}
return function;
}
public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) { public WasmFunction generate(MethodReference methodReference, MethodHolder bodyMethod) {
ClassHolder cls = classSource.get(methodReference.getClassName()); ClassHolder cls = classSource.get(methodReference.getClassName());
MethodHolder method = cls.getMethod(methodReference.getDescriptor()); MethodHolder method = cls.getMethod(methodReference.getDescriptor());
RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod);
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); WasmFunction function = context.getFunction(WasmMangling.mangleMethod(methodReference));
int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; int firstVariable = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) { for (int i = firstVariable; i < methodAst.getVariables().size(); ++i) {
VariableNode variable = methodAst.getVariables().get(i); VariableNode variable = methodAst.getVariables().get(i);
@ -62,13 +80,6 @@ public class WasmGenerator {
function.add(new WasmLocal(type, variable.getName())); function.add(new WasmLocal(type, variable.getName()));
} }
for (int i = firstVariable; i <= methodReference.parameterCount(); ++i) {
function.getParameters().add(function.getLocalVariables().get(i - firstVariable).getType());
}
if (methodReference.getReturnType() != ValueType.VOID) {
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
}
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function, WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,
firstVariable); firstVariable);
methodAst.getBody().acceptVisitor(visitor); methodAst.getBody().acceptVisitor(visitor);
@ -83,12 +94,7 @@ public class WasmGenerator {
} }
public WasmFunction generateNative(MethodReference methodReference) { public WasmFunction generateNative(MethodReference methodReference) {
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); WasmFunction function = context.getFunction(WasmMangling.mangleMethod(methodReference));
for (int i = 0; i < methodReference.parameterCount(); ++i) {
WasmType paramType = WasmGeneratorUtil.mapType(methodReference.parameterType(i));
function.getParameters().add(paramType);
}
function.setResult(WasmGeneratorUtil.mapType(methodReference.getReturnType()));
WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference); WasmGenerationContext.ImportedMethod importedMethod = context.getImportedMethod(methodReference);
if (importedMethod != null) { if (importedMethod != null) {

View File

@ -17,10 +17,12 @@ package org.teavm.backend.wasm.model.expression;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.backend.wasm.model.WasmType;
public class WasmBlock extends WasmExpression { public class WasmBlock extends WasmExpression {
private boolean loop; private boolean loop;
private List<WasmExpression> body = new ArrayList<>(); private List<WasmExpression> body = new ArrayList<>();
private WasmType type;
public WasmBlock(boolean loop) { public WasmBlock(boolean loop) {
this.loop = loop; this.loop = loop;
@ -38,6 +40,14 @@ public class WasmBlock extends WasmExpression {
return body; return body;
} }
public WasmType getType() {
return type;
}
public void setType(WasmType type) {
this.type = type;
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -16,11 +16,13 @@
package org.teavm.backend.wasm.model.expression; package org.teavm.backend.wasm.model.expression;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmType;
public class WasmConditional extends WasmExpression { public class WasmConditional extends WasmExpression {
private WasmExpression condition; private WasmExpression condition;
private WasmBlock thenBlock = new WasmBlock(false); private WasmBlock thenBlock = new WasmBlock(false);
private WasmBlock elseBlock = new WasmBlock(false); private WasmBlock elseBlock = new WasmBlock(false);
private WasmType type;
public WasmConditional(WasmExpression condition) { public WasmConditional(WasmExpression condition) {
Objects.requireNonNull(condition); Objects.requireNonNull(condition);
@ -44,6 +46,14 @@ public class WasmConditional extends WasmExpression {
return elseBlock; return elseBlock;
} }
public WasmType getType() {
return type;
}
public void setType(WasmType type) {
this.type = type;
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -28,19 +28,43 @@ import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
public class WasmBinaryRenderer { public class WasmBinaryRenderer {
private static final int SECTION_UNKNOWN = 0;
private static final int SECTION_TYPE = 1;
private static final int SECTION_IMPORT = 2;
private static final int SECTION_FUNCTION = 3;
private static final int SECTION_TABLE = 4;
private static final int SECTION_MEMORY = 5;
private static final int SECTION_EXPORT = 7;
private static final int SECTION_START = 8;
private static final int SECTION_ELEMENT = 9;
private static final int SECTION_CODE = 10;
private static final int SECTION_DATA = 11;
private static final int EXTERNAL_KIND_FUNCTION = 0;
private WasmBinaryWriter output; private WasmBinaryWriter output;
private WasmBinaryVersion version;
private List<WasmSignature> signatures = new ArrayList<>(); private List<WasmSignature> signatures = new ArrayList<>();
private Map<WasmSignature, Integer> signatureIndexes = new HashMap<>(); private Map<WasmSignature, Integer> signatureIndexes = new HashMap<>();
private Map<String, Integer> importIndexes = new HashMap<>(); private Map<String, Integer> importIndexes = new HashMap<>();
private Map<String, Integer> functionIndexes = new HashMap<>(); private Map<String, Integer> functionIndexes = new HashMap<>();
public WasmBinaryRenderer(WasmBinaryWriter output) { public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version) {
this.output = output; this.output = output;
this.version = version;
} }
public void render(WasmModule module) { public void render(WasmModule module) {
output.writeInt32(0x6d736100); output.writeInt32(0x6d736100);
output.writeInt32(11); switch (version) {
case V_0xB:
output.writeInt32(0xB);
break;
case V_0xC:
output.writeInt32(0xC);
break;
}
renderSignatures(module); renderSignatures(module);
renderImports(module); renderImports(module);
renderFunctions(module); renderFunctions(module);
@ -48,6 +72,7 @@ public class WasmBinaryRenderer {
renderMemory(module); renderMemory(module);
renderExport(module); renderExport(module);
renderStart(module); renderStart(module);
renderElement(module);
renderCode(module); renderCode(module);
renderData(module); renderData(module);
renderNames(module); renderNames(module);
@ -79,7 +104,7 @@ public class WasmBinaryRenderer {
} }
} }
writeSection("type", section.getData()); writeSection(SECTION_TYPE, "type", section.getData());
} }
private void renderImports(WasmModule module) { private void renderImports(WasmModule module) {
@ -90,7 +115,11 @@ public class WasmBinaryRenderer {
continue; continue;
} }
functions.add(function); functions.add(function);
importIndexes.put(function.getName(), index++); if (version == WasmBinaryVersion.V_0xB) {
importIndexes.put(function.getName(), index++);
} else {
functionIndexes.put(function.getName(), functions.size());
}
} }
if (functions.isEmpty()) { if (functions.isEmpty()) {
return; return;
@ -101,7 +130,10 @@ public class WasmBinaryRenderer {
section.writeLEB(functions.size()); section.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {
WasmSignature signature = WasmSignature.fromFunction(function); WasmSignature signature = WasmSignature.fromFunction(function);
section.writeLEB(signatureIndexes.get(signature)); int signatureIndex = signatureIndexes.get(signature);
if (version == WasmBinaryVersion.V_0xB) {
section.writeLEB(signatureIndex);
}
String moduleName = function.getImportModule(); String moduleName = function.getImportModule();
if (moduleName == null) { if (moduleName == null) {
@ -110,9 +142,14 @@ public class WasmBinaryRenderer {
section.writeAsciiString(moduleName); section.writeAsciiString(moduleName);
section.writeAsciiString(function.getImportName()); section.writeAsciiString(function.getImportName());
if (version == WasmBinaryVersion.V_0xC) {
section.writeByte(EXTERNAL_KIND_FUNCTION);
section.writeLEB(signatureIndex);
}
} }
writeSection("import", section.getData()); writeSection(SECTION_IMPORT, "import", section.getData());
} }
private void renderFunctions(WasmModule module) { private void renderFunctions(WasmModule module) {
@ -132,7 +169,7 @@ public class WasmBinaryRenderer {
section.writeLEB(signatureIndexes.get(signature)); section.writeLEB(signatureIndexes.get(signature));
} }
writeSection("function", section.getData()); writeSection(SECTION_FUNCTION, "function", section.getData());
} }
private void renderTable(WasmModule module) { private void renderTable(WasmModule module) {
@ -142,22 +179,35 @@ public class WasmBinaryRenderer {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(module.getFunctionTable().size()); if (version == WasmBinaryVersion.V_0xB) {
for (WasmFunction function : module.getFunctionTable()) { section.writeLEB(module.getFunctionTable().size());
section.writeLEB(functionIndexes.get(function.getName())); for (WasmFunction function : module.getFunctionTable()) {
section.writeLEB(functionIndexes.get(function.getName()));
}
} else {
section.writeByte(1);
section.writeByte(0x20);
section.writeByte(1);
section.writeLEB(functionIndexes.size());
//section.writeLEB(functionIndexes.size());
} }
writeSection(SECTION_TABLE, "table", section.getData());
writeSection("table", section.getData());
} }
private void renderMemory(WasmModule module) { private void renderMemory(WasmModule module) {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
if (version == WasmBinaryVersion.V_0xC) {
section.writeByte(1);
section.writeByte(3);
}
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMemorySize());
section.writeLEB(module.getMemorySize()); section.writeLEB(module.getMemorySize());
section.writeByte(1); if (version == WasmBinaryVersion.V_0xB) {
section.writeByte(1);
}
writeSection("memory", section.getData()); writeSection(SECTION_MEMORY, "memory", section.getData());
} }
private void renderExport(WasmModule module) { private void renderExport(WasmModule module) {
@ -172,12 +222,20 @@ public class WasmBinaryRenderer {
section.writeLEB(functions.size()); section.writeLEB(functions.size());
for (WasmFunction function : functions) { for (WasmFunction function : functions) {
section.writeLEB(functionIndexes.get(function.getName())); int functionIndex = functionIndexes.get(function.getName());
if (version == WasmBinaryVersion.V_0xB) {
section.writeLEB(functionIndex);
}
section.writeAsciiString(function.getExportName()); section.writeAsciiString(function.getExportName());
if (version == WasmBinaryVersion.V_0xC) {
section.writeByte(EXTERNAL_KIND_FUNCTION);
section.writeLEB(functionIndex);
}
} }
writeSection("export", section.getData()); writeSection(SECTION_EXPORT, "export", section.getData());
} }
private void renderStart(WasmModule module) { private void renderStart(WasmModule module) {
@ -188,7 +246,28 @@ public class WasmBinaryRenderer {
WasmBinaryWriter section = new WasmBinaryWriter(); WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(functionIndexes.get(module.getStartFunction().getName())); section.writeLEB(functionIndexes.get(module.getStartFunction().getName()));
writeSection("start", section.getData()); writeSection(SECTION_START, "start", section.getData());
}
private void renderElement(WasmModule module) {
if (module.getFunctionTable().isEmpty() || version != WasmBinaryVersion.V_0xC) {
return;
}
WasmBinaryWriter section = new WasmBinaryWriter();
section.writeLEB(1);
section.writeLEB(0);
section.writeByte(0x10);
section.writeLEB(0);
section.writeByte(0x0F);
section.writeLEB(module.getFunctionTable().size());
for (WasmFunction function : module.getFunctionTable()) {
section.writeLEB(functionIndexes.get(function.getName()));
}
writeSection(SECTION_ELEMENT, "element", section.getData());
} }
private void renderCode(WasmModule module) { private void renderCode(WasmModule module) {
@ -205,7 +284,7 @@ public class WasmBinaryRenderer {
section.writeBytes(body); section.writeBytes(body);
} }
writeSection("code", section.getData()); writeSection(SECTION_CODE, "code", section.getData());
} }
private byte[] renderFunction(WasmFunction function) { private byte[] renderFunction(WasmFunction function) {
@ -236,8 +315,8 @@ public class WasmBinaryRenderer {
} }
} }
WasmBinaryRenderingVisitor visitor = new WasmBinaryRenderingVisitor(code, functionIndexes, importIndexes, WasmBinaryRenderingVisitor visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes,
signatureIndexes); importIndexes, signatureIndexes);
for (WasmExpression part : function.getBody()) { for (WasmExpression part : function.getBody()) {
part.acceptVisitor(visitor); part.acceptVisitor(visitor);
} }
@ -263,7 +342,7 @@ public class WasmBinaryRenderer {
} }
} }
writeSection("data", section.getData()); writeSection(SECTION_DATA, "data", section.getData());
} }
private void renderNames(WasmModule module) { private void renderNames(WasmModule module) {
@ -280,7 +359,7 @@ public class WasmBinaryRenderer {
section.writeLEB(0); section.writeLEB(0);
} }
writeSection("name", section.getData()); writeSection(SECTION_UNKNOWN, "name", section.getData());
} }
static class LocalEntry { static class LocalEntry {
@ -300,10 +379,22 @@ public class WasmBinaryRenderer {
}); });
} }
private void writeSection(String id, byte[] data) { private void writeSection(int id, String name, byte[] data) {
output.writeAsciiString(id); if (version == WasmBinaryVersion.V_0xC) {
output.writeByte(id);
int length = data.length;
if (id == 0) {
length += name.length() + 1;
}
output.writeLEB(length);
if (id == 0) {
output.writeAsciiString(name);
}
} else {
output.writeAsciiString(name);
output.writeLEB(data.length);
}
output.writeLEB(data.length);
output.writeBytes(data); output.writeBytes(data);
} }
} }

View File

@ -52,15 +52,17 @@ import org.teavm.backend.wasm.model.expression.WasmUnreachable;
class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private WasmBinaryWriter writer; private WasmBinaryWriter writer;
private WasmBinaryVersion version;
private Map<String, Integer> functionIndexes; private Map<String, Integer> functionIndexes;
private Map<String, Integer> importedIndexes; private Map<String, Integer> importedIndexes;
private Map<WasmSignature, Integer> signatureIndexes; private Map<WasmSignature, Integer> signatureIndexes;
private int depth; private int depth;
private Map<WasmBlock, Integer> blockDepths = new HashMap<>(); private Map<WasmBlock, Integer> blockDepths = new HashMap<>();
WasmBinaryRenderingVisitor(WasmBinaryWriter writer, Map<String, Integer> functionIndexes, WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmBinaryVersion version, Map<String, Integer> functionIndexes,
Map<String, Integer> importedIndexes, Map<WasmSignature, Integer> signatureIndexes) { Map<String, Integer> importedIndexes, Map<WasmSignature, Integer> signatureIndexes) {
this.writer = writer; this.writer = writer;
this.version = version;
this.functionIndexes = functionIndexes; this.functionIndexes = functionIndexes;
this.importedIndexes = importedIndexes; this.importedIndexes = importedIndexes;
this.signatureIndexes = signatureIndexes; this.signatureIndexes = signatureIndexes;
@ -71,6 +73,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
depth += expression.isLoop() ? 2 : 1; depth += expression.isLoop() ? 2 : 1;
blockDepths.put(expression, depth); blockDepths.put(expression, depth);
writer.writeByte(expression.isLoop() ? 0x02 : 0x01); writer.writeByte(expression.isLoop() ? 0x02 : 0x01);
writeBlockType(expression.getType());
for (WasmExpression part : expression.getBody()) { for (WasmExpression part : expression.getBody()) {
part.acceptVisitor(this); part.acceptVisitor(this);
} }
@ -79,6 +82,12 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
depth -= expression.isLoop() ? 2 : 1; depth -= expression.isLoop() ? 2 : 1;
} }
private void writeBlockType(WasmType type) {
if (version == WasmBinaryVersion.V_0xC) {
writer.writeType(type);
}
}
@Override @Override
public void visit(WasmBranch expression) { public void visit(WasmBranch expression) {
if (expression.getResult() != null) { if (expression.getResult() != null) {
@ -86,7 +95,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
} }
expression.getCondition().acceptVisitor(this); expression.getCondition().acceptVisitor(this);
writer.writeByte(0x07); writer.writeByte(0x07);
writer.writeByte(expression.getResult() != null ? 1 : 0); if (version == WasmBinaryVersion.V_0xB) {
writer.writeByte(expression.getResult() != null ? 1 : 0);
}
writeLabel(expression.getTarget()); writeLabel(expression.getTarget());
} }
@ -96,7 +107,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
expression.getResult().acceptVisitor(this); expression.getResult().acceptVisitor(this);
} }
writer.writeByte(0x06); writer.writeByte(0x06);
writer.writeByte(expression.getResult() != null ? 1 : 0); if (version == WasmBinaryVersion.V_0xB) {
writer.writeByte(expression.getResult() != null ? 1 : 0);
}
writeLabel(expression.getTarget()); writeLabel(expression.getTarget());
} }
@ -104,20 +117,33 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
public void visit(WasmSwitch expression) { public void visit(WasmSwitch expression) {
expression.getSelector().acceptVisitor(this); expression.getSelector().acceptVisitor(this);
writer.writeByte(0x08); writer.writeByte(0x08);
writer.writeByte(0); if (version == WasmBinaryVersion.V_0xB) {
writer.writeByte(0);
}
writer.writeLEB(expression.getTargets().size()); writer.writeLEB(expression.getTargets().size());
for (WasmBlock target : expression.getTargets()) { for (WasmBlock target : expression.getTargets()) {
int targetDepth = blockDepths.get(target); int targetDepth = blockDepths.get(target);
writer.writeFixed(depth - targetDepth); int relativeDepth = depth - targetDepth;
if (version == WasmBinaryVersion.V_0xC) {
writer.writeLEB(relativeDepth);
} else {
writer.writeFixed(relativeDepth);
}
} }
int defaultDepth = blockDepths.get(expression.getDefaultTarget()); int defaultDepth = blockDepths.get(expression.getDefaultTarget());
writer.writeFixed(depth - defaultDepth); int relativeDepth = depth - defaultDepth;
if (version == WasmBinaryVersion.V_0xC) {
writer.writeLEB(relativeDepth);
} else {
writer.writeFixed(relativeDepth);
}
} }
@Override @Override
public void visit(WasmConditional expression) { public void visit(WasmConditional expression) {
expression.getCondition().acceptVisitor(this); expression.getCondition().acceptVisitor(this);
writer.writeByte(0x03); writer.writeByte(0x03);
writeBlockType(expression.getType());
++depth; ++depth;
blockDepths.put(expression.getThenBlock(), depth); blockDepths.put(expression.getThenBlock(), depth);
@ -145,12 +171,18 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
expression.getValue().acceptVisitor(this); expression.getValue().acceptVisitor(this);
} }
writer.writeByte(0x09); writer.writeByte(0x09);
writer.writeByte(expression.getValue() != null ? 1 : 0); if (version == WasmBinaryVersion.V_0xB) {
writer.writeByte(expression.getValue() != null ? 1 : 0);
}
} }
@Override @Override
public void visit(WasmUnreachable expression) { public void visit(WasmUnreachable expression) {
writer.writeByte(0x0A); if (version == WasmBinaryVersion.V_0xB) {
writer.writeByte(0x0A);
} else {
writer.writeByte(0x0);
}
} }
@Override @Override
@ -613,7 +645,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
for (WasmExpression argument : expression.getArguments()) { for (WasmExpression argument : expression.getArguments()) {
argument.acceptVisitor(this); argument.acceptVisitor(this);
} }
Integer functionIndex = !expression.isImported() Integer functionIndex = !expression.isImported() || version == WasmBinaryVersion.V_0xC
? functionIndexes.get(expression.getFunctionName()) ? functionIndexes.get(expression.getFunctionName())
: importedIndexes.get(expression.getFunctionName()); : importedIndexes.get(expression.getFunctionName());
if (functionIndex == null) { if (functionIndex == null) {
@ -621,8 +653,12 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
return; return;
} }
writer.writeByte(!expression.isImported() ? 0x16 : 0x18); if (version == WasmBinaryVersion.V_0xB) {
writer.writeLEB(expression.getArguments().size()); writer.writeByte(!expression.isImported() ? 0x16 : 0x18);
writer.writeLEB(expression.getArguments().size());
} else {
writer.writeByte(0x16);
}
writer.writeLEB(functionIndex); writer.writeLEB(functionIndex);
} }
@ -633,7 +669,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
argument.acceptVisitor(this); argument.acceptVisitor(this);
} }
writer.writeByte(0x17); writer.writeByte(0x17);
writer.writeLEB(expression.getArguments().size()); if (version == WasmBinaryVersion.V_0xB) {
writer.writeLEB(expression.getArguments().size());
}
WasmType[] signatureTypes = new WasmType[expression.getParameterTypes().size() + 1]; WasmType[] signatureTypes = new WasmType[expression.getParameterTypes().size() + 1];
signatureTypes[0] = expression.getReturnType(); signatureTypes[0] = expression.getReturnType();
@ -646,6 +684,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
@Override @Override
public void visit(WasmDrop expression) { public void visit(WasmDrop expression) {
expression.getOperand().acceptVisitor(this); expression.getOperand().acceptVisitor(this);
if (version == WasmBinaryVersion.V_0xC) {
writer.writeByte(0x0B);
}
} }
@Override @Override

View File

@ -0,0 +1,21 @@
/*
* 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.backend.wasm.render;
public enum WasmBinaryVersion {
V_0xB,
V_0xC
}

View File

@ -28,6 +28,10 @@ public class WasmBinaryWriter {
} }
public void writeType(WasmType type) { public void writeType(WasmType type) {
if (type == null) {
writeByte(0);
return;
}
switch (type) { switch (type) {
case INT32: case INT32:
writeByte(1); writeByte(1);

View File

@ -0,0 +1,233 @@
/*
* 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.backend.wasm.render;
import org.teavm.backend.wasm.generate.WasmGenerationContext;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmBranch;
import org.teavm.backend.wasm.model.expression.WasmBreak;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant;
import org.teavm.backend.wasm.model.expression.WasmFloat64Constant;
import org.teavm.backend.wasm.model.expression.WasmFloatBinary;
import org.teavm.backend.wasm.model.expression.WasmFloatType;
import org.teavm.backend.wasm.model.expression.WasmFloatUnary;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmIndirectCall;
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
import org.teavm.backend.wasm.model.expression.WasmStoreFloat64;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
public class WasmTypeInference implements WasmExpressionVisitor {
private WasmGenerationContext context;
private WasmType result;
public WasmTypeInference(WasmGenerationContext context) {
this.context = context;
}
public WasmType getResult() {
return result;
}
@Override
public void visit(WasmBlock expression) {
result = expression.getType();
}
@Override
public void visit(WasmBranch expression) {
result = null;
}
@Override
public void visit(WasmBreak expression) {
result = null;
}
@Override
public void visit(WasmSwitch expression) {
result = null;
}
@Override
public void visit(WasmConditional expression) {
result = expression.getType();
}
@Override
public void visit(WasmReturn expression) {
result = null;
}
@Override
public void visit(WasmUnreachable expression) {
result = null;
}
@Override
public void visit(WasmInt32Constant expression) {
result = WasmType.INT32;
}
@Override
public void visit(WasmInt64Constant expression) {
result = WasmType.INT64;
}
@Override
public void visit(WasmFloat32Constant expression) {
result = WasmType.FLOAT32;
}
@Override
public void visit(WasmFloat64Constant expression) {
result = WasmType.FLOAT64;
}
@Override
public void visit(WasmGetLocal expression) {
result = expression.getLocal().getType();
}
@Override
public void visit(WasmSetLocal expression) {
result = null;
}
@Override
public void visit(WasmIntBinary expression) {
result = map(expression.getType());
}
@Override
public void visit(WasmFloatBinary expression) {
result = map(expression.getType());
}
@Override
public void visit(WasmIntUnary expression) {
result = map(expression.getType());
}
@Override
public void visit(WasmFloatUnary expression) {
result = map(expression.getType());
}
@Override
public void visit(WasmConversion expression) {
result = expression.getTargetType();
}
@Override
public void visit(WasmCall expression) {
WasmFunction function = context.getFunction(expression.getFunctionName());
result = function == null ? null : function.getResult();
}
@Override
public void visit(WasmIndirectCall expression) {
result = expression.getReturnType();
}
@Override
public void visit(WasmDrop expression) {
result = null;
}
@Override
public void visit(WasmLoadInt32 expression) {
result = WasmType.INT32;
}
@Override
public void visit(WasmLoadInt64 expression) {
result = WasmType.INT64;
}
@Override
public void visit(WasmLoadFloat32 expression) {
result = WasmType.FLOAT32;
}
@Override
public void visit(WasmLoadFloat64 expression) {
result = WasmType.FLOAT64;
}
@Override
public void visit(WasmStoreInt32 expression) {
result = null;
}
@Override
public void visit(WasmStoreInt64 expression) {
result = null;
}
@Override
public void visit(WasmStoreFloat32 expression) {
result = null;
}
@Override
public void visit(WasmStoreFloat64 expression) {
result = null;
}
private static WasmType map(WasmIntType type) {
switch (type) {
case INT32:
return WasmType.INT32;
case INT64:
return WasmType.INT64;
default:
throw new IllegalArgumentException(type.toString());
}
}
private static WasmType map(WasmFloatType type) {
switch (type) {
case FLOAT32:
return WasmType.FLOAT32;
case FLOAT64:
return WasmType.FLOAT64;
default:
throw new IllegalArgumentException(type.toString());
}
}
}

View File

@ -20,6 +20,7 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.RuntimeCopyOperation; import org.teavm.tooling.RuntimeCopyOperation;
import org.teavm.tooling.TeaVMTargetType; import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMTool; import org.teavm.tooling.TeaVMTool;
@ -105,6 +106,12 @@ public final class TeaVMRunner {
.withDescription("Additional classpath that will be reloaded by TeaVM each time in wait mode") .withDescription("Additional classpath that will be reloaded by TeaVM each time in wait mode")
.withLongOpt("classpath") .withLongOpt("classpath")
.create('p')); .create('p'));
options.addOption(OptionBuilder
.withArgName("wasm-version")
.hasArg()
.withDescription("WebAssembly binary version (11, 12)")
.withLongOpt("version")
.create());
if (args.length == 0) { if (args.length == 0) {
printUsage(options); printUsage(options);
@ -209,6 +216,8 @@ public final class TeaVMRunner {
} }
boolean interactive = commandLine.hasOption('w'); boolean interactive = commandLine.hasOption('w');
setupWasm(tool, commandLine, options);
args = commandLine.getArgs(); args = commandLine.getArgs();
if (args.length > 1) { if (args.length > 1) {
System.err.println("Unexpected arguments"); System.err.println("Unexpected arguments");
@ -275,6 +284,29 @@ public final class TeaVMRunner {
System.out.println("Build complete for " + ((System.currentTimeMillis() - startTime) / 1000.0) + " seconds"); System.out.println("Build complete for " + ((System.currentTimeMillis() - startTime) / 1000.0) + " seconds");
} }
private static void setupWasm(TeaVMTool tool, CommandLine commandLine, Options options) {
if (commandLine.hasOption("wasm-version")) {
String value = commandLine.getOptionValue("wasm-version");
try {
int version = Integer.parseInt(value);
switch (version) {
case 11:
tool.setWasmVersion(WasmBinaryVersion.V_0xB);
break;
case 12:
tool.setWasmVersion(WasmBinaryVersion.V_0xC);
break;
default:
System.err.print("Wrong version value");
printUsage(options);
}
} catch (NumberFormatException e) {
System.err.print("Wrong version value");
printUsage(options);
}
}
}
private static void resetClassLoader(TeaVMTool tool) { private static void resetClassLoader(TeaVMTool tool) {
if (classPath == null || classPath.length == 0) { if (classPath == null || classPath.length == 0) {
return; return;

View File

@ -36,6 +36,7 @@ import org.apache.commons.io.IOUtils;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.backend.javascript.rendering.RenderingManager; import org.teavm.backend.javascript.rendering.RenderingManager;
import org.teavm.backend.wasm.WasmTarget; import org.teavm.backend.wasm.WasmTarget;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.cache.DiskCachedClassHolderSource; import org.teavm.cache.DiskCachedClassHolderSource;
import org.teavm.cache.DiskProgramCache; import org.teavm.cache.DiskProgramCache;
import org.teavm.cache.DiskRegularMethodNodeCache; import org.teavm.cache.DiskRegularMethodNodeCache;
@ -98,6 +99,7 @@ public class TeaVMTool implements BaseTeaVMTool {
private DebugInformationBuilder debugEmitter; private DebugInformationBuilder debugEmitter;
private JavaScriptTarget javaScriptTarget; private JavaScriptTarget javaScriptTarget;
private WasmTarget webAssemblyTarget; private WasmTarget webAssemblyTarget;
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0xC;
public File getTargetDirectory() { public File getTargetDirectory() {
return targetDirectory; return targetDirectory;
@ -245,6 +247,14 @@ public class TeaVMTool implements BaseTeaVMTool {
this.classLoader = classLoader; this.classLoader = classLoader;
} }
public WasmBinaryVersion getWasmVersion() {
return wasmVersion;
}
public void setWasmVersion(WasmBinaryVersion wasmVersion) {
this.wasmVersion = wasmVersion;
}
public void setProgressListener(TeaVMProgressListener progressListener) { public void setProgressListener(TeaVMProgressListener progressListener) {
this.progressListener = progressListener; this.progressListener = progressListener;
} }
@ -332,6 +342,7 @@ public class TeaVMTool implements BaseTeaVMTool {
webAssemblyTarget.setDebugging(debugInformationGenerated); webAssemblyTarget.setDebugging(debugInformationGenerated);
webAssemblyTarget.setCEmitted(debugInformationGenerated); webAssemblyTarget.setCEmitted(debugInformationGenerated);
webAssemblyTarget.setWastEmitted(debugInformationGenerated); webAssemblyTarget.setWastEmitted(debugInformationGenerated);
webAssemblyTarget.setVersion(wasmVersion);
return webAssemblyTarget; return webAssemblyTarget;
} }

View File

@ -24,6 +24,7 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.plugins.annotations.ResolutionScope;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.ClassAlias; import org.teavm.tooling.ClassAlias;
import org.teavm.tooling.MethodAlias; import org.teavm.tooling.MethodAlias;
import org.teavm.tooling.RuntimeCopyOperation; import org.teavm.tooling.RuntimeCopyOperation;
@ -76,6 +77,9 @@ public class TeaVMCompileMojo extends AbstractTeaVMMojo {
private TeaVMTool tool = new TeaVMTool(); private TeaVMTool tool = new TeaVMTool();
@Parameter
private WasmBinaryVersion wasmVersion;
@Override @Override
protected File getTargetDirectory() { protected File getTargetDirectory() {
return targetDirectory; return targetDirectory;
@ -102,6 +106,7 @@ public class TeaVMCompileMojo extends AbstractTeaVMMojo {
} }
tool.setCacheDirectory(cacheDirectory); tool.setCacheDirectory(cacheDirectory);
tool.setTargetType(targetType); tool.setTargetType(targetType);
tool.setWasmVersion(wasmVersion);
tool.generate(); tool.generate();
if (stopOnErrors && !tool.getProblemProvider().getSevereProblems().isEmpty()) { if (stopOnErrors && !tool.getProblemProvider().getSevereProblems().isEmpty()) {
throw new MojoExecutionException("Build error"); throw new MojoExecutionException("Build error");