From f716a4be62956149fe3995a283a1b0b5d9e9cb8a Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 12 Apr 2017 23:06:45 +0300 Subject: [PATCH] WASM: refactoring resource generator --- .../org/teavm/backend/wasm/WasmTarget.java | 7 +- .../wasm/generate/WasmGenerationVisitor.java | 6 + .../WasmIntrinsicFactoryContext.java | 4 +- .../wasm/intrinsics/WasmIntrinsicManager.java | 3 + .../plugin/BuildTimeResourceArray.java | 4 - .../plugin/BuildTimeResourceGetter.java | 4 - .../platform/plugin/BuildTimeResourceMap.java | 4 - .../plugin/BuildTimeResourceProxyBuilder.java | 199 ++++-------------- .../plugin/BuildTimeResourceProxyFactory.java | 4 - .../plugin/BuildTimeResourceWriterMethod.java | 4 - .../plugin/BuildTimeStaticFieldResource.java | 4 - .../platform/plugin/MetadataIntrinsic.java | 35 ++- .../MetadataProviderNativeGenerator.java | 37 +--- .../teavm/platform/plugin/MetadataUtils.java | 64 ++++++ .../teavm/platform/plugin/PlatformPlugin.java | 3 +- .../platform/plugin/ResourceAccessorType.java | 21 ++ .../plugin/ResourceMethodDescriptor.java | 34 +++ .../plugin/ResourceTypeDescriptor.java | 185 ++++++++++++++++ .../ResourceTypeDescriptorProvider.java | 24 +++ .../teavm/platform/plugin/ResourceWriter.java | 4 - .../platform/plugin/ResourceWriterHelper.java | 4 - 21 files changed, 416 insertions(+), 238 deletions(-) create mode 100644 platform/src/main/java/org/teavm/platform/plugin/MetadataUtils.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorType.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/ResourceMethodDescriptor.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptor.java create mode 100644 platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptorProvider.java diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index a9d516b3b..22af8563c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -93,7 +93,6 @@ import org.teavm.model.CallLocation; import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReader; import org.teavm.model.FieldReference; @@ -397,14 +396,14 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private class IntrinsicFactoryContext implements WasmIntrinsicFactoryContext { - private ClassReaderSource classSource; + private ListableClassReaderSource classSource; - public IntrinsicFactoryContext(ClassReaderSource classSource) { + public IntrinsicFactoryContext(ListableClassReaderSource classSource) { this.classSource = classSource; } @Override - public ClassReaderSource getClassSource() { + public ListableClassReaderSource getClassSource() { return classSource; } 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 6209bf729..903c854e3 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 @@ -107,6 +107,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt64; import org.teavm.backend.wasm.model.expression.WasmSwitch; import org.teavm.backend.wasm.model.expression.WasmUnreachable; import org.teavm.backend.wasm.render.WasmTypeInference; +import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; @@ -1534,6 +1535,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public BinaryWriter getBinaryWriter() { return binaryWriter; } + + @Override + public Diagnostics getDiagnostics() { + return context.getDiagnostics(); + } }; private WasmLocal getTemporary(WasmType type) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactoryContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactoryContext.java index c9012aef1..eb682f5df 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactoryContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactoryContext.java @@ -17,10 +17,10 @@ package org.teavm.backend.wasm.intrinsics; import java.util.Properties; import org.teavm.common.ServiceRepository; -import org.teavm.model.ClassReaderSource; +import org.teavm.model.ListableClassReaderSource; public interface WasmIntrinsicFactoryContext { - ClassReaderSource getClassSource(); + ListableClassReaderSource getClassSource(); ClassLoader getClassLoader(); 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 570e0ec4e..ce182ec00 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 @@ -18,9 +18,12 @@ package org.teavm.backend.wasm.intrinsics; import org.teavm.ast.Expr; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.diagnostics.Diagnostics; public interface WasmIntrinsicManager { WasmExpression generate(Expr expr); BinaryWriter getBinaryWriter(); + + Diagnostics getDiagnostics(); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java index 047e06bb7..451a79618 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceArray.java @@ -22,10 +22,6 @@ import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.ResourceArray; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceArray implements ResourceArray, ResourceWriter { private List data = new ArrayList<>(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java index f00aa2e27..970d1e84d 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceGetter.java @@ -15,10 +15,6 @@ */ package org.teavm.platform.plugin; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceGetter implements BuildTimeResourceMethod { private int index; diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java index 5e6c2b7ec..8b30c7e42 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceMap.java @@ -22,10 +22,6 @@ import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.platform.metadata.Resource; import org.teavm.platform.metadata.ResourceMap; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceMap implements ResourceMap, ResourceWriter { private Map data = new HashMap<>(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java index a9d734372..7196ac1fc 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyBuilder.java @@ -16,21 +16,12 @@ package org.teavm.platform.plugin; import java.lang.reflect.Method; -import java.util.*; +import java.util.HashMap; +import java.util.Map; import org.teavm.backend.javascript.codegen.SourceWriter; -import org.teavm.platform.metadata.Resource; -import org.teavm.platform.metadata.ResourceArray; -import org.teavm.platform.metadata.ResourceMap; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceProxyBuilder { private Map, BuildTimeResourceProxyFactory> factories = new HashMap<>(); - private static Set> allowedPropertyTypes = new HashSet<>(Arrays.asList( - boolean.class, byte.class, short.class, int.class, float.class, double.class, - String.class, ResourceArray.class, ResourceMap.class)); private static Map, Object> defaultValues = new HashMap<>(); static { @@ -43,41 +34,41 @@ class BuildTimeResourceProxyBuilder { } public BuildTimeResourceProxy buildProxy(Class iface) { - BuildTimeResourceProxyFactory factory = factories.get(iface); - if (factory == null) { - factory = createFactory(iface); - factories.put(iface, factory); - } - return factory.create(); + return factories.computeIfAbsent(iface, k -> createFactory(iface)).create(); } private BuildTimeResourceProxyFactory createFactory(Class iface) { - return new ProxyFactoryCreation(iface).create(); + return new ProxyFactoryCreation(new ResourceTypeDescriptor(iface)).create(); } private static class ProxyFactoryCreation { - private Class rootIface; - Map> getters = new HashMap<>(); - Map> setters = new HashMap<>(); Map methods = new HashMap<>(); private Map propertyIndexes = new HashMap<>(); private Object[] initialData; - private Map> propertyTypes = new HashMap<>(); + private ResourceTypeDescriptor descriptor; - public ProxyFactoryCreation(Class iface) { - this.rootIface = iface; + public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) { + this.descriptor = descriptor; } BuildTimeResourceProxyFactory create() { - if (!rootIface.isInterface()) { - throw new IllegalArgumentException("Error creating a new resource of type " + rootIface.getName() - + " that is not an interface"); + for (Map.Entry entry : descriptor.getMethods().entrySet()) { + Method method = entry.getKey(); + ResourceMethodDescriptor methodDescriptor = entry.getValue(); + int index = getPropertyIndex(methodDescriptor.getPropertyName()); + switch (methodDescriptor.getType()) { + case GETTER: + methods.put(method, new BuildTimeResourceGetter(index)); + break; + case SETTER: + methods.put(method, new BuildTimeResourceSetter(index)); + break; + } } - scanIface(rootIface); // Fill default values initialData = new Object[propertyIndexes.size()]; - for (Map.Entry> property : propertyTypes.entrySet()) { + for (Map.Entry> property : descriptor.getPropertyTypes().entrySet()) { String propertyName = property.getKey(); Class propertyType = property.getValue(); initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType); @@ -92,147 +83,31 @@ class BuildTimeResourceProxyBuilder { } // Create factory - String[] properties = new String[propertyIndexes.size()]; + String[] properties = new String[descriptor.getPropertyTypes().size()]; for (Map.Entry entry : propertyIndexes.entrySet()) { properties[entry.getValue()] = entry.getKey(); } methods.put(writeMethod, new BuildTimeResourceWriterMethod(properties)); + + for (Method method : ResourceTypeDescriptorProvider.class.getDeclaredMethods()) { + switch (method.getName()) { + case "getDescriptor": + methods.put(method, (proxy, args) -> descriptor); + break; + case "getValues": + methods.put(method, (proxy, args) -> initialData.clone()); + break; + case "getPropertyIndex": + methods.put(method, (proxy, args) -> propertyIndexes.getOrDefault(args[0], -1)); + break; + } + } + return new BuildTimeResourceProxyFactory(methods, initialData); } - private void scanIface(Class iface) { - if (!Resource.class.isAssignableFrom(iface)) { - throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + "." - + " This type does not implement the " + Resource.class.getName() + " interface"); - } - - // Scan methods - getters.clear(); - setters.clear(); - for (Method method : iface.getDeclaredMethods()) { - if (method.getName().startsWith("get")) { - scanGetter(method); - } else if (method.getName().startsWith("is")) { - scanBooleanGetter(method); - } else if (method.getName().startsWith("set")) { - scanSetter(method); - } else { - throwInvalidMethod(method); - } - } - - // Verify consistency of getters and setters - for (Map.Entry> property : getters.entrySet()) { - String propertyName = property.getKey(); - Class getterType = property.getValue(); - Class setterType = setters.get(propertyName); - if (setterType == null) { - throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName - + " has a getter, but does not have a setter"); - } - if (!setterType.equals(getterType)) { - throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName - + " has a getter and a setter of different types"); - } - } - for (String propertyName : setters.keySet()) { - if (!getters.containsKey(propertyName)) { - throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName - + " has a setter, but does not have a getter"); - } - } - - // Verify types of properties - for (Map.Entry> property : getters.entrySet()) { - String propertyName = property.getKey(); - Class propertyType = property.getValue(); - if (!allowedPropertyTypes.contains(propertyType)) { - if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) { - throw new IllegalArgumentException("Property " + rootIface.getName() + "." + propertyName - + " has an illegal type " + propertyType.getName()); - } - } - if (!propertyTypes.containsKey(propertyName)) { - propertyTypes.put(propertyName, propertyType); - } - } - - // Scan superinterfaces - for (Class superIface : iface.getInterfaces()) { - scanIface(superIface); - } - } - - private void throwInvalidMethod(Method method) { - throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." - + method.getName() + " is not likely to be either getter or setter"); - } - - private void scanGetter(Method method) { - String propertyName = extractPropertyName(method.getName().substring(3)); - if (propertyName == null || method.getReturnType().equals(void.class) - || method.getParameterTypes().length > 0) { - throwInvalidMethod(method); - } - if (getters.put(propertyName, method.getReturnType()) != null) { - throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." - + method.getName() + " is a duplicate getter for property " + propertyName); - } - methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName))); - } - - private void scanBooleanGetter(Method method) { - String propertyName = extractPropertyName(method.getName().substring(2)); - if (propertyName == null || !method.getReturnType().equals(boolean.class) - || method.getParameterTypes().length > 0) { - throwInvalidMethod(method); - } - if (getters.put(propertyName, method.getReturnType()) != null) { - throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." - + method.getName() + " is a duplicate getter for property " + propertyName); - } - methods.put(method, new BuildTimeResourceGetter(getPropertyIndex(propertyName))); - } - - private void scanSetter(Method method) { - String propertyName = extractPropertyName(method.getName().substring(3)); - if (propertyName == null || !method.getReturnType().equals(void.class) - || method.getParameterTypes().length != 1) { - throwInvalidMethod(method); - } - if (setters.put(propertyName, method.getParameterTypes()[0]) != null) { - throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." - + method.getName() + " is a duplicate setter for property " + propertyName); - } - methods.put(method, new BuildTimeResourceSetter(getPropertyIndex(propertyName))); - } - - private String extractPropertyName(String propertyName) { - if (propertyName.isEmpty()) { - return null; - } - char c = propertyName.charAt(0); - if (c != Character.toUpperCase(c)) { - return null; - } - if (propertyName.length() == 1) { - return propertyName.toLowerCase(); - } - c = propertyName.charAt(1); - if (c == Character.toUpperCase(c)) { - return propertyName; - } else { - return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1); - } - } - private int getPropertyIndex(String propertyName) { - Integer index = propertyIndexes.get(propertyName); - if (index == null) { - index = propertyIndexes.size(); - propertyIndexes.put(propertyName, index); - } - return index; + return propertyIndexes.computeIfAbsent(propertyName, k -> propertyIndexes.size()); } } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java index 4283bda22..1c810154c 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceProxyFactory.java @@ -19,10 +19,6 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceProxyFactory { private Map methods = new HashMap<>(); private Object[] initialData; diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java index 9437a4d01..f440c5e0a 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeResourceWriterMethod.java @@ -17,10 +17,6 @@ package org.teavm.platform.plugin; import org.teavm.backend.javascript.codegen.SourceWriter; -/** - * - * @author Alexey Andreev - */ class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod { private String[] propertyNames; diff --git a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeStaticFieldResource.java b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeStaticFieldResource.java index 68045ccd9..bfa67491c 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/BuildTimeStaticFieldResource.java +++ b/platform/src/main/java/org/teavm/platform/plugin/BuildTimeStaticFieldResource.java @@ -20,10 +20,6 @@ import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.model.FieldReference; import org.teavm.platform.metadata.StaticFieldResource; -/** - * - * @author Alexey Andreev - */ class BuildTimeStaticFieldResource implements StaticFieldResource, ResourceWriter { private FieldReference field; 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 fc00a3b0f..f7e999098 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java @@ -20,36 +20,53 @@ 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.AnnotationReader; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.common.ServiceRepository; +import org.teavm.model.CallLocation; +import org.teavm.model.ListableClassReaderSource; +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 MetadataIntrinsic implements WasmIntrinsic { - private ClassReaderSource classSource; + private ListableClassReaderSource classSource; private ClassLoader classLoader; + private ServiceRepository services; private Properties properties; - public MetadataIntrinsic(ClassReaderSource classSource, ClassLoader classLoader, Properties properties) { + public MetadataIntrinsic(ListableClassReaderSource classSource, ClassLoader classLoader, + ServiceRepository services, Properties properties) { this.classSource = classSource; this.classLoader = classLoader; + this.services = services; this.properties = properties; } @Override public boolean isApplicable(MethodReference methodReference) { - ClassReader cls = classSource.get(methodReference.getClassName()); - if (cls == null) { + MethodReader method = classSource.resolve(methodReference); + if (method == null) { return false; } - AnnotationReader annot = cls.getAnnotations().get(MetadataProvider.class.getName()); - return annot != null; + return method.getAnnotations().get(MetadataProvider.class.getName()) != null; } @Override public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + MethodReader method = classSource.resolve(invocation.getMethod()); + MetadataGenerator generator = MetadataUtils.createMetadataGenerator(classLoader, method, + new CallLocation(invocation.getMethod()), manager.getDiagnostics()); + if (generator == null) { + return new WasmInt32Constant(0); + } + + DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(classSource, + classLoader, properties, services); + Resource resource = generator.generateMetadata(metadataContext, invocation.getMethod()); + return null; } } 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 803c6ee47..59fef43df 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataProviderNativeGenerator.java @@ -16,12 +16,14 @@ package org.teavm.platform.plugin; import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; -import org.teavm.model.*; +import org.teavm.model.AnnotationReader; +import org.teavm.model.CallLocation; +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; @@ -36,33 +38,12 @@ public class MetadataProviderNativeGenerator implements Generator { AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName()); methodRef = MethodReference.parse(refAnnot.getValue("value").getString()); - // Find and instantiate metadata generator - ValueType generatorType = providerAnnot.getValue("value").getJavaClass(); - String generatorClassName = ((ValueType.Object) generatorType).getClassName(); - Class generatorClass; - try { - generatorClass = Class.forName(generatorClassName, true, context.getClassLoader()); - } catch (ClassNotFoundException e) { - context.getDiagnostics().error(new CallLocation(methodRef), "Can't find metadata provider class {{c0}}", - generatorClassName); - return; - } - Constructor cons; - try { - cons = generatorClass.getConstructor(); - } catch (NoSuchMethodException e) { - context.getDiagnostics().error(new CallLocation(methodRef), "Metadata generator {{c0}} does not have " - + "a public no-arg constructor", generatorClassName); - return; - } - MetadataGenerator generator; - try { - generator = (MetadataGenerator) cons.newInstance(); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - context.getDiagnostics().error(new CallLocation(methodRef), "Error instantiating metadata " - + "generator {{c0}}", generatorClassName); + MetadataGenerator generator = MetadataUtils.createMetadataGenerator(context.getClassLoader(), + method, new CallLocation(methodRef), context.getDiagnostics()); + if (generator == null) { return; } + DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(), context.getClassLoader(), context.getProperties(), context); diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataUtils.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataUtils.java new file mode 100644 index 000000000..15efb80ca --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataUtils.java @@ -0,0 +1,64 @@ +/* + * 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 java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.AnnotationReader; +import org.teavm.model.CallLocation; +import org.teavm.model.MethodReader; +import org.teavm.model.ValueType; +import org.teavm.platform.metadata.MetadataGenerator; +import org.teavm.platform.metadata.MetadataProvider; + +public final class MetadataUtils { + private MetadataUtils() { + } + + public static MetadataGenerator createMetadataGenerator(ClassLoader classLoader, MethodReader method, + CallLocation callLocation, Diagnostics diagnostics) { + AnnotationReader annot = method.getAnnotations().get(MetadataProvider.class.getName()); + ValueType generatorType = annot.getValue("value").getJavaClass(); + String generatorClassName = ((ValueType.Object) generatorType).getClassName(); + + Class generatorClass; + try { + generatorClass = Class.forName(generatorClassName, true, classLoader); + } catch (ClassNotFoundException e) { + diagnostics.error(callLocation, "Can't find metadata provider class {{c0}}", + generatorClassName); + return null; + } + Constructor cons; + try { + cons = generatorClass.getConstructor(); + } catch (NoSuchMethodException e) { + diagnostics.error(callLocation, "Metadata generator {{c0}} does not have " + + "a public no-arg constructor", generatorClassName); + return null; + } + MetadataGenerator generator; + try { + generator = (MetadataGenerator) cons.newInstance(); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { + diagnostics.error(callLocation, "Error instantiating metadata " + + "generator {{c0}}", generatorClassName); + return null; + } + return generator; + } +} 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 b4f230c42..20fbc4067 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -32,7 +32,8 @@ public class PlatformPlugin implements TeaVMPlugin { TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class); if (wasmHost != null) { - wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getProperties())); + wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getServices(), + ctx.getProperties())); } host.add(new AsyncMethodProcessor()); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorType.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorType.java new file mode 100644 index 000000000..87f8e0087 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorType.java @@ -0,0 +1,21 @@ +/* + * 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; + +public enum ResourceAccessorType { + GETTER, + SETTER +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceMethodDescriptor.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceMethodDescriptor.java new file mode 100644 index 000000000..4b8255400 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceMethodDescriptor.java @@ -0,0 +1,34 @@ +/* + * 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; + +public class ResourceMethodDescriptor { + private String propertyName; + private ResourceAccessorType type; + + public ResourceMethodDescriptor(String propertyName, ResourceAccessorType type) { + this.propertyName = propertyName; + this.type = type; + } + + public String getPropertyName() { + return propertyName; + } + + public ResourceAccessorType getType() { + return type; + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptor.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptor.java new file mode 100644 index 000000000..0a4d71abc --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptor.java @@ -0,0 +1,185 @@ +/* + * 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 java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +public class ResourceTypeDescriptor { + private static Set> allowedPropertyTypes = new HashSet<>(Arrays.asList( + boolean.class, byte.class, short.class, int.class, float.class, double.class, + String.class, ResourceArray.class, ResourceMap.class)); + private Class rootIface; + private Map> getters = new HashMap<>(); + private Map> setters = new HashMap<>(); + private Map methods = new LinkedHashMap<>(); + private Map> propertyTypes = new LinkedHashMap<>(); + + public ResourceTypeDescriptor(Class iface) { + this.rootIface = iface; + if (!rootIface.isInterface()) { + throw new IllegalArgumentException("Error creating a new resource of type " + rootIface.getName() + + " that is not an interface"); + } + scanIface(rootIface); + } + + public Class getRootInterface() { + return rootIface; + } + + public Map getMethods() { + return methods; + } + + public Map> getPropertyTypes() { + return propertyTypes; + } + + private void scanIface(Class iface) { + if (!Resource.class.isAssignableFrom(iface)) { + throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + "." + + " This type does not implement the " + Resource.class.getName() + " interface"); + } + + // Scan methods + getters.clear(); + setters.clear(); + for (Method method : iface.getDeclaredMethods()) { + if (method.getName().startsWith("get")) { + scanGetter(method); + } else if (method.getName().startsWith("is")) { + scanBooleanGetter(method); + } else if (method.getName().startsWith("set")) { + scanSetter(method); + } else { + throwInvalidMethod(method); + } + } + + // Verify consistency of getters and setters + for (Map.Entry> property : getters.entrySet()) { + String propertyName = property.getKey(); + Class getterType = property.getValue(); + Class setterType = setters.get(propertyName); + if (setterType == null) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a getter, but does not have a setter"); + } + if (!setterType.equals(getterType)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a getter and a setter of different types"); + } + } + for (String propertyName : setters.keySet()) { + if (!getters.containsKey(propertyName)) { + throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName + + " has a setter, but does not have a getter"); + } + } + + // Verify types of properties + for (Map.Entry> property : getters.entrySet()) { + String propertyName = property.getKey(); + Class propertyType = property.getValue(); + if (!allowedPropertyTypes.contains(propertyType)) { + if (!propertyType.isInterface() || !Resource.class.isAssignableFrom(propertyType)) { + throw new IllegalArgumentException("Property " + rootIface.getName() + "." + propertyName + + " has an illegal type " + propertyType.getName()); + } + } + if (!propertyTypes.containsKey(propertyName)) { + propertyTypes.put(propertyName, propertyType); + } + } + + // Scan superinterfaces + for (Class superIface : iface.getInterfaces()) { + scanIface(superIface); + } + } + + private void throwInvalidMethod(Method method) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is not likely to be either getter or setter"); + } + + private void scanGetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(3)); + if (propertyName == null || method.getReturnType().equals(void.class) + || method.getParameterTypes().length > 0) { + throwInvalidMethod(method); + } + if (getters.put(propertyName, method.getReturnType()) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate getter for property " + propertyName); + } + methods.put(method, new ResourceMethodDescriptor(propertyName, ResourceAccessorType.GETTER)); + } + + private void scanBooleanGetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(2)); + if (propertyName == null || !method.getReturnType().equals(boolean.class) + || method.getParameterTypes().length > 0) { + throwInvalidMethod(method); + } + if (getters.put(propertyName, method.getReturnType()) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate getter for property " + propertyName); + } + methods.put(method, new ResourceMethodDescriptor(propertyName, ResourceAccessorType.GETTER)); + } + + private void scanSetter(Method method) { + String propertyName = extractPropertyName(method.getName().substring(3)); + if (propertyName == null || !method.getReturnType().equals(void.class) + || method.getParameterTypes().length != 1) { + throwInvalidMethod(method); + } + if (setters.put(propertyName, method.getParameterTypes()[0]) != null) { + throw new IllegalArgumentException("Method " + method.getDeclaringClass().getName() + "." + + method.getName() + " is a duplicate setter for property " + propertyName); + } + methods.put(method, new ResourceMethodDescriptor(propertyName, ResourceAccessorType.SETTER)); + } + + private String extractPropertyName(String propertyName) { + if (propertyName.isEmpty()) { + return null; + } + char c = propertyName.charAt(0); + if (c != Character.toUpperCase(c)) { + return null; + } + if (propertyName.length() == 1) { + return propertyName.toLowerCase(); + } + c = propertyName.charAt(1); + if (c == Character.toUpperCase(c)) { + return propertyName; + } else { + return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1); + } + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptorProvider.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptorProvider.java new file mode 100644 index 000000000..2d89e9ce7 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceTypeDescriptorProvider.java @@ -0,0 +1,24 @@ +/* + * 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; + +public interface ResourceTypeDescriptorProvider { + ResourceTypeDescriptor getDescriptor(); + + Object[] getValues(); + + int getPropertyIndex(String propertyName); +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java index 50a0a1870..5247b185c 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriter.java @@ -18,10 +18,6 @@ package org.teavm.platform.plugin; import java.io.IOException; import org.teavm.backend.javascript.codegen.SourceWriter; -/** - * - * @author Alexey Andreev - */ public interface ResourceWriter { void write(SourceWriter writer) throws IOException; } diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java index a5a1faa66..71f95dc3a 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceWriterHelper.java @@ -18,10 +18,6 @@ package org.teavm.platform.plugin; import java.io.IOException; import org.teavm.backend.javascript.codegen.SourceWriter; -/** - * - * @author Alexey Andreev - */ final class ResourceWriterHelper { private ResourceWriterHelper() { }