diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 903c854e3..fff43f6a5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -1536,6 +1536,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return binaryWriter; } + @Override + public WasmStringPool getStringPool() { + return context.getStringPool(); + } + @Override public Diagnostics getDiagnostics() { return context.getDiagnostics(); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index ce182ec00..3b23f5f47 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.Expr; import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.diagnostics.Diagnostics; @@ -25,5 +26,7 @@ public interface WasmIntrinsicManager { BinaryWriter getBinaryWriter(); + WasmStringPool getStringPool(); + Diagnostics getDiagnostics(); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java index edfbd5f0f..ab4bb6143 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java +++ b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java @@ -29,7 +29,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder(); private ServiceRepository services; - public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, + DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader, Properties properties, ServiceRepository services) { this.classSource = classSource; this.classLoader = classLoader; @@ -55,7 +55,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { @Override public T createResource(Class resourceType) { return resourceType.cast(Proxy.newProxyInstance(classLoader, - new Class[] { resourceType, ResourceWriter.class }, + new Class[] { resourceType, ResourceWriter.class, ResourceTypeDescriptorProvider.class }, proxyBuilder.buildProxy(resourceType))); } 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 f7e999098..56118d28d 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java @@ -15,8 +15,18 @@ */ package org.teavm.platform.plugin; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Properties; import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.binary.DataPrimitives; +import org.teavm.backend.wasm.binary.DataStructure; +import org.teavm.backend.wasm.binary.DataType; +import org.teavm.backend.wasm.binary.DataValue; +import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.model.expression.WasmExpression; @@ -35,6 +45,7 @@ public class MetadataIntrinsic implements WasmIntrinsic { private ClassLoader classLoader; private ServiceRepository services; private Properties properties; + private Map resourceTypeCache = new HashMap<>(); public MetadataIntrinsic(ListableClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Properties properties) { @@ -66,7 +77,94 @@ public class MetadataIntrinsic implements WasmIntrinsic { DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(classSource, classLoader, properties, services); Resource resource = generator.generateMetadata(metadataContext, invocation.getMethod()); + int address = writeValue(manager.getBinaryWriter(), manager.getStringPool(), resource); - return null; + return new WasmInt32Constant(address); + } + + private int writeValue(BinaryWriter writer, WasmStringPool stringPool, Object value) { + if (value instanceof String) { + return stringPool.getStringPointer((String) value); + } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { + return writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value); + } else { + throw new IllegalArgumentException("Don't know how to write resource: " + value); + } + } + + private int writeResource(BinaryWriter writer, WasmStringPool stringPool, + ResourceTypeDescriptorProvider resourceType) { + DataStructure structure = getDataStructure(resourceType.getDescriptor()); + DataValue value = structure.createValue(); + int address = writer.append(value); + Object[] propertyValues = resourceType.getValues(); + + for (String propertyName : resourceType.getDescriptor().getPropertyTypes().keySet()) { + Class propertyType = resourceType.getDescriptor().getPropertyTypes().get(propertyName); + int index = resourceType.getPropertyIndex(propertyName); + Object propertyValue = propertyValues[index]; + writeValueTo(writer, stringPool, propertyType, value, index, propertyValue); + } + + return address; + } + + 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)); + } else if (type == boolean.class) { + target.setByte(index, (boolean) value ? (byte) 1 : 0); + } else if (type == byte.class) { + target.setByte(index, (byte) value); + } else if (type == short.class) { + target.setShort(index, (short) value); + } else if (type == char.class) { + target.setShort(index, (short) (char) value); + } else if (type == int.class) { + target.setInt(index, (int) value); + } else if (type == long.class) { + target.setLong(index, (long) value); + } else if (type == float.class) { + target.setFloat(index, (float) value); + } else if (type == double.class) { + target.setDouble(index, (double) value); + } else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) { + int address = writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value); + target.setAddress(index, address); + } else { + throw new IllegalArgumentException("Don't know how to write resource: " + value); + } + } + + private DataStructure getDataStructure(ResourceTypeDescriptor descriptor) { + return resourceTypeCache.computeIfAbsent(descriptor, t -> { + List propertyNames = new ArrayList<>(descriptor.getPropertyTypes().keySet()); + DataType[] propertyDataTypes = new DataType[propertyNames.size()]; + for (int i = 0; i < propertyNames.size(); i++) { + String propertyName = propertyNames.get(i); + propertyDataTypes[i] = getDataType(descriptor.getPropertyTypes().get(propertyName)); + } + + return new DataStructure((byte) 4, propertyDataTypes); + }); + } + + private static DataType getDataType(Class cls) { + if (cls == boolean.class || cls == byte.class) { + return DataPrimitives.BYTE; + } else if (cls == short.class || cls == char.class) { + return DataPrimitives.SHORT; + } else if (cls == int.class) { + return DataPrimitives.INT; + } else if (cls == float.class) { + return DataPrimitives.FLOAT; + } else if (cls == long.class) { + return DataPrimitives.LONG; + } else if (cls == double.class) { + return DataPrimitives.DOUBLE; + } else { + return DataPrimitives.ADDRESS; + } } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java index 59fef43df..fde618e74 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java @@ -25,7 +25,6 @@ import org.teavm.model.ClassReader; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.platform.metadata.MetadataGenerator; -import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.Resource; public class MetadataProviderNativeGenerator implements Generator { @@ -33,7 +32,6 @@ public class MetadataProviderNativeGenerator implements Generator { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { ClassReader cls = context.getClassSource().get(methodRef.getClassName()); MethodReader method = cls.getMethod(methodRef.getDescriptor()); - AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName()); AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName()); methodRef = MethodReference.parse(refAnnot.getValue("value").getString()); 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 20fbc4067..d1dc28ec2 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -34,6 +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())); } 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 new file mode 100644 index 000000000..bc2acca2c --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceReadIntrinsic.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 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.platform.plugin; + +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.model.ClassReaderSource; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.Resource; + +public class ResourceReadIntrinsic implements WasmIntrinsic { + private ClassReaderSource classSource; + + public ResourceReadIntrinsic(ClassReaderSource classSource) { + this.classSource = classSource; + } + + @Override + public boolean isApplicable(MethodReference methodReference) { + return classSource.isSuperType(Resource.class.getTypeName(), methodReference.getClassName()).orElse(false); + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + return null; + } +}