Implementing reflection in WASM

This commit is contained in:
Alexey Andreev 2016-08-19 19:57:03 +03:00
parent 3f02cad9e7
commit 5479d24a21
12 changed files with 217 additions and 43 deletions

View File

@ -17,13 +17,13 @@ package org.teavm.classlib.java.lang.reflect;
import org.teavm.classlib.java.lang.*; import org.teavm.classlib.java.lang.*;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.DelegateTo;
import org.teavm.javascript.spi.GeneratedBy; import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
import org.teavm.runtime.Allocator;
import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject;
/**
*
* @author Alexey Andreev
*/
public final class TArray extends TObject { public final class TArray extends TObject {
@GeneratedBy(ArrayNativeGenerator.class) @GeneratedBy(ArrayNativeGenerator.class)
@PluggableDependency(ArrayNativeGenerator.class) @PluggableDependency(ArrayNativeGenerator.class)
@ -44,8 +44,14 @@ public final class TArray extends TObject {
@GeneratedBy(ArrayNativeGenerator.class) @GeneratedBy(ArrayNativeGenerator.class)
@PluggableDependency(ArrayNativeGenerator.class) @PluggableDependency(ArrayNativeGenerator.class)
@DelegateTo("newInstanceLowLevel")
private static native TObject newInstanceImpl(PlatformClass componentType, int length); private static native TObject newInstanceImpl(PlatformClass componentType, int length);
@SuppressWarnings("unused")
private static RuntimeObject newInstanceLowLevel(RuntimeClass cls, int length) {
return Allocator.allocateArray(cls, length).toStructure();
}
public static TObject get(TObject array, int index) throws TIllegalArgumentException, public static TObject get(TObject array, int index) throws TIllegalArgumentException,
TArrayIndexOutOfBoundsException { TArrayIndexOutOfBoundsException {
if (index < 0 || index >= getLength(array)) { if (index < 0 || index >= getLength(array)) {

View File

@ -39,6 +39,7 @@ import org.teavm.codegen.SourceWriterBuilder;
import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DebugInformationEmitter;
import org.teavm.debugging.information.SourceLocation; import org.teavm.debugging.information.SourceLocation;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.MethodDependency; import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.Renderer; import org.teavm.javascript.Renderer;
import org.teavm.javascript.RenderingException; import org.teavm.javascript.RenderingException;
@ -88,6 +89,11 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public List<DependencyListener> getDependencyListeners() {
return Collections.emptyList();
}
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
this.controller = controller; this.controller = controller;

View File

@ -137,6 +137,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
for (ClassHolderTransformer transformer : target.getTransformers()) { for (ClassHolderTransformer transformer : target.getTransformers()) {
dependencyChecker.addClassTransformer(transformer); dependencyChecker.addClassTransformer(transformer);
} }
for (DependencyListener listener : target.getDependencyListeners()) {
dependencyChecker.addDependencyListener(listener);
}
for (TeaVMHostExtension extension : target.getHostExtensions()) { for (TeaVMHostExtension extension : target.getHostExtensions()) {
for (Class<?> extensionType : getExtensionTypes(extension)) { for (Class<?> extensionType : getExtensionTypes(extension)) {

View File

@ -18,6 +18,7 @@ package org.teavm.vm;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassHolderSource;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
@ -25,6 +26,8 @@ import org.teavm.vm.spi.TeaVMHostExtension;
public interface TeaVMTarget { public interface TeaVMTarget {
List<ClassHolderTransformer> getTransformers(); List<ClassHolderTransformer> getTransformers();
List<DependencyListener> getDependencyListeners();
void setController(TeaVMTargetController controller); void setController(TeaVMTargetController controller);
List<TeaVMHostExtension> getHostExtensions(); List<TeaVMHostExtension> getHostExtensions();

View File

@ -71,13 +71,13 @@ public final class Example {
WasmRuntime.print(new Object().hashCode()); WasmRuntime.print(new Object().hashCode());
WasmRuntime.print(new Object().hashCode()); WasmRuntime.print(new Object().hashCode());
/*List<Integer> list = new ArrayList<>(Arrays.asList(333, 444, 555)); List<Integer> list = new ArrayList<>(Arrays.asList(333, 444, 555));
list.add(1234); list.add(1234);
list.remove(444); list.remove((Integer) 444);
for (int item : list) { for (int item : list) {
WasmRuntime.print(item); WasmRuntime.print(item);
}*/ }
} }
private static Base instance(int index) { private static Base instance(int index) {

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.wasm; package org.teavm.wasm;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
@ -29,9 +28,12 @@ import java.util.Set;
import org.teavm.ast.decompilation.Decompiler; import org.teavm.ast.decompilation.Decompiler;
import org.teavm.dependency.ClassDependency; import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.DelegateTo;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.model.AnnotationHolder;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.CallLocation; import org.teavm.model.CallLocation;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
@ -59,14 +61,13 @@ import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeJavaObject; import org.teavm.runtime.RuntimeJavaObject;
import org.teavm.runtime.RuntimeObject; import org.teavm.runtime.RuntimeObject;
import org.teavm.vm.BuildTarget; import org.teavm.vm.BuildTarget;
import org.teavm.vm.TeaVM;
import org.teavm.vm.TeaVMBuilder;
import org.teavm.vm.TeaVMEntryPoint; 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.binary.BinaryWriter; import org.teavm.wasm.binary.BinaryWriter;
import org.teavm.wasm.generate.WasmClassGenerator; import org.teavm.wasm.generate.WasmClassGenerator;
import org.teavm.wasm.generate.WasmDependencyListener;
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;
@ -126,6 +127,13 @@ public class WasmTarget implements TeaVMTarget {
return transformers; return transformers;
} }
@Override
public List<DependencyListener> getDependencyListeners() {
List<DependencyListener> listeners = new ArrayList<>();
listeners.add(new WasmDependencyListener());
return listeners;
}
@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)) {
@ -171,8 +179,7 @@ public class WasmTarget implements TeaVMTarget {
VirtualTableProvider vtableProvider = createVirtualTableProvider(classes); VirtualTableProvider vtableProvider = createVirtualTableProvider(classes);
TagRegistry tagRegistry = new TagRegistry(classes); TagRegistry tagRegistry = new TagRegistry(classes);
BinaryWriter binaryWriter = new BinaryWriter(256); BinaryWriter binaryWriter = new BinaryWriter(256);
WasmClassGenerator classGenerator = new WasmClassGenerator(controller.getUnprocessedClassSource(), WasmClassGenerator classGenerator = new WasmClassGenerator(classes, vtableProvider, tagRegistry, binaryWriter);
vtableProvider, tagRegistry, binaryWriter);
Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(),
new HashSet<>()); new HashSet<>());
@ -205,7 +212,26 @@ public class WasmTarget implements TeaVMTarget {
continue; continue;
} }
if (method.hasModifier(ElementModifier.NATIVE)) { MethodHolder implementor = method;
AnnotationHolder delegateAnnot = method.getAnnotations().get(DelegateTo.class.getName());
if (delegateAnnot != null) {
String methodName = delegateAnnot.getValue("value").getString();
boolean found = false;
for (MethodHolder candidate : cls.getMethods()) {
if (candidate.getName().equals(methodName)) {
if (found) {
controller.getDiagnostics().error(new CallLocation(method.getReference()),
"Method is delegated to " + methodName + " but several implementations "
+ "found");
break;
}
implementor = candidate;
found = true;
}
}
}
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 "
@ -214,10 +240,10 @@ public class WasmTarget implements TeaVMTarget {
module.add(generator.generateNative(method.getReference())); module.add(generator.generateNative(method.getReference()));
continue; continue;
} }
if (method.getProgram() == null || method.getProgram().basicBlockCount() == 0) { if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) {
continue; continue;
} }
module.add(generator.generate(method.getReference())); module.add(generator.generate(method.getReference(), implementor));
if (controller.wasCancelled()) { if (controller.wasCancelled()) {
return; return;
} }
@ -365,14 +391,4 @@ public class WasmTarget implements TeaVMTarget {
return new VirtualTableProvider(classes, virtualMethods); return new VirtualTableProvider(classes, virtualMethods);
} }
public static void main(String[] args) throws IOException {
TeaVM vm = new TeaVMBuilder(new WasmTarget()).build();
vm.installPlugins();
vm.entryPoint("main", new MethodReference(Example.class, "main", String[].class, void.class));
try (OutputStream output = new FileOutputStream(args[0])) {
vm.build(output, null);
System.err.println("Problems found: " + vm.getProblemProvider().getProblems().size());
}
}
} }

View File

@ -0,0 +1,43 @@
/*
* 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 org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.MethodDependency;
import org.teavm.interop.DelegateTo;
import org.teavm.model.AnnotationReader;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodReader;
public class WasmDependencyListener extends AbstractDependencyListener {
@Override
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
AnnotationReader delegateAnnot = method.getMethod().getAnnotations().get(DelegateTo.class.getName());
if (delegateAnnot != null) {
String delegateMethodName = delegateAnnot.getValue("value").getString();
ClassReader cls = agent.getClassSource().get(method.getReference().getClassName());
for (MethodReader delegate : cls.getMethods()) {
if (delegate.getName().equals(delegateMethodName)) {
if (delegate != method.getMethod()) {
agent.linkMethod(delegate.getReference(), location).use();
}
}
}
}
}
}

View File

@ -45,6 +45,7 @@ import org.teavm.ast.MonitorExitStatement;
import org.teavm.ast.NewArrayExpr; import org.teavm.ast.NewArrayExpr;
import org.teavm.ast.NewExpr; import org.teavm.ast.NewExpr;
import org.teavm.ast.NewMultiArrayExpr; import org.teavm.ast.NewMultiArrayExpr;
import org.teavm.ast.NodeLocation;
import org.teavm.ast.OperationType; import org.teavm.ast.OperationType;
import org.teavm.ast.PrimitiveCastExpr; import org.teavm.ast.PrimitiveCastExpr;
import org.teavm.ast.QualificationExpr; import org.teavm.ast.QualificationExpr;
@ -114,6 +115,7 @@ import org.teavm.wasm.model.expression.WasmSwitch;
import org.teavm.wasm.model.expression.WasmUnreachable; import org.teavm.wasm.model.expression.WasmUnreachable;
class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private static FieldReference tagField = new FieldReference(RuntimeClass.class.getName(), "tag");
private WasmGenerationContext context; private WasmGenerationContext context;
private WasmClassGenerator classGenerator; private WasmClassGenerator classGenerator;
private WasmFunction function; private WasmFunction function;
@ -165,6 +167,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
call.getArguments().add(result); call.getArguments().add(result);
expr.getSecondOperand().acceptVisitor(this); expr.getSecondOperand().acceptVisitor(this);
call.getArguments().add(result); call.getArguments().add(result);
call.setLocation(expr.getLocation());
result = call; result = call;
break; break;
} }
@ -215,6 +218,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
call.getArguments().add(result); call.getArguments().add(result);
expr.getSecondOperand().acceptVisitor(this); expr.getSecondOperand().acceptVisitor(this);
call.getArguments().add(result); call.getArguments().add(result);
call.setLocation(expr.getLocation());
result = call; result = call;
break; break;
} }
@ -251,6 +255,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
break; break;
} }
} }
result.setLocation(expr.getLocation());
} }
private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) { private void generateBinary(WasmIntBinaryOperation intOp, BinaryExpr expr) {
@ -282,6 +287,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
case DOUBLE: case DOUBLE:
throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType()); throw new AssertionError("Can't translate operation " + intOp + " for type " + expr.getType());
} }
result.setLocation(expr.getLocation());
} }
private Class<?> convertType(OperationType type) { private Class<?> convertType(OperationType type) {
@ -304,11 +311,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
expr.getFirstOperand().acceptVisitor(this); expr.getFirstOperand().acceptVisitor(this);
WasmBranch branch = new WasmBranch(negate(result), block); WasmBranch branch = new WasmBranch(negate(result), block);
branch.setResult(new WasmInt32Constant(0)); branch.setResult(new WasmInt32Constant(0));
branch.setLocation(expr.getLocation());
branch.getResult().setLocation(expr.getLocation());
block.getBody().add(branch); block.getBody().add(branch);
expr.getSecondOperand().acceptVisitor(this); expr.getSecondOperand().acceptVisitor(this);
block.getBody().add(result); block.getBody().add(result);
block.setLocation(expr.getLocation());
result = block; result = block;
} }
@ -318,11 +329,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
expr.getFirstOperand().acceptVisitor(this); expr.getFirstOperand().acceptVisitor(this);
WasmBranch branch = new WasmBranch(result, block); WasmBranch branch = new WasmBranch(result, block);
branch.setResult(new WasmInt32Constant(1)); branch.setResult(new WasmInt32Constant(1));
branch.setLocation(expr.getLocation());
branch.getResult().setLocation(expr.getLocation());
block.getBody().add(branch); block.getBody().add(branch);
expr.getSecondOperand().acceptVisitor(this); expr.getSecondOperand().acceptVisitor(this);
block.getBody().add(result); block.getBody().add(result);
block.setLocation(expr.getLocation());
result = block; result = block;
} }
@ -333,22 +348,28 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(24)); result, new WasmInt32Constant(24));
result.setLocation(expr.getLocation());
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED,
result, new WasmInt32Constant(24)); result, new WasmInt32Constant(24));
result.setLocation(expr.getLocation());
break; break;
case INT_TO_SHORT: case INT_TO_SHORT:
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(16)); result, new WasmInt32Constant(16));
result.setLocation(expr.getLocation());
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_SIGNED,
result, new WasmInt32Constant(16)); result, new WasmInt32Constant(16));
result.setLocation(expr.getLocation());
break; break;
case INT_TO_CHAR: case INT_TO_CHAR:
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
result, new WasmInt32Constant(16)); result, new WasmInt32Constant(16));
result.setLocation(expr.getLocation());
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED,
result, new WasmInt32Constant(16)); result, new WasmInt32Constant(16));
result.setLocation(expr.getLocation());
break; break;
case LENGTH: case LENGTH:
expr.getOperand().acceptVisitor(this); expr.getOperand().acceptVisitor(this);
@ -364,18 +385,22 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
case INT: case INT:
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
new WasmInt32Constant(0), result); new WasmInt32Constant(0), result);
result.setLocation(expr.getLocation());
break; break;
case LONG: case LONG:
result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB, result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.SUB,
new WasmInt64Constant(0), result); new WasmInt64Constant(0), result);
result.setLocation(expr.getLocation());
break; break;
case FLOAT: case FLOAT:
result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB, result = new WasmFloatBinary(WasmFloatType.FLOAT32, WasmFloatBinaryOperation.SUB,
new WasmFloat32Constant(0), result); new WasmFloat32Constant(0), result);
result.setLocation(expr.getLocation());
break; break;
case DOUBLE: case DOUBLE:
result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB, result = new WasmFloatBinary(WasmFloatType.FLOAT64, WasmFloatBinaryOperation.SUB,
new WasmFloat64Constant(0), result); new WasmFloat64Constant(0), result);
result.setLocation(expr.getLocation());
break; break;
} }
break; break;
@ -388,9 +413,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
private WasmExpression generateArrayLength(WasmExpression array) { private WasmExpression generateArrayLength(WasmExpression array) {
int sizeOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeArray.class.getName(), "size")); int sizeOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeArray.class.getName(), "size"));
WasmIntBinary ptr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, WasmExpression ptr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
array, new WasmInt32Constant(sizeOffset)); array, new WasmInt32Constant(sizeOffset));
return new WasmLoadInt32(4, ptr, WasmInt32Subtype.INT32); ptr.setLocation(array.getLocation());
WasmExpression length = new WasmLoadInt32(4, ptr, WasmInt32Subtype.INT32);
length.setLocation(array.getLocation());
return length;
} }
@Override @Override
@ -399,14 +428,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
if (left == null) { if (left == null) {
statement.getRightValue().acceptVisitor(this); statement.getRightValue().acceptVisitor(this);
result = new WasmDrop(result); 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);
statement.getRightValue().acceptVisitor(this); statement.getRightValue().acceptVisitor(this);
result = new WasmSetLocal(local, result); result = new WasmSetLocal(local, result);
result.setLocation(statement.getLocation());
} else if (left instanceof QualificationExpr) { } else if (left instanceof QualificationExpr) {
QualificationExpr lhs = (QualificationExpr) left; QualificationExpr lhs = (QualificationExpr) left;
storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue()); storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue(), statement.getLocation());
} else if (left instanceof SubscriptExpr) { } else if (left instanceof SubscriptExpr) {
SubscriptExpr lhs = (SubscriptExpr) left; SubscriptExpr lhs = (SubscriptExpr) left;
storeArrayItem(lhs, statement.getRightValue()); storeArrayItem(lhs, statement.getRightValue());
@ -415,8 +446,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
} }
private void storeField(Expr qualified, FieldReference field, Expr value) { private void storeField(Expr qualified, FieldReference field, Expr value, NodeLocation location) {
WasmExpression address = getAddress(qualified, field); WasmExpression address = getAddress(qualified, field, location);
ValueType type = context.getFieldType(field); ValueType type = context.getFieldType(field);
value.acceptVisitor(this); value.acceptVisitor(this);
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
@ -447,6 +478,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} else { } else {
result = new WasmStoreInt32(4, address, result, WasmInt32Subtype.INT32); result = new WasmStoreInt32(4, address, result, WasmInt32Subtype.INT32);
} }
result.setLocation(location);
} }
private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) { private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) {
@ -741,7 +773,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} else if (expr.getType() == InvocationType.CONSTRUCTOR) { } else if (expr.getType() == InvocationType.CONSTRUCTOR) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
WasmLocal tmp = function.getLocalVariables().get(getTemporaryInt32()); WasmLocal tmp = function.getLocalVariables().get(getTemporaryInt32());
block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName()))); block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(),
expr.getLocation())));
String methodName = WasmMangling.mangleMethod(expr.getMethod()); String methodName = WasmMangling.mangleMethod(expr.getMethod());
WasmCall call = new WasmCall(methodName); WasmCall call = new WasmCall(methodName);
@ -825,7 +858,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(QualificationExpr expr) { public void visit(QualificationExpr expr) {
WasmExpression address = getAddress(expr.getQualified(), expr.getField()); WasmExpression address = getAddress(expr.getQualified(), expr.getField(), expr.getLocation());
ValueType type = context.getFieldType(expr.getField()); ValueType type = context.getFieldType(expr.getField());
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
@ -858,14 +891,25 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
} }
} }
private WasmExpression getAddress(Expr qualified, FieldReference field) { private WasmExpression getAddress(Expr qualified, FieldReference field, NodeLocation location) {
int offset = classGenerator.getFieldOffset(field); int offset = classGenerator.getFieldOffset(field);
if (qualified == null) { if (qualified == null) {
return new WasmInt32Constant(offset); WasmExpression result = new WasmInt32Constant(offset);
result.setLocation(location);
return result;
} else { } else {
qualified.acceptVisitor(this); qualified.acceptVisitor(this);
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, result, if (offset != 0) {
new WasmInt32Constant(offset)); WasmExpression offsetExpr = new WasmInt32Constant(offset);
offsetExpr.setLocation(qualified.getLocation());
WasmExpression address = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
result, offsetExpr);
address.setLocation(location);
return address;
} else {
return result;
}
} }
} }
@ -878,6 +922,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmBlock wasmTarget = breakTargets.get(target); WasmBlock wasmTarget = breakTargets.get(target);
usedBlocks.add(wasmTarget); usedBlocks.add(wasmTarget);
result = new WasmBreak(wasmTarget); result = new WasmBreak(wasmTarget);
result.setLocation(statement.getLocation());
} }
@Override @Override
@ -889,19 +934,21 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
WasmBlock wasmTarget = continueTargets.get(target); WasmBlock wasmTarget = continueTargets.get(target);
usedBlocks.add(wasmTarget); usedBlocks.add(wasmTarget);
result = new WasmBreak(wasmTarget); result = new WasmBreak(wasmTarget);
result.setLocation(statement.getLocation());
} }
@Override @Override
public void visit(NewExpr expr) { public void visit(NewExpr expr) {
result = allocateObject(expr.getConstructedClass()); result = allocateObject(expr.getConstructedClass(), expr.getLocation());
} }
private WasmExpression allocateObject(String className) { private WasmExpression allocateObject(String className, NodeLocation location) {
int tag = classGenerator.getClassPointer(ValueType.object(className)); int tag = classGenerator.getClassPointer(ValueType.object(className));
String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate", String allocName = WasmMangling.mangleMethod(new MethodReference(Allocator.class, "allocate",
RuntimeClass.class, Address.class)); RuntimeClass.class, Address.class));
WasmCall call = new WasmCall(allocName); WasmCall call = new WasmCall(allocName);
call.getArguments().add(new WasmInt32Constant(tag)); call.getArguments().add(new WasmInt32Constant(tag));
call.setLocation(location);
return call; return call;
} }
@ -916,6 +963,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
call.getArguments().add(new WasmInt32Constant(classPointer)); call.getArguments().add(new WasmInt32Constant(classPointer));
expr.getLength().acceptVisitor(this); expr.getLength().acceptVisitor(this);
call.getArguments().add(result); call.getArguments().add(result);
call.setLocation(expr.getLocation());
result = call; result = call;
} }
@ -932,6 +980,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
result = null; result = null;
} }
result = new WasmReturn(result); result = new WasmReturn(result);
result.setLocation(statement.getLocation());
} }
@Override @Override
@ -944,9 +993,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
Collections.sort(ranges, Comparator.comparingInt(range -> range.lower)); Collections.sort(ranges, Comparator.comparingInt(range -> range.lower));
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setLocation(expr.getLocation());
WasmLocal tagVar = function.getLocalVariables().get(getTemporaryInt32()); WasmLocal tagVar = function.getLocalVariables().get(getTemporaryInt32());
int tagOffset = classGenerator.getFieldOffset(tagField);
WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
getReferenceToClass(result), new WasmInt32Constant(8)); getReferenceToClass(result), new WasmInt32Constant(tagOffset));
block.getBody().add(new WasmSetLocal(tagVar, new WasmLoadInt32(4, tagPtr, WasmInt32Subtype.INT32))); block.getBody().add(new WasmSetLocal(tagVar, new WasmLoadInt32(4, tagPtr, WasmInt32Subtype.INT32)));
WasmExpression lowerThanMinCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, WasmExpression lowerThanMinCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
@ -990,6 +1041,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
@Override @Override
public void visit(ThrowStatement statement) { public void visit(ThrowStatement statement) {
WasmBlock block = new WasmBlock(false); WasmBlock block = new WasmBlock(false);
block.setLocation(statement.getLocation());
statement.getException().acceptVisitor(this); statement.getException().acceptVisitor(this);
block.getBody().add(result); block.getBody().add(result);
@ -1007,6 +1059,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
if (classGenerator.hasClinit(statement.getClassName())) { if (classGenerator.hasClinit(statement.getClassName())) {
result = new WasmCall(WasmMangling.mangleInitializer(statement.getClassName())); result = new WasmCall(WasmMangling.mangleInitializer(statement.getClassName()));
result.setLocation(statement.getLocation());
} else { } else {
result = null; result = null;
} }
@ -1017,7 +1070,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
expr.getValue().acceptVisitor(this); expr.getValue().acceptVisitor(this);
result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()), result = new WasmConversion(WasmGeneratorUtil.mapType(expr.getSource()),
WasmGeneratorUtil.mapType(expr.getTarget()), true, result); WasmGeneratorUtil.mapType(expr.getTarget()), true, result);
result.setLocation(expr.getLocation());
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeClass;
import org.teavm.wasm.model.WasmFunction; import org.teavm.wasm.model.WasmFunction;
@ -53,11 +54,11 @@ public class WasmGenerator {
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
} }
public WasmFunction generate(MethodReference methodReference) { 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(method); RegularMethodNode methodAst = decompiler.decompileRegular(bodyMethod);
WasmFunction function = new WasmFunction(WasmMangling.mangleMethod(methodReference)); WasmFunction function = new WasmFunction(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) {

View File

@ -15,9 +15,21 @@
*/ */
package org.teavm.wasm.model.expression; package org.teavm.wasm.model.expression;
import org.teavm.ast.NodeLocation;
public abstract class WasmExpression { public abstract class WasmExpression {
private NodeLocation location;
WasmExpression() { WasmExpression() {
} }
public NodeLocation getLocation() {
return location;
}
public void setLocation(NodeLocation location) {
this.location = location;
}
public abstract void acceptVisitor(WasmExpressionVisitor visitor); public abstract void acceptVisitor(WasmExpressionVisitor visitor);
} }

View File

@ -97,7 +97,11 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
} }
WasmRenderingVisitor line(WasmExpression expression) { WasmRenderingVisitor line(WasmExpression expression) {
return lf().append(expression); if (expression.getLocation() != null) {
lf().append(";; " + expression.getLocation().getFileName() + ":" + expression.getLocation().getLine());
}
lf().append(expression);
return this;
} }
WasmRenderingVisitor indent() { WasmRenderingVisitor indent() {

View File

@ -0,0 +1,27 @@
/*
* 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.METHOD)
public @interface DelegateTo {
String value();
}