WASM: implementing intrinsics to access resources

This commit is contained in:
Alexey Andreev 2017-04-15 23:21:35 +03:00 committed by Alexey Andreev
parent 05d4652c86
commit 9e6061c3f1
9 changed files with 157 additions and 5 deletions

View File

@ -26,6 +26,7 @@ public final class Example {
} }
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(Integer.parseInt("123"));
testFibonacci(); testFibonacci();
testClasses(); testClasses();
testVirtualCall(); testVirtualCall();
@ -38,7 +39,7 @@ public final class Example {
testArrayIsObject(); testArrayIsObject();
testIsAssignableFrom(); testIsAssignableFrom();
testExceptions(); testExceptions();
testBigInteger(); //testBigInteger();
testGC(); testGC();
} }

View File

@ -23,9 +23,14 @@ public class WasmLoadFloat32 extends WasmExpression implements WasmMemoryAccess
private int offset; private int offset;
public WasmLoadFloat32(int alignment, WasmExpression index) { public WasmLoadFloat32(int alignment, WasmExpression index) {
this(alignment, index, 0);
}
public WasmLoadFloat32(int alignment, WasmExpression index, int offset) {
Objects.requireNonNull(index); Objects.requireNonNull(index);
this.alignment = alignment; this.alignment = alignment;
this.index = index; this.index = index;
this.offset = offset;
} }
public int getAlignment() { public int getAlignment() {

View File

@ -23,9 +23,14 @@ public class WasmLoadFloat64 extends WasmExpression implements WasmMemoryAccess
private int offset; private int offset;
public WasmLoadFloat64(int alignment, WasmExpression index) { public WasmLoadFloat64(int alignment, WasmExpression index) {
this(alignment, index, 0);
}
public WasmLoadFloat64(int alignment, WasmExpression index, int offset) {
Objects.requireNonNull(index); Objects.requireNonNull(index);
this.alignment = alignment; this.alignment = alignment;
this.index = index; this.index = index;
this.offset = offset;
} }
public int getAlignment() { public int getAlignment() {

View File

@ -24,11 +24,16 @@ public class WasmLoadInt32 extends WasmExpression implements WasmMemoryAccess {
private int offset; private int offset;
public WasmLoadInt32(int alignment, WasmExpression index, WasmInt32Subtype convertFrom) { public WasmLoadInt32(int alignment, WasmExpression index, WasmInt32Subtype convertFrom) {
this(alignment, index, convertFrom, 0);
}
public WasmLoadInt32(int alignment, WasmExpression index, WasmInt32Subtype convertFrom, int offset) {
Objects.requireNonNull(index); Objects.requireNonNull(index);
Objects.requireNonNull(convertFrom); Objects.requireNonNull(convertFrom);
this.alignment = alignment; this.alignment = alignment;
this.index = index; this.index = index;
this.convertFrom = convertFrom; this.convertFrom = convertFrom;
this.offset = offset;
} }
public int getAlignment() { public int getAlignment() {

View File

@ -24,11 +24,16 @@ public class WasmLoadInt64 extends WasmExpression implements WasmMemoryAccess {
private int offset; private int offset;
public WasmLoadInt64(int alignment, WasmExpression index, WasmInt64Subtype convertFrom) { public WasmLoadInt64(int alignment, WasmExpression index, WasmInt64Subtype convertFrom) {
this(alignment, index, convertFrom, 0);
}
public WasmLoadInt64(int alignment, WasmExpression index, WasmInt64Subtype convertFrom, int offset) {
Objects.requireNonNull(index); Objects.requireNonNull(index);
Objects.requireNonNull(convertFrom); Objects.requireNonNull(convertFrom);
this.alignment = alignment; this.alignment = alignment;
this.index = index; this.index = index;
this.convertFrom = convertFrom; this.convertFrom = convertFrom;
this.offset = offset;
} }
public int getAlignment() { public int getAlignment() {

View File

@ -18,7 +18,9 @@ package org.teavm.model;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Stream;
/** /**
* <p>Specifies a fully qualified name of a method, including its name, class name, parameter types * <p>Specifies a fully qualified name of a method, including its name, class name, parameter types
@ -162,6 +164,12 @@ public class MethodReference implements Serializable {
return desc != null ? new MethodReference(className, desc) : null; return desc != null ? new MethodReference(className, desc) : null;
} }
public static MethodReference parse(Method method) {
ValueType[] signature = Stream.concat(Arrays.stream(method.getParameterTypes()).map(ValueType::parse),
Stream.of(ValueType.parse(method.getReturnType()))).toArray(ValueType[]::new);
return new MethodReference(method.getDeclaringClass().getName(), method.getName(), signature);
}
public String signatureToString() { public String signatureToString() {
return getDescriptor().signatureToString(); return getDescriptor().signatureToString();
} }

View File

@ -112,7 +112,7 @@ public class MetadataIntrinsic implements WasmIntrinsic {
private void writeValueTo(BinaryWriter writer, WasmStringPool stringPool, Class<?> type, DataValue target, private void writeValueTo(BinaryWriter writer, WasmStringPool stringPool, Class<?> type, DataValue target,
int index, Object value) { int index, Object value) {
if (type == String.class) { if (type == String.class) {
target.setAddress(index, stringPool.getStringPointer((String) value)); target.setAddress(index, value != null ? stringPool.getStringPointer((String) value) : 0);
} else if (type == boolean.class) { } else if (type == boolean.class) {
target.setByte(index, (boolean) value ? (byte) 1 : 0); target.setByte(index, (boolean) value ? (byte) 1 : 0);
} else if (type == byte.class) { } else if (type == byte.class) {
@ -132,6 +132,8 @@ public class MetadataIntrinsic implements WasmIntrinsic {
} else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) {
int address = writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value); int address = writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value);
target.setAddress(index, address); target.setAddress(index, address);
} else if (value == null) {
target.setAddress(index, 0);
} else { } else {
throw new IllegalArgumentException("Don't know how to write resource: " + value); throw new IllegalArgumentException("Don't know how to write resource: " + value);
} }

View File

@ -34,7 +34,7 @@ public class PlatformPlugin implements TeaVMPlugin {
if (wasmHost != null) { if (wasmHost != null) {
wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getServices(), wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getServices(),
ctx.getProperties())); ctx.getProperties()));
wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource())); wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource(), ctx.getClassLoader()));
} }
host.add(new AsyncMethodProcessor()); host.add(new AsyncMethodProcessor());

View File

@ -15,19 +15,34 @@
*/ */
package org.teavm.platform.plugin; package org.teavm.platform.plugin;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.ast.InvocationExpr; import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
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.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.Resource;
public class ResourceReadIntrinsic implements WasmIntrinsic { public class ResourceReadIntrinsic implements WasmIntrinsic {
private ClassReaderSource classSource; private ClassReaderSource classSource;
private ClassLoader classLoader;
private Map<String, StructureDescriptor> typeDescriptorCache = new HashMap<>();
public ResourceReadIntrinsic(ClassReaderSource classSource) { public ResourceReadIntrinsic(ClassReaderSource classSource, ClassLoader classLoader) {
this.classSource = classSource; this.classSource = classSource;
this.classLoader = classLoader;
} }
@Override @Override
@ -37,6 +52,112 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
return null; StructureDescriptor typeDescriptor = getTypeDescriptor(invocation.getMethod().getClassName());
PropertyDescriptor property = typeDescriptor.layout.get(invocation.getMethod());
WasmExpression base = manager.generate(invocation.getArguments().get(0));
if (property.type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) property.type).getKind()) {
case BOOLEAN:
case BYTE:
return new WasmLoadInt32(1, base, WasmInt32Subtype.INT8, property.offset);
case SHORT:
return new WasmLoadInt32(2, base, WasmInt32Subtype.INT16, property.offset);
case CHARACTER:
return new WasmLoadInt32(2, base, WasmInt32Subtype.UINT16, property.offset);
case INTEGER:
return new WasmLoadInt32(4, base, WasmInt32Subtype.INT32, property.offset);
case LONG:
return new WasmLoadInt64(8, base, WasmInt64Subtype.INT64, property.offset);
case FLOAT:
return new WasmLoadFloat32(4, base, property.offset);
case DOUBLE:
return new WasmLoadFloat64(8, base, property.offset);
}
}
return new WasmLoadInt32(4, base, WasmInt32Subtype.INT32, property.offset);
}
private StructureDescriptor getTypeDescriptor(String className) {
return typeDescriptorCache.computeIfAbsent(className, n -> {
Class<?> cls;
try {
cls = Class.forName(className, false, classLoader);
} catch (ClassNotFoundException e) {
throw new AssertionError("Class " + className + " should exist", e);
}
StructureDescriptor structureDescriptor = new StructureDescriptor();
structureDescriptor.typeDescriptor = new ResourceTypeDescriptor(cls);
calculateLayout(structureDescriptor.typeDescriptor, structureDescriptor.layout);
return structureDescriptor;
});
}
private void calculateLayout(ResourceTypeDescriptor typeDescriptor,
Map<MethodReference, PropertyDescriptor> layout) {
Map<String, Integer> propertyIndexes = new HashMap<>();
List<String> propertyNames = new ArrayList<>(typeDescriptor.getPropertyTypes().keySet());
for (int i = 0; i < propertyNames.size(); ++i) {
propertyIndexes.put(propertyNames.get(i), i);
}
Method[] methods = new Method[typeDescriptor.getPropertyTypes().size()];
for (Method method : typeDescriptor.getMethods().keySet()) {
ResourceMethodDescriptor methodDescriptor = typeDescriptor.getMethods().get(method);
if (methodDescriptor.getType() == ResourceAccessorType.SETTER) {
continue;
}
String propertyName = methodDescriptor.getPropertyName();
int index = propertyIndexes.get(propertyName);
methods[index] = method;
}
int currentOffset = 0;
for (Method method : methods) {
MethodReference methodRef = MethodReference.parse(method);
ValueType propertyType = methodRef.getReturnType();
int size = getPropertySize(propertyType);
currentOffset = ((currentOffset + size - 1) / size + 1) * size;
PropertyDescriptor propertyDescriptor = new PropertyDescriptor();
propertyDescriptor.offset = currentOffset;
propertyDescriptor.type = propertyType;
layout.put(methodRef, propertyDescriptor);
currentOffset += size;
}
}
private int getPropertySize(ValueType type) {
if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) type).getKind()) {
case BOOLEAN:
case BYTE:
return 1;
case SHORT:
case CHARACTER:
return 2;
case INTEGER:
case FLOAT:
return 4;
case LONG:
case DOUBLE:
return 8;
}
}
return 4;
}
static class StructureDescriptor {
ResourceTypeDescriptor typeDescriptor;
Map<MethodReference, PropertyDescriptor> layout = new HashMap<>();
}
static class PropertyDescriptor {
int offset;
ValueType type;
} }
} }