diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 43c9e673f..7a082b802 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -26,6 +26,7 @@ public final class Example { } public static void main(String[] args) { + System.out.println(Integer.parseInt("123")); testFibonacci(); testClasses(); testVirtualCall(); @@ -38,7 +39,7 @@ public final class Example { testArrayIsObject(); testIsAssignableFrom(); testExceptions(); - testBigInteger(); + //testBigInteger(); testGC(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat32.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat32.java index 42e533cfc..82ed5337d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat32.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat32.java @@ -23,9 +23,14 @@ public class WasmLoadFloat32 extends WasmExpression implements WasmMemoryAccess private int offset; public WasmLoadFloat32(int alignment, WasmExpression index) { + this(alignment, index, 0); + } + + public WasmLoadFloat32(int alignment, WasmExpression index, int offset) { Objects.requireNonNull(index); this.alignment = alignment; this.index = index; + this.offset = offset; } public int getAlignment() { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat64.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat64.java index 29ce11d98..926f46aa7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat64.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadFloat64.java @@ -23,9 +23,14 @@ public class WasmLoadFloat64 extends WasmExpression implements WasmMemoryAccess private int offset; public WasmLoadFloat64(int alignment, WasmExpression index) { + this(alignment, index, 0); + } + + public WasmLoadFloat64(int alignment, WasmExpression index, int offset) { Objects.requireNonNull(index); this.alignment = alignment; this.index = index; + this.offset = offset; } public int getAlignment() { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt32.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt32.java index ed2c84a00..36da4c4a2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt32.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt32.java @@ -24,11 +24,16 @@ public class WasmLoadInt32 extends WasmExpression implements WasmMemoryAccess { private int offset; 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(convertFrom); this.alignment = alignment; this.index = index; this.convertFrom = convertFrom; + this.offset = offset; } public int getAlignment() { diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt64.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt64.java index 3b998ab0f..1bd729adc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt64.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmLoadInt64.java @@ -24,11 +24,16 @@ public class WasmLoadInt64 extends WasmExpression implements WasmMemoryAccess { private int offset; 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(convertFrom); this.alignment = alignment; this.index = index; this.convertFrom = convertFrom; + this.offset = offset; } public int getAlignment() { diff --git a/core/src/main/java/org/teavm/model/MethodReference.java b/core/src/main/java/org/teavm/model/MethodReference.java index 76abd8300..9ecb4c1ba 100644 --- a/core/src/main/java/org/teavm/model/MethodReference.java +++ b/core/src/main/java/org/teavm/model/MethodReference.java @@ -18,7 +18,9 @@ package org.teavm.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import java.io.Serializable; +import java.lang.reflect.Method; import java.util.Arrays; +import java.util.stream.Stream; /** *

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; } + 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() { return getDescriptor().signatureToString(); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java index 56118d28d..da9f32cd5 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java @@ -112,7 +112,7 @@ public class MetadataIntrinsic implements WasmIntrinsic { private void writeValueTo(BinaryWriter writer, WasmStringPool stringPool, Class type, DataValue target, int index, Object value) { 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) { target.setByte(index, (boolean) value ? (byte) 1 : 0); } else if (type == byte.class) { @@ -132,6 +132,8 @@ public class MetadataIntrinsic implements WasmIntrinsic { } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { int address = writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value); target.setAddress(index, address); + } else if (value == null) { + target.setAddress(index, 0); } else { throw new IllegalArgumentException("Don't know how to write resource: " + value); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java index d1dc28ec2..44a26dfdb 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -34,7 +34,7 @@ public class PlatformPlugin implements TeaVMPlugin { if (wasmHost != null) { wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getServices(), ctx.getProperties())); - wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource())); + wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource(), ctx.getClassLoader())); } host.add(new AsyncMethodProcessor()); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java index bc2acca2c..1a64ec14e 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java @@ -15,19 +15,34 @@ */ 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.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; 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.MethodReference; +import org.teavm.model.ValueType; import org.teavm.platform.metadata.Resource; public class ResourceReadIntrinsic implements WasmIntrinsic { private ClassReaderSource classSource; + private ClassLoader classLoader; + private Map typeDescriptorCache = new HashMap<>(); - public ResourceReadIntrinsic(ClassReaderSource classSource) { + public ResourceReadIntrinsic(ClassReaderSource classSource, ClassLoader classLoader) { this.classSource = classSource; + this.classLoader = classLoader; } @Override @@ -37,6 +52,112 @@ public class ResourceReadIntrinsic implements WasmIntrinsic { @Override 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 layout) { + Map propertyIndexes = new HashMap<>(); + List 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 layout = new HashMap<>(); + } + + static class PropertyDescriptor { + int offset; + ValueType type; } }