Adds ResourceAccessor and its implementation. ResourceAccessor is to

access properties and methods of resource in rumtime.
This commit is contained in:
konsoletyper 2014-06-05 15:18:40 +04:00
parent 10a8e0261a
commit 3cf7991cfb
14 changed files with 527 additions and 8 deletions

View File

@ -44,6 +44,7 @@ public class Decompiler {
private RangeTree.Node parentNode; private RangeTree.Node parentNode;
private FiniteExecutor executor; private FiniteExecutor executor;
private Map<MethodReference, Generator> generators = new HashMap<>(); private Map<MethodReference, Generator> generators = new HashMap<>();
private Set<MethodReference> methodsToPass = new HashSet<>();
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) { public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
this.classSource = classSource; this.classSource = classSource;
@ -96,6 +97,10 @@ public class Decompiler {
generators.put(method, generator); generators.put(method, generator);
} }
public void addMethodToPass(MethodReference method) {
methodsToPass.add(method);
}
private void orderClasses(String className, Set<String> visited, List<String> order) { private void orderClasses(String className, Set<String> visited, List<String> order) {
if (!visited.add(className)) { if (!visited.add(className)) {
return; return;
@ -125,7 +130,8 @@ public class Decompiler {
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) { if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
continue; continue;
} }
if (method.getAnnotations().get(InjectedBy.class.getName()) != null) { if (method.getAnnotations().get(InjectedBy.class.getName()) != null ||
methodsToPass.contains(method)) {
continue; continue;
} }
MethodNode methodNode = decompile(method); MethodNode methodNode = decompile(method);
@ -140,8 +146,8 @@ public class Decompiler {
} }
public MethodNode decompile(MethodHolder method) { public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) :
decompileNative(method) : decompileRegular(method); decompileRegular(method);
} }
public NativeMethodNode decompileNative(MethodHolder method) { public NativeMethodNode decompileNative(MethodHolder method) {

View File

@ -53,6 +53,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
} }
public void addInjector(MethodReference method, Injector injector) {
injectorMap.put(method, new InjectorHolder(injector));
}
public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader) { public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader) {
this.naming = writer.getNaming(); this.naming = writer.getNaming();
this.writer = writer; this.writer = writer;

View File

@ -34,6 +34,17 @@ public class MethodDescriptor {
this.signature = Arrays.copyOf(signature, signature.length); this.signature = Arrays.copyOf(signature, signature.length);
} }
public MethodDescriptor(String name, Class<?>... signature) {
if (signature.length < 1) {
throw new IllegalArgumentException("Signature must be at least 1 element length");
}
this.name = name;
this.signature = new ValueType[signature.length];
for (int i = 0; i < signature.length; ++i) {
this.signature[i] = ValueType.parse(signature[i]);
}
}
public String getName() { public String getName() {
return name; return name;
} }

View File

@ -55,6 +55,10 @@ public class MethodReference {
this(className, new MethodDescriptor(name, signature)); this(className, new MethodDescriptor(name, signature));
} }
public MethodReference(Class<?> cls, String name, Class<?>... signature) {
this(cls.getName(), new MethodDescriptor(name, signature));
}
public String getClassName() { public String getClassName() {
return className; return className;
} }

View File

@ -16,7 +16,9 @@
package org.teavm.model; package org.teavm.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
@ -25,6 +27,7 @@ import java.util.List;
*/ */
public abstract class ValueType { public abstract class ValueType {
volatile String reprCache; volatile String reprCache;
private static final Map<Class<?>, ValueType> primitiveMap = new HashMap<>();
private ValueType() { private ValueType() {
} }
@ -166,6 +169,19 @@ public abstract class ValueType {
public static final Null NULL = new Null(); public static final Null NULL = new Null();
static {
primitiveMap.put(boolean.class, BOOLEAN);
primitiveMap.put(char.class, CHARACTER);
primitiveMap.put(byte.class, BYTE);
primitiveMap.put(short.class, SHORT);
primitiveMap.put(int.class, INTEGER);
primitiveMap.put(long.class, LONG);
primitiveMap.put(float.class, FLOAT);
primitiveMap.put(double.class, DOUBLE);
primitiveMap.put(void.class, VOID);
}
public static ValueType object(String cls) { public static ValueType object(String cls) {
return new Object(cls); return new Object(cls);
} }
@ -267,6 +283,16 @@ public abstract class ValueType {
} }
} }
public static ValueType parse(Class<?> cls) {
if (cls.isPrimitive()) {
return primitiveMap.get(cls);
} else if (cls.getComponentType() != null) {
return ValueType.arrayOf(ValueType.parse(cls.getComponentType()));
} else {
return ValueType.object(cls.getName());
}
}
@Override @Override
public int hashCode() { public int hashCode() {
return toString().hashCode(); return toString().hashCode();

View File

@ -20,9 +20,12 @@ import java.util.*;
import org.teavm.codegen.*; import org.teavm.codegen.*;
import org.teavm.common.FiniteExecutor; import org.teavm.common.FiniteExecutor;
import org.teavm.dependency.*; import org.teavm.dependency.*;
import org.teavm.javascript.*; import org.teavm.javascript.Decompiler;
import org.teavm.javascript.Renderer;
import org.teavm.javascript.RenderingException;
import org.teavm.javascript.ast.ClassNode; import org.teavm.javascript.ast.ClassNode;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.Injector;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.util.ListingBuilder; import org.teavm.model.util.ListingBuilder;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
@ -73,6 +76,7 @@ public class TeaVM implements TeaVMHost {
private Map<String, TeaVMEntryPoint> entryPoints = new HashMap<>(); private Map<String, TeaVMEntryPoint> entryPoints = new HashMap<>();
private Map<String, String> exportedClasses = new HashMap<>(); private Map<String, String> exportedClasses = new HashMap<>();
private Map<MethodReference, Generator> methodGenerators = new HashMap<>(); private Map<MethodReference, Generator> methodGenerators = new HashMap<>();
private Map<MethodReference, Injector> methodInjectors = new HashMap<>();
private List<RendererListener> rendererListeners = new ArrayList<>(); private List<RendererListener> rendererListeners = new ArrayList<>();
private Properties properties = new Properties(); private Properties properties = new Properties();
@ -98,6 +102,11 @@ public class TeaVM implements TeaVMHost {
methodGenerators.put(methodRef, generator); methodGenerators.put(methodRef, generator);
} }
@Override
public void add(MethodReference methodRef, Injector injector) {
methodInjectors.put(methodRef, injector);
}
@Override @Override
public void add(RendererListener listener) { public void add(RendererListener listener) {
rendererListeners.add(listener); rendererListeners.add(listener);
@ -315,6 +324,9 @@ public class TeaVM implements TeaVMHost {
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) { for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
decompiler.addGenerator(entry.getKey(), entry.getValue()); decompiler.addGenerator(entry.getKey(), entry.getValue());
} }
for (MethodReference injectedMethod : methodInjectors.keySet()) {
decompiler.addMethodToPass(injectedMethod);
}
List<ClassNode> clsNodes = decompiler.decompile(classSet.getClassNames()); List<ClassNode> clsNodes = decompiler.decompile(classSet.getClassNames());
// Render // Render
@ -324,6 +336,9 @@ public class TeaVM implements TeaVMHost {
builder.setMinified(minifying); builder.setMinified(minifying);
SourceWriter sourceWriter = builder.build(writer); SourceWriter sourceWriter = builder.build(writer);
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader); Renderer renderer = new Renderer(sourceWriter, classSet, classLoader);
for (Map.Entry<MethodReference, Injector> entry : methodInjectors.entrySet()) {
renderer.addInjector(entry.getKey(), entry.getValue());
}
try { try {
for (RendererListener listener : rendererListeners) { for (RendererListener listener : rendererListeners) {
listener.begin(renderer, target); listener.begin(renderer, target);

View File

@ -18,6 +18,7 @@ package org.teavm.vm.spi;
import java.util.Properties; import java.util.Properties;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
import org.teavm.javascript.ni.Generator; import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.Injector;
import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.vm.TeaVM; import org.teavm.vm.TeaVM;
@ -36,6 +37,8 @@ public interface TeaVMHost {
void add(MethodReference methodRef, Generator generator); void add(MethodReference methodRef, Generator generator);
void add(MethodReference methodRef, Injector injector);
void add(RendererListener listener); void add(RendererListener listener);
/** /**

View File

@ -15,10 +15,7 @@
*/ */
package org.teavm.platform.metadata; package org.teavm.platform.metadata;
import java.lang.annotation.ElementType; import java.lang.annotation.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* <p>Marks a valid <b>resource interface</b>. Resource interface is an interface, that has get* and set* methods, * <p>Marks a valid <b>resource interface</b>. Resource interface is an interface, that has get* and set* methods,
@ -33,5 +30,7 @@ import java.lang.annotation.Target;
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@Inherited
@Documented
public @interface Resource { public @interface Resource {
} }

View File

@ -26,5 +26,8 @@ public class PlatformPlugin implements TeaVMPlugin {
@Override @Override
public void install(TeaVMHost host) { public void install(TeaVMHost host) {
host.add(new MetadataProviderTransformer()); host.add(new MetadataProviderTransformer());
host.add(new ResourceTransformer());
host.add(new ResourceAccessorTransformer(host));
host.add(new ResourceAccessorDependencyListener());
} }
} }

View File

@ -0,0 +1,86 @@
/*
* Copyright 2014 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;
/**
*
* @author Alexey Andreev
*/
final class ResourceAccessor {
public static native Object get(Object obj, String propertyName);
public static native void put(Object obj, String propertyName, Object elem);
public static native Object get(Object obj, int index);
public static native Object add(Object obj, Object elem);
public static native boolean has(Object obj, String key);
public static native boolean size(Object obj);
public static native int castToInt(Object obj);
public static native Integer castToIntWrapper(Object obj);
public static native short castToShort(Object obj);
public static native Short castToShortWrapper(Object obj);
public static native byte castToByte(Object obj);
public static native Byte castToByteWrapper(Object obj);
public static native boolean castToBoolean(Object obj);
public static native Boolean castToBooleanWrapper(Object obj);
public static native float castToFloat(Object obj);
public static native Float castToFloatWrapper(Object obj);
public static native double castToDouble(Object obj);
public static native Double castToDoubleWrapper(Object obj);
public static native String castToString(Object obj);
public static native Object castFromInt(int value);
public static native Object castFromIntWrapper(Integer value);
public static native Object castFromShort(short value);
public static native Object castFromShortWrapper(Short value);
public static native Object castFromByte(byte value);
public static native Object castFromByteWrapper(Byte value);
public static native Object castFromBoolean(boolean value);
public static native Object castFromBooleanWrapper(Boolean value);
public static native Object castFromFloat(float value);
public static native Object castFromFloatWrapper(Float value);
public static native Object castFromDouble(double value);
public static native Object castFromDoubleWrapper(Double value);
public static native Object castFromString(String value);
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.platform.plugin;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyListener;
import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
class ResourceAccessorDependencyListener implements DependencyListener {
@Override
public void started(DependencyAgent agent) {
}
@Override
public void classAchieved(DependencyAgent agent, String className) {
}
@Override
public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) {
case "castToIntWrapper":
castToWrapper(agent, method, Integer.class, int.class);
break;
case "castToShortWrapper":
castToWrapper(agent, method, Short.class, short.class);
break;
case "castToByteWrapper":
castToWrapper(agent, method, Byte.class, byte.class);
break;
case "castToBooleanWrapper":
castToWrapper(agent, method, Boolean.class, boolean.class);
break;
case "castToFloatWrapper":
castToWrapper(agent, method, Float.class, float.class);
break;
case "castToDoubleWrapper":
castToWrapper(agent, method, Double.class, double.class);
break;
case "castFromIntegerWrapper":
castFromWrapper(agent, method, Integer.class, int.class);
break;
case "castFromShortWrapper":
castFromWrapper(agent, method, Short.class, short.class);
break;
case "castFromByteWrapper":
castFromWrapper(agent, method, Byte.class, byte.class);
break;
case "castFromBooleanWrapper":
castFromWrapper(agent, method, Boolean.class, boolean.class);
break;
case "castFromFloatWrapper":
castFromWrapper(agent, method, Float.class, float.class);
break;
case "castFromDoubleWrapper":
castFromWrapper(agent, method, Double.class, double.class);
break;
case "castToString":
method.getResult().propagate("java.lang.String");
break;
}
}
private void castToWrapper(DependencyAgent agent, MethodDependency method, Class<?> wrapper, Class<?> primitive) {
method.getResult().propagate(wrapper.getName());
agent.linkMethod(new MethodReference(wrapper, "valueOf", primitive, wrapper), method.getStack()).use();
}
private void castFromWrapper(DependencyAgent agent, MethodDependency method, Class<?> wrapper, Class<?> primitive) {
String primitiveName = primitive.getName();
agent.linkMethod(new MethodReference(wrapper, primitiveName + "Value", primitive), method.getStack()).use();
}
@Override
public void fieldAchieved(DependencyAgent agent, FieldDependency field) {
}
}

View File

@ -0,0 +1,193 @@
/*
* Copyright 2014 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.io.IOException;
import org.teavm.javascript.ast.ConstantExpr;
import org.teavm.javascript.ast.Expr;
import org.teavm.javascript.ni.Injector;
import org.teavm.javascript.ni.InjectorContext;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
/**
*
* @author Alexey Andreev
*/
class ResourceAccessorGenerator implements Injector {
@Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "get":
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
context.writeExpr(context.getArgument(1));
context.getWriter().append('[');
context.writeExpr(context.getArgument(2));
context.getWriter().append(']');
} else {
context.writeExpr(context.getArgument(1));
writePropertyAccessor(context, context.getArgument(2));
}
break;
case "put":
context.getWriter().append('(');
if (methodRef.getDescriptor().parameterType(1) == ValueType.INTEGER) {
context.writeExpr(context.getArgument(1));
context.getWriter().append('[');
context.writeExpr(context.getArgument(2));
} else {
context.writeExpr(context.getArgument(1));
writePropertyAccessor(context, context.getArgument(2));
}
context.getWriter().append(']').ws().append('=').ws();
context.writeExpr(context.getArgument(3));
context.getWriter().append(')');
break;
case "add":
context.writeExpr(context.getArgument(1));
context.getWriter().append(".push(");
context.writeExpr(context.getArgument(2));
context.getWriter().append(')');
break;
case "has":
context.writeExpr(context.getArgument(1));
context.getWriter().append(".hasOwnProperty(");
writeStringExpr(context, context.getArgument(2));
context.getWriter().append(')');
break;
case "size":
context.writeExpr(context.getArgument(1));
context.getWriter().append(".length");
break;
case "castToInt":
case "castToShort":
case "castToByte":
case "castToBoolean":
case "castToFloat":
case "castToDouble":
case "castFromInt":
case "castFromShort":
case "castFromByte":
case "castFromBoolean":
case "castFromFloat":
case "castFromDouble":
context.writeExpr(context.getArgument(1));
break;
case "castToIntWrapper":
castToWrapper(context, Integer.class, int.class);
break;
case "castToShortWrapper":
castToWrapper(context, Short.class, short.class);
break;
case "castToByteWrapper":
castToWrapper(context, Byte.class, byte.class);
break;
case "castToBooleanWrapper":
castToWrapper(context, Boolean.class, boolean.class);
break;
case "castToFloatWrapper":
castToWrapper(context, Float.class, float.class);
break;
case "castToDoubleWrapper":
castToWrapper(context, Double.class, double.class);
break;
case "castFromIntWrapper":
castFromWrapper(context, Integer.class, int.class);
break;
case "castFromShortWrapper":
castFromWrapper(context, Short.class, short.class);
break;
case "castFromByteWrapper":
castFromWrapper(context, Byte.class, byte.class);
break;
case "castFromBooleanWrapper":
castFromWrapper(context, Boolean.class, boolean.class);
break;
case "castFromFloatWrapper":
castFromWrapper(context, Float.class, float.class);
break;
case "castFromDoubleWrapper":
castFromWrapper(context, Double.class, double.class);
break;
case "castToString":
context.getWriter().append("$rt_str(");
context.writeExpr(context.getArgument(1));
context.getWriter().append(")");
break;
case "castFromString":
context.getWriter().append("$rt_ustr(");
context.writeExpr(context.getArgument(1));
context.getWriter().append(")");
break;
}
}
private void castToWrapper(InjectorContext context, Class<?> wrapper, Class<?> primitive) throws IOException {
context.getWriter().appendMethodBody(new MethodReference(wrapper, "valueOf", primitive, wrapper)).append('(');
context.writeExpr(context.getArgument(1));
context.getWriter().append(')');
}
private void castFromWrapper(InjectorContext context, Class<?> wrapper, Class<?> primitive) throws IOException {
String primitiveName = primitive.getName();
context.getWriter().appendMethodBody(new MethodReference(wrapper, primitiveName + "Value", primitive))
.append('(');
context.writeExpr(context.getArgument(1));
context.getWriter().append(')');
}
private void writePropertyAccessor(InjectorContext context, Expr property) throws IOException {
if (property instanceof ConstantExpr) {
String str = (String)((ConstantExpr)property).getValue();
if (str.isEmpty()) {
context.getWriter().append("[\"\"]");
return;
}
if (isValidIndentifier(str)) {
context.getWriter().append(".").append(str);
return;
}
}
context.getWriter().append("[$rt_ustr(");
context.writeExpr(property);
context.getWriter().append(")]");
}
private void writeStringExpr(InjectorContext context, Expr expr) throws IOException {
if (expr instanceof ConstantExpr) {
String str = (String)((ConstantExpr)expr).getValue();
context.getWriter().append('"');
context.writeEscaped(str);
context.getWriter().append('"');
return;
}
context.getWriter().append("$rt_ustr(");
context.writeExpr(expr);
context.getWriter().append(")");
}
private boolean isValidIndentifier(String str) {
if (!Character.isJavaIdentifierStart(str.charAt(0))) {
return false;
}
for (int i = 1; i < str.length(); ++i) {
if (!Character.isJavaIdentifierPart(str.charAt(i))) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.platform.plugin;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.MethodHolder;
import org.teavm.vm.spi.TeaVMHost;
/**
*
* @author Alexey Andreev
*/
class ResourceAccessorTransformer implements ClassHolderTransformer {
private TeaVMHost vm;
public ResourceAccessorTransformer(TeaVMHost vm) {
this.vm = vm;
}
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
ResourceAccessorGenerator generator = new ResourceAccessorGenerator();
if (cls.getName().equals(ResourceAccessor.class)) {
for (MethodHolder method : cls.getMethods()) {
vm.add(method.getReference(), generator);
}
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.platform.plugin;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
/**
*
* @author Alexey Andreev
*/
class ResourceTransformer implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource) {
}
}