mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
WASM: refactoring resource generator
This commit is contained in:
parent
f1da7a417e
commit
f716a4be62
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<T extends Resource> implements ResourceArray<T>, ResourceWriter {
|
||||
private List<T> data = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
*/
|
||||
package org.teavm.platform.plugin;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class BuildTimeResourceGetter implements BuildTimeResourceMethod {
|
||||
private int index;
|
||||
|
||||
|
|
|
@ -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<T extends Resource> implements ResourceMap<T>, ResourceWriter {
|
||||
private Map<String, T> data = new HashMap<>();
|
||||
|
||||
|
|
|
@ -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<Class<?>, BuildTimeResourceProxyFactory> factories = new HashMap<>();
|
||||
private static Set<Class<?>> 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<Class<?>, 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<String, Class<?>> getters = new HashMap<>();
|
||||
Map<String, Class<?>> setters = new HashMap<>();
|
||||
Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
|
||||
private Map<String, Integer> propertyIndexes = new HashMap<>();
|
||||
private Object[] initialData;
|
||||
private Map<String, Class<?>> 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<Method, ResourceMethodDescriptor> 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<String, Class<?>> property : propertyTypes.entrySet()) {
|
||||
for (Map.Entry<String, Class<?>> 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<String, Integer> 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<String, Class<?>> 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<String, Class<?>> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,6 @@ import java.lang.reflect.Method;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class BuildTimeResourceProxyFactory {
|
||||
private Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
|
||||
private Object[] initialData;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Class<?>> 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<String, Class<?>> getters = new HashMap<>();
|
||||
private Map<String, Class<?>> setters = new HashMap<>();
|
||||
private Map<Method, ResourceMethodDescriptor> methods = new LinkedHashMap<>();
|
||||
private Map<String, Class<?>> 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<Method, ResourceMethodDescriptor> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public Map<String, Class<?>> 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<String, Class<?>> 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<String, Class<?>> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user