mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -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.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -397,14 +396,14 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IntrinsicFactoryContext implements WasmIntrinsicFactoryContext {
|
private class IntrinsicFactoryContext implements WasmIntrinsicFactoryContext {
|
||||||
private ClassReaderSource classSource;
|
private ListableClassReaderSource classSource;
|
||||||
|
|
||||||
public IntrinsicFactoryContext(ClassReaderSource classSource) {
|
public IntrinsicFactoryContext(ListableClassReaderSource classSource) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassReaderSource getClassSource() {
|
public ListableClassReaderSource getClassSource() {
|
||||||
return classSource;
|
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.WasmSwitch;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||||
import org.teavm.backend.wasm.render.WasmTypeInference;
|
import org.teavm.backend.wasm.render.WasmTypeInference;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -1534,6 +1535,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
public BinaryWriter getBinaryWriter() {
|
public BinaryWriter getBinaryWriter() {
|
||||||
return binaryWriter;
|
return binaryWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Diagnostics getDiagnostics() {
|
||||||
|
return context.getDiagnostics();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private WasmLocal getTemporary(WasmType type) {
|
private WasmLocal getTemporary(WasmType type) {
|
||||||
|
|
|
@ -17,10 +17,10 @@ package org.teavm.backend.wasm.intrinsics;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
|
||||||
public interface WasmIntrinsicFactoryContext {
|
public interface WasmIntrinsicFactoryContext {
|
||||||
ClassReaderSource getClassSource();
|
ListableClassReaderSource getClassSource();
|
||||||
|
|
||||||
ClassLoader getClassLoader();
|
ClassLoader getClassLoader();
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,12 @@ package org.teavm.backend.wasm.intrinsics;
|
||||||
import org.teavm.ast.Expr;
|
import org.teavm.ast.Expr;
|
||||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
|
||||||
public interface WasmIntrinsicManager {
|
public interface WasmIntrinsicManager {
|
||||||
WasmExpression generate(Expr expr);
|
WasmExpression generate(Expr expr);
|
||||||
|
|
||||||
BinaryWriter getBinaryWriter();
|
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.Resource;
|
||||||
import org.teavm.platform.metadata.ResourceArray;
|
import org.teavm.platform.metadata.ResourceArray;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeResourceArray<T extends Resource> implements ResourceArray<T>, ResourceWriter {
|
class BuildTimeResourceArray<T extends Resource> implements ResourceArray<T>, ResourceWriter {
|
||||||
private List<T> data = new ArrayList<>();
|
private List<T> data = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeResourceGetter implements BuildTimeResourceMethod {
|
class BuildTimeResourceGetter implements BuildTimeResourceMethod {
|
||||||
private int index;
|
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.Resource;
|
||||||
import org.teavm.platform.metadata.ResourceMap;
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeResourceMap<T extends Resource> implements ResourceMap<T>, ResourceWriter {
|
class BuildTimeResourceMap<T extends Resource> implements ResourceMap<T>, ResourceWriter {
|
||||||
private Map<String, T> data = new HashMap<>();
|
private Map<String, T> data = new HashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -16,21 +16,12 @@
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
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.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 {
|
class BuildTimeResourceProxyBuilder {
|
||||||
private Map<Class<?>, BuildTimeResourceProxyFactory> factories = new HashMap<>();
|
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<>();
|
private static Map<Class<?>, Object> defaultValues = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -43,41 +34,41 @@ class BuildTimeResourceProxyBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuildTimeResourceProxy buildProxy(Class<?> iface) {
|
public BuildTimeResourceProxy buildProxy(Class<?> iface) {
|
||||||
BuildTimeResourceProxyFactory factory = factories.get(iface);
|
return factories.computeIfAbsent(iface, k -> createFactory(iface)).create();
|
||||||
if (factory == null) {
|
|
||||||
factory = createFactory(iface);
|
|
||||||
factories.put(iface, factory);
|
|
||||||
}
|
|
||||||
return factory.create();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BuildTimeResourceProxyFactory createFactory(Class<?> iface) {
|
private BuildTimeResourceProxyFactory createFactory(Class<?> iface) {
|
||||||
return new ProxyFactoryCreation(iface).create();
|
return new ProxyFactoryCreation(new ResourceTypeDescriptor(iface)).create();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ProxyFactoryCreation {
|
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<>();
|
Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
|
||||||
private Map<String, Integer> propertyIndexes = new HashMap<>();
|
private Map<String, Integer> propertyIndexes = new HashMap<>();
|
||||||
private Object[] initialData;
|
private Object[] initialData;
|
||||||
private Map<String, Class<?>> propertyTypes = new HashMap<>();
|
private ResourceTypeDescriptor descriptor;
|
||||||
|
|
||||||
public ProxyFactoryCreation(Class<?> iface) {
|
public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
|
||||||
this.rootIface = iface;
|
this.descriptor = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildTimeResourceProxyFactory create() {
|
BuildTimeResourceProxyFactory create() {
|
||||||
if (!rootIface.isInterface()) {
|
for (Map.Entry<Method, ResourceMethodDescriptor> entry : descriptor.getMethods().entrySet()) {
|
||||||
throw new IllegalArgumentException("Error creating a new resource of type " + rootIface.getName()
|
Method method = entry.getKey();
|
||||||
+ " that is not an interface");
|
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
|
// Fill default values
|
||||||
initialData = new Object[propertyIndexes.size()];
|
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();
|
String propertyName = property.getKey();
|
||||||
Class<?> propertyType = property.getValue();
|
Class<?> propertyType = property.getValue();
|
||||||
initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
|
initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
|
||||||
|
@ -92,147 +83,31 @@ class BuildTimeResourceProxyBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create factory
|
// Create factory
|
||||||
String[] properties = new String[propertyIndexes.size()];
|
String[] properties = new String[descriptor.getPropertyTypes().size()];
|
||||||
for (Map.Entry<String, Integer> entry : propertyIndexes.entrySet()) {
|
for (Map.Entry<String, Integer> entry : propertyIndexes.entrySet()) {
|
||||||
properties[entry.getValue()] = entry.getKey();
|
properties[entry.getValue()] = entry.getKey();
|
||||||
}
|
}
|
||||||
methods.put(writeMethod, new BuildTimeResourceWriterMethod(properties));
|
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);
|
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) {
|
private int getPropertyIndex(String propertyName) {
|
||||||
Integer index = propertyIndexes.get(propertyName);
|
return propertyIndexes.computeIfAbsent(propertyName, k -> propertyIndexes.size());
|
||||||
if (index == null) {
|
|
||||||
index = propertyIndexes.size();
|
|
||||||
propertyIndexes.put(propertyName, index);
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,6 @@ import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeResourceProxyFactory {
|
class BuildTimeResourceProxyFactory {
|
||||||
private Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
|
private Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
|
||||||
private Object[] initialData;
|
private Object[] initialData;
|
||||||
|
|
|
@ -17,10 +17,6 @@ package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
|
class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
|
||||||
private String[] propertyNames;
|
private String[] propertyNames;
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,6 @@ import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.platform.metadata.StaticFieldResource;
|
import org.teavm.platform.metadata.StaticFieldResource;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
class BuildTimeStaticFieldResource implements StaticFieldResource, ResourceWriter {
|
class BuildTimeStaticFieldResource implements StaticFieldResource, ResourceWriter {
|
||||||
private FieldReference field;
|
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.WasmIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.platform.metadata.MetadataGenerator;
|
||||||
import org.teavm.platform.metadata.MetadataProvider;
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
|
import org.teavm.platform.metadata.Resource;
|
||||||
|
|
||||||
public class MetadataIntrinsic implements WasmIntrinsic {
|
public class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
private ClassReaderSource classSource;
|
private ListableClassReaderSource classSource;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
|
private ServiceRepository services;
|
||||||
private Properties properties;
|
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.classSource = classSource;
|
||||||
this.classLoader = classLoader;
|
this.classLoader = classLoader;
|
||||||
|
this.services = services;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isApplicable(MethodReference methodReference) {
|
public boolean isApplicable(MethodReference methodReference) {
|
||||||
ClassReader cls = classSource.get(methodReference.getClassName());
|
MethodReader method = classSource.resolve(methodReference);
|
||||||
if (cls == null) {
|
if (method == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotationReader annot = cls.getAnnotations().get(MetadataProvider.class.getName());
|
return method.getAnnotations().get(MetadataProvider.class.getName()) != null;
|
||||||
return annot != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,14 @@
|
||||||
package org.teavm.platform.plugin;
|
package org.teavm.platform.plugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
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.MetadataGenerator;
|
||||||
import org.teavm.platform.metadata.MetadataProvider;
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
import org.teavm.platform.metadata.Resource;
|
import org.teavm.platform.metadata.Resource;
|
||||||
|
@ -36,33 +38,12 @@ public class MetadataProviderNativeGenerator implements Generator {
|
||||||
AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName());
|
AnnotationReader refAnnot = method.getAnnotations().get(MetadataProviderRef.class.getName());
|
||||||
methodRef = MethodReference.parse(refAnnot.getValue("value").getString());
|
methodRef = MethodReference.parse(refAnnot.getValue("value").getString());
|
||||||
|
|
||||||
// Find and instantiate metadata generator
|
MetadataGenerator generator = MetadataUtils.createMetadataGenerator(context.getClassLoader(),
|
||||||
ValueType generatorType = providerAnnot.getValue("value").getJavaClass();
|
method, new CallLocation(methodRef), context.getDiagnostics());
|
||||||
String generatorClassName = ((ValueType.Object) generatorType).getClassName();
|
if (generator == null) {
|
||||||
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);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
|
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
|
||||||
context.getClassLoader(), context.getProperties(), context);
|
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);
|
TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class);
|
||||||
if (wasmHost != null) {
|
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());
|
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 java.io.IOException;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
public interface ResourceWriter {
|
public interface ResourceWriter {
|
||||||
void write(SourceWriter writer) throws IOException;
|
void write(SourceWriter writer) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,6 @@ package org.teavm.platform.plugin;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
*/
|
|
||||||
final class ResourceWriterHelper {
|
final class ResourceWriterHelper {
|
||||||
private ResourceWriterHelper() {
|
private ResourceWriterHelper() {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user