Most of TClass now uses Platform instead of code generation

This commit is contained in:
konsoletyper 2015-02-08 22:35:51 +04:00
parent d93fa6cf41
commit 2ae7b587d1
31 changed files with 368 additions and 199 deletions

View File

@ -15,18 +15,26 @@
*/
package org.teavm.classlib.impl;
import java.util.HashMap;
import java.util.Map;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodReference;
import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataGeneratorContext;
import org.teavm.platform.metadata.Resource;
import org.teavm.platform.metadata.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class DeclaringClassMetadataGenerator implements MetadataGenerator {
public class DeclaringClassMetadataGenerator implements ClassScopedMetadataGenerator {
@Override
public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) {
return null;
public Map<String, Resource> generateMetadata(MetadataGeneratorContext context, MethodReference method) {
Map<String, Resource> result = new HashMap<>();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
if (cls.getOwnerName() != null) {
result.put(clsName, context.createClassResource(cls.getOwnerName()));
}
}
return result;
}
}

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.impl;
import java.util.ServiceLoader;
import org.teavm.classlib.impl.unicode.CLDRReader;
import org.teavm.model.MethodReference;
import org.teavm.platform.PlatformClass;
import org.teavm.vm.spi.TeaVMHost;
import org.teavm.vm.spi.TeaVMPlugin;
@ -30,12 +31,11 @@ public class JCLPlugin implements TeaVMPlugin {
public void install(TeaVMHost host) {
host.add(new EnumDependencySupport());
host.add(new EnumTransformer());
host.add(new ClassLookupDependencySupport());
host.add(new ObjectEnrichRenderer());
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
host.add(serviceLoaderSupp);
MethodReference loadServicesMethod = new MethodReference(ServiceLoader.class, "loadServices",
Class.class, Object[].class);
PlatformClass.class, Object[].class);
host.add(loadServicesMethod, serviceLoaderSupp);
JavacSupport javacSupport = new JavacSupport();
host.add(javacSupport);

View File

@ -65,7 +65,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
}
writer.outdent().append("}").softNewLine();
String param = context.getParameterName(1);
writer.append("var cls = " + param + ".$data;").softNewLine();
writer.append("var cls = " + param + ";").softNewLine();
writer.append("if (!cls.$$serviceList$$) {").indent().softNewLine();
writer.append("return $rt_createArray($rt_objcls(), 0);").softNewLine();
writer.outdent().append("}").softNewLine();

View File

@ -16,12 +16,6 @@
package org.teavm.classlib.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.MethodDependency;
import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.javascript.spi.Injector;
import org.teavm.javascript.spi.InjectorContext;
import org.teavm.model.*;
@ -30,70 +24,13 @@ import org.teavm.model.*;
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class ClassNativeGenerator implements Generator, Injector, DependencyPlugin {
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
throws IOException {
switch (methodRef.getName()) {
case "forNameImpl":
generateForName(context, writer);
break;
case "getDeclaringClass":
generateGetDeclaringClass(context, writer);
break;
}
}
public class ClassNativeGenerator implements Injector {
@Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "getEnumConstantsImpl":
context.writeExpr(context.getArgument(0));
context.getWriter().append(".$data.values()");
break;
}
}
private void generateForName(GeneratorContext context, SourceWriter writer) throws IOException {
String param = context.getParameterName(1);
writer.append("switch ($rt_ustr(" + param + ")) {").softNewLine().indent();
for (String name : context.getClassSource().getClassNames()) {
writer.append("case \"" + name + "\": ").appendClass(name).append(".$clinit(); ")
.append("return $rt_cls(").appendClass(name).append(");").softNewLine();
}
writer.append("default: return null;").softNewLine();
writer.outdent().append("}").softNewLine();
}
private void generateGetDeclaringClass(GeneratorContext context, SourceWriter writer) throws IOException {
String self = context.getParameterName(0);
writer.append("if (!").appendClass("java.lang.Class").append(".$$owners$$) {").indent().softNewLine();
writer.appendClass("java.lang.Class").append(".$$owners$$ = true;").softNewLine();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
writer.appendClass(clsName).append(".$$owner$$ = ");
if (cls.getOwnerName() != null) {
writer.appendClass(cls.getOwnerName());
} else {
writer.append("null");
}
writer.append(";").softNewLine();
}
writer.outdent().append("}").softNewLine();
writer.append("var cls = " + self + ".$data;").softNewLine();
writer.append("return cls.$$owner$$ != null ? $rt_cls(cls.$$owner$$) : null;").softNewLine();
}
@Override
public void methodAchieved(DependencyAgent agent, MethodDependency graph, CallLocation location) {
switch (graph.getReference().getName()) {
case "forNameImpl":
case "getDeclaringClass":
graph.getResult().propagate(agent.getType("java.lang.Class"));
break;
case "newInstance":
agent.linkMethod(new MethodReference(InstantiationException.class, "<init>", void.class),
location).use();
context.getWriter().append(".values()");
break;
}
}

View File

@ -19,7 +19,8 @@ import org.teavm.classlib.impl.DeclaringClassMetadataGenerator;
import org.teavm.javascript.spi.InjectedBy;
import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.metadata.MetadataProvider;
import org.teavm.platform.metadata.ClassResource;
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
/**
*
@ -36,7 +37,7 @@ public class TClass<T> extends TObject {
platformClass.setJavaClass(Platform.getPlatformObject(this));
}
static TClass<?> getClass(PlatformClass cls) {
public static TClass<?> getClass(PlatformClass cls) {
if (cls == null) {
return null;
}
@ -47,7 +48,7 @@ public class TClass<T> extends TObject {
return result;
}
PlatformClass getPlatformClass() {
public PlatformClass getPlatformClass() {
return platformClass;
}
@ -71,7 +72,7 @@ public class TClass<T> extends TObject {
}
public boolean isArray() {
return platformClass.getMetadata().isArray();
return platformClass.getMetadata().getArrayItem() != null;
}
public boolean isEnum() {
@ -141,12 +142,13 @@ public class TClass<T> extends TObject {
return (TClass<? super T>)getClass(platformClass.getMetadata().getSuperclass());
}
@SuppressWarnings("unchecked")
public T[] getEnumConstants() {
return isEnum() ? getEnumConstantsImpl() : null;
return isEnum() ? (T[])getEnumConstantsImpl(platformClass) : null;
}
@InjectedBy(ClassNativeGenerator.class)
public native T[] getEnumConstantsImpl();
private static native Object[] getEnumConstantsImpl(PlatformClass cls);
@SuppressWarnings("unchecked")
public T cast(TObject obj) {
@ -175,12 +177,22 @@ public class TClass<T> extends TObject {
return forName(name);
}
@SuppressWarnings("unchecked")
public T newInstance() throws TInstantiationException, TIllegalAccessException {
return Platform.newInstance(platformClass);
Object instance = Platform.newInstance(platformClass);
if (instance == null) {
throw new TInstantiationException();
}
return (T)instance;
}
@MetadataProvider(DeclaringClassMetadataGenerator.class)
public native TClass<?> getDeclaringClass();
public TClass<?> getDeclaringClass() {
ClassResource res = getDeclaringClass(platformClass);
return res != null ? getClass(Platform.classFromResource(res)) : null;
}
@ClassScopedMetadataProvider(DeclaringClassMetadataGenerator.class)
private static native ClassResource getDeclaringClass(PlatformClass cls);
@SuppressWarnings("unchecked")
public <U> TClass<? extends U> asSubclass(TClass<U> clazz) {

View File

@ -93,15 +93,14 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
private void generateNewInstance(GeneratorContext context, SourceWriter writer) throws IOException {
String type = context.getParameterName(1);
String length = context.getParameterName(2);
writer.append("var cls = " + type + ".$data;").softNewLine();
writer.append("if (cls.primitive) {").softNewLine().indent();
writer.append("if (").append(type).append(".$meta.primitive) {").softNewLine().indent();
for (String primitive : primitives) {
writer.append("if (cls == $rt_" + primitive.toLowerCase() + "cls()) {").indent().softNewLine();
writer.append("if (" + type + " == $rt_" + primitive.toLowerCase() + "cls()) {").indent().softNewLine();
writer.append("return $rt_create" + primitive + "Array(" + length + ");").softNewLine();
writer.outdent().append("}").softNewLine();
}
writer.outdent().append("} else {").indent().softNewLine();
writer.append("return $rt_createArray(cls, " + length + ")").softNewLine();
writer.append("return $rt_createArray(" + type + ", " + length + ")").softNewLine();
writer.outdent().append("}").softNewLine();
}

View File

@ -18,6 +18,7 @@ package org.teavm.classlib.java.lang.reflect;
import org.teavm.classlib.java.lang.*;
import org.teavm.dependency.PluggableDependency;
import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.platform.PlatformClass;
/**
*
@ -38,12 +39,12 @@ public final class TArray extends TObject {
if (length < 0) {
throw new TNegativeArraySizeException();
}
return newInstanceImpl(componentType, length);
return newInstanceImpl(componentType.getPlatformClass(), length);
}
@GeneratedBy(ArrayNativeGenerator.class)
@PluggableDependency(ArrayNativeGenerator.class)
private static native TObject newInstanceImpl(TClass<?> componentType, int length);
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
public static TObject get(TObject array, int index) throws TIllegalArgumentException,
TArrayIndexOutOfBoundsException {

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.java.util;
import org.teavm.classlib.java.lang.*;
import org.teavm.platform.PlatformClass;
/**
*
@ -48,7 +49,7 @@ public final class TServiceLoader<S> extends TObject implements TIterable<S> {
}
public static <S> TServiceLoader<S> load(TClass<S> service) {
return new TServiceLoader<>(loadServices(service));
return new TServiceLoader<>(loadServices(service.getPlatformClass()));
}
public static <S> TServiceLoader<S> load(TClass<S> service, @SuppressWarnings("unused") TClassLoader loader) {
@ -59,7 +60,7 @@ public final class TServiceLoader<S> extends TObject implements TIterable<S> {
return load(service);
}
private static native <T> T[] loadServices(TClass<T> serviceType);
private static native <T> T[] loadServices(PlatformClass cls);
public void reload() {
// Do nothing, services are bound at build time

View File

@ -26,6 +26,7 @@ import org.teavm.common.ServiceRepository;
import org.teavm.debugging.information.DebugInformationEmitter;
import org.teavm.debugging.information.DeferredCallSite;
import org.teavm.debugging.information.DummyDebugInformationEmitter;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.ast.*;
import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.javascript.spi.InjectedBy;
@ -54,6 +55,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private DeferredCallSite lastCallSite;
private DeferredCallSite prevCallSite;
private Set<MethodReference> asyncMethods;
private Diagnostics diagnostics;
private boolean async;
private static class InjectorHolder {
@ -77,13 +79,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
public Renderer(SourceWriter writer, ListableClassHolderSource classSource, ClassLoader classLoader,
ServiceRepository services, Set<MethodReference> asyncMethods) {
ServiceRepository services, Set<MethodReference> asyncMethods, Diagnostics diagnostics) {
this.naming = writer.getNaming();
this.writer = writer;
this.classSource = classSource;
this.classLoader = classLoader;
this.services = services;
this.asyncMethods = new HashSet<>(asyncMethods);
this.diagnostics = diagnostics;
}
@Override
@ -167,43 +170,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
private void renderRuntimeCls() throws IOException {
writer.append("function $rt_cls").ws().append("(clsProto)").ws().append("{")
.indent().softNewLine();
String classClass = "java.lang.Class";
writer.append("var cls").ws().append("=").ws().append("clsProto.classObject;").softNewLine();
writer.append("if").ws().append("(typeof cls").ws().append("===").ws().append("'undefined')").ws()
.append("{").softNewLine().indent();
MethodReference createMethodRef = new MethodReference(classClass, "createNew", ValueType.object(classClass));
writer.append("cls").ws().append("=").ws().appendMethodBody(createMethodRef).append("();").softNewLine();
writer.append("cls.$data = clsProto;").softNewLine();
if (classSource.get(classClass).getField("name") != null) {
writer.append("cls.").appendField(new FieldReference(classClass, "name")).ws().append("=").ws()
.append("clsProto.$meta.name").ws().append("!==").ws().append("undefined").ws().append("?").ws()
.append("$rt_str(clsProto.$meta.name)").ws().append(":").ws().append("null;").softNewLine();
}
if (classSource.get(classClass).getField("name") != null) {
writer.append("cls.").appendField(new FieldReference(classClass, "binaryName")).ws().append("=").ws()
.append("clsProto.$meta.binaryName").ws().append("!==").ws().append("undefined").ws()
.append("?").ws()
.append("$rt_str(clsProto.$meta.binaryName)").ws().append(":").ws().append("null;").softNewLine();
}
if (classSource.get(classClass).getField("primitive") != null) {
writer.append("cls.").appendField(new FieldReference(classClass, "primitive"))
.append(" = clsProto.$meta.primitive ? 1 : 0;").newLine();
}
if (classSource.get(classClass).getField("array") != null) {
writer.append("cls.").appendField(new FieldReference(classClass, "array")).ws()
.append("=").ws().append("clsProto.$meta.item").ws().append("?").ws()
.append("1").ws().append(":").ws().append("0;").softNewLine();
}
if (classSource.get(classClass).getField("isEnum") != null) {
writer.append("cls.").appendField(new FieldReference(classClass, "isEnum")).ws()
.append("=").ws().append("clsProto.$meta.enum").ws().append("?").ws()
.append("1").ws().append(":").ws().append("0;").softNewLine();
}
writer.append("clsProto.classObject").ws().append("=").ws().append("cls;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return cls;").softNewLine();
writer.append("function $rt_cls(cls)").ws().append("{").softNewLine().indent();
writer.append("return ").appendMethodBody("java.lang.Class", "getClass",
ValueType.object("org.teavm.platform.PlatformClass"),
ValueType.object("java.lang.Class")).append("(cls);")
.softNewLine();
writer.outdent().append("}").newLine();
}
@ -706,6 +677,11 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public String getCompleteContinuation() {
return "$return";
}
@Override
public Diagnostics getDiagnostics() {
return diagnostics;
}
}
private void pushLocation(NodeLocation location) {

View File

@ -17,6 +17,7 @@ package org.teavm.javascript.spi;
import java.util.Properties;
import org.teavm.common.ServiceRepository;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReference;
@ -38,4 +39,6 @@ public interface GeneratorContext extends ServiceRepository {
String getCompleteContinuation();
boolean isAsync(MethodReference method);
Diagnostics getDiagnostics();
}

View File

@ -334,7 +334,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE;
}
});
dependencyChecker.linkMethod(new MethodReference(Class.class, "createNew", Class.class), null).use();
dependencyChecker.linkMethod(new MethodReference(Class.class.getName(), "getClass",
ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Class.class)), null).use();
dependencyChecker.linkMethod(new MethodReference(String.class, "<init>", char[].class, void.class),
null).use();
dependencyChecker.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class, char[].class,
@ -396,7 +397,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
SourceWriterBuilder builder = new SourceWriterBuilder(naming);
builder.setMinified(minifying);
SourceWriter sourceWriter = builder.build(writer);
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this, asyncMethods);
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this, asyncMethods, diagnostics);
renderer.setProperties(properties);
if (debugEmitter != null) {
int classIndex = 0;

View File

@ -130,15 +130,18 @@ function $rt_arraycls(cls) {
}
var name = "[" + cls.$meta.binaryName;
arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls(),
name : name, binaryName : name };
name : name, binaryName : name, enum : false };
arraycls.classObject = null;
cls.$array = arraycls;
}
return cls.$array;
}
function $rt_createcls() {
return {
classObject : null,
$meta : {
supertypes : []
supertypes : [],
superclass : null
}
};
}
@ -147,6 +150,8 @@ function $rt_createPrimitiveCls(name, binaryName) {
cls.$meta.primitive = true;
cls.$meta.name = name;
cls.$meta.binaryName = binaryName;
cls.$meta.enum = false;
cls.$meta.item = null;
return cls;
}
var $rt_booleanclsCache = null;
@ -365,18 +370,21 @@ function $rt_putStderr(ch) {
}
function $rt_declClass(cls, data) {
cls.$meta = {};
cls.$meta.superclass = data.superclass;
cls.$meta.supertypes = data.interfaces ? data.interfaces.slice() : [];
var m = cls.$meta
m.superclass = typeof(data.superclass) !== 'undefined' ? data.superclass : null;
m.supertypes = data.interfaces ? data.interfaces.slice() : [];
if (data.superclass) {
cls.$meta.supertypes.push(data.superclass);
m.supertypes.push(data.superclass);
cls.prototype = new data.superclass();
} else {
cls.prototype = new Object();
}
cls.$meta.name = data.name;
cls.$meta.binaryName = "L" + data.name + ";";
cls.$meta.enum = data.enum;
m.name = data.name;
m.binaryName = "L" + data.name + ";";
m.enum = data.enum;
m.item = null;
cls.prototype.constructor = cls;
cls.classObject = null;
cls.$clinit = data.clinit ? data.clinit : function() {};
}
function $rt_virtualMethods(cls) {

View File

@ -370,11 +370,11 @@ var JUnitClient = {};
JUnitClient.run = function() {
var handler = window.addEventListener("message", function() {
window.removeEventListener("message", handler);
var message = {};
try {
var instance = new TestClass();
initInstance(instance);
runTest(instance, function(restore) {
var message = {};
try {
var result = restore();
message.status = "ok";

View File

@ -88,7 +88,7 @@ public class JSNativeGenerator implements Injector, DependencyPlugin {
}
writer.append("))");
break;
case "pass":
case "marshall":
context.writeExpr(context.getArgument(0));
break;
case "wrap":

View File

@ -72,7 +72,7 @@ public final class Platform {
@PluggableDependency(PlatformGenerator.class)
public static native void initClass(PlatformClass cls);
@GeneratedBy(PlatformGenerator.class)
@InjectedBy(PlatformGenerator.class)
@PluggableDependency(PlatformGenerator.class)
public static native PlatformClass classFromResource(ClassResource resource);
}

View File

@ -17,13 +17,12 @@ package org.teavm.platform;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface PlatformClass extends JSObject, Resource {
public interface PlatformClass extends JSObject {
@JSProperty("$meta")
PlatformClassMetadata getMetadata();

View File

@ -38,9 +38,6 @@ public interface PlatformClassMetadata extends JSObject {
@JSProperty
boolean isPrimitive();
@JSProperty
boolean isArray();
@JSProperty
boolean isEnum();
}

View File

@ -15,38 +15,38 @@
*/
package org.teavm.platform;
import org.teavm.jso.JSMethod;
import org.teavm.jso.JSObject;
import org.teavm.jso.JSProperty;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface PlatformPrimitives extends JSObject {
@JSProperty("$rt_voidcls")
@JSMethod("$rt_voidcls")
PlatformClass getVoidClass();
@JSProperty("$rt_booleancls")
@JSMethod("$rt_booleancls")
PlatformClass getBooleanClass();
@JSProperty("$rt_bytecls")
@JSMethod("$rt_bytecls")
PlatformClass getByteClass();
@JSProperty("$rt_shortcls")
@JSMethod("$rt_shortcls")
PlatformClass getShortClass();
@JSProperty("$rt_charcls")
@JSMethod("$rt_charcls")
PlatformClass getCharClass();
@JSProperty("$rt_intcls")
@JSMethod("$rt_intcls")
PlatformClass getIntClass();
@JSProperty("$rt_longcls")
@JSMethod("$rt_longcls")
PlatformClass getLongClass();
@JSProperty("$rt_floatcls")
@JSMethod("$rt_floatcls")
PlatformClass getFloatClass();
@JSProperty("$rt_doublecls")
@JSMethod("$rt_doublecls")
PlatformClass getDoubleClass();
}

View File

@ -23,7 +23,7 @@ import org.teavm.jso.JSProperty;
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface PlatformSequence<T> extends JSObject {
public interface PlatformSequence<T extends JSObject> extends JSObject {
@JSProperty
int getLength();

View File

@ -0,0 +1,35 @@
/*
* Copyright 2015 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.metadata;
import java.util.Map;
import org.teavm.model.MethodReference;
import org.teavm.platform.PlatformClass;
/**
* <p>Behaviour of this class is similar to {@link MetadataGenerator}. The difference is that method, marked with
* {@link ClassScopedMetadataProvider} must take one argument of type {@link PlatformClass}. It will
* return different resource for each given class, corresponding to map entries, produced by
* {@link #generateMetadata(MetadataGeneratorContext, MethodReference)}.
*
* @see ClassScopedMetadataProvider
* @see MetadataGenerator
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface ClassScopedMetadataGenerator {
Map<String, Resource> generateMetadata(MetadataGeneratorContext context, MethodReference method);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2015 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.metadata;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>Binds a {@link ClassScopedMetadataGenerator} to a native method.</p>
*
* @see MetadataProvider
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClassScopedMetadataProvider {
Class<? extends ClassScopedMetadataGenerator> value();
}

View File

@ -50,6 +50,8 @@ import org.teavm.model.MethodReference;
*
* <p>All other types are not considered to be resources and therefore are not accepted.</p>
*
* @see ClassScopedMetadataGenerator
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface MetadataGenerator {

View File

@ -102,8 +102,8 @@ class BuildTimeResourceProxyBuilder {
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");
throw new IllegalArgumentException("Error creating a new resource of type " + iface.getName() + "." +
" This type does not implement the " + Resource.class.getName() + " interface");
}
// Scan methods

View File

@ -36,7 +36,7 @@ class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
if (i > 0) {
writer.append(',').ws();
}
ResourceWriterHelper.writeString(writer, propertyNames[i]);
ResourceWriterHelper.writeIdentifier(writer, propertyNames[i]);
writer.ws().append(':').ws();
ResourceWriterHelper.write(writer, proxy.data[i]);
}

View File

@ -13,10 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl;
package org.teavm.platform.plugin;
import org.teavm.dependency.*;
import org.teavm.model.*;
import org.teavm.platform.Platform;
/**
*
@ -38,14 +39,14 @@ public class ClassLookupDependencySupport implements DependencyListener {
@Override
public void methodAchieved(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReference ref = method.getReference();
if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
if (ref.getClassName().equals(Platform.class.getName()) && ref.getName().equals("lookupClass")) {
allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(DependencyAgentType type) {
ClassReader cls = agent.getClassSource().get(type.getName());
if (cls == null) {
return;
}
MethodReader initMethod = cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID));
MethodReader initMethod = cls.getMethod(new MethodDescriptor("<clinit>", void.class));
if (initMethod != null) {
agent.linkMethod(initMethod.getReference(), location).use();
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2015 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 java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.Renderer;
import org.teavm.javascript.spi.Generator;
import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.model.*;
import org.teavm.platform.metadata.ClassScopedMetadataGenerator;
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
import org.teavm.platform.metadata.Resource;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class ClassScopedMetadataProviderNativeGenerator implements Generator {
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
// Validate method
ClassReader cls = context.getClassSource().get(methodRef.getClassName());
MethodReader method = cls.getMethod(methodRef.getDescriptor());
AnnotationReader providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName());
if (providerAnnot == null) {
return;
}
if (!method.hasModifier(ElementModifier.NATIVE)) {
context.getDiagnostics().error(new CallLocation(methodRef), "Method {{m0}} is marked with " +
"{{c1}} annotation, but it is not native", methodRef, ClassScopedMetadataProvider.class.getName());
return;
}
// 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;
}
ClassScopedMetadataGenerator generator;
try {
generator = (ClassScopedMetadataGenerator)cons.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
context.getDiagnostics().error(new CallLocation(methodRef), "Error instantiating metadata " +
"generator {{c0}}", generatorClassName);
return;
}
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
context.getClassLoader(), context.getProperties(), context);
Map<String, Resource> resourceMap = generator.generateMetadata(metadataContext, methodRef);
writer.append("var p").ws().append("=").ws().append("\"" + Renderer.escapeString("$$res_" +
writer.getNaming().getNameFor(methodRef)) + "\"").append(";").softNewLine();
for (Map.Entry<String, Resource> entry : resourceMap.entrySet()) {
writer.appendClass(entry.getKey()).append("[p]").ws().append("=").ws();
ResourceWriterHelper.write(writer, entry.getValue());
writer.append(";").softNewLine();
}
writer.appendMethodBody(methodRef).ws().append('=').ws().append("function(cls)").ws().append("{")
.softNewLine().indent();
writer.append("return cls.hasOwnProperty(p)").ws().append("?").ws().append("cls[p]").ws().append(":")
.ws().append("null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").appendMethodBody(methodRef).append("(").append(context.getParameterName(1))
.append(");").softNewLine();
}
}

View File

@ -41,8 +41,9 @@ public class MetadataProviderNativeGenerator implements Generator {
return;
}
if (!method.hasModifier(ElementModifier.NATIVE)) {
throw new IllegalStateException("Method " + method.getReference() + " was marked with " +
MetadataProvider.class.getName() + " but it is not native");
context.getDiagnostics().error(new CallLocation(methodRef), "Method {{m0}} is marked with " +
"{{c1}} annotation, but it is not native", methodRef, MetadataProvider.class.getName());
return;
}
// Find and instantiate metadata generator
@ -52,23 +53,25 @@ public class MetadataProviderNativeGenerator implements Generator {
try {
generatorClass = Class.forName(generatorClassName, true, context.getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Can't find metadata generator class: " + generatorClassName, 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) {
throw new RuntimeException("Metadata generator " + generatorClassName + " does not have a public " +
"no-arg constructor", 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 e) {
throw new RuntimeException("Error instantiating metadata generator " + generatorClassName, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Error instantiating metadata generator " + generatorClassName,
e.getTargetException());
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
context.getDiagnostics().error(new CallLocation(methodRef), "Error instantiating metadata " +
"generator {{c0}}", generatorClassName);
return;
}
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
context.getClassLoader(), context.getProperties(), context);

View File

@ -18,6 +18,8 @@ package org.teavm.platform.plugin;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.spi.GeneratedBy;
import org.teavm.model.*;
import org.teavm.platform.PlatformClass;
import org.teavm.platform.metadata.ClassScopedMetadataProvider;
import org.teavm.platform.metadata.MetadataProvider;
/**
@ -29,13 +31,26 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
for (MethodHolder method : cls.getMethods()) {
AnnotationReader providerAnnot = method.getAnnotations().get(MetadataProvider.class.getName());
if (providerAnnot == null) {
continue;
if (providerAnnot != null) {
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
MetadataProviderNativeGenerator.class.getName())));
method.getAnnotations().add(genAnnot);
}
providerAnnot = method.getAnnotations().get(ClassScopedMetadataProvider.class.getName());
if (providerAnnot != null) {
ValueType[] params = method.getParameterTypes();
if (params.length != 1 && params[0].isObject(PlatformClass.class.getName())) {
diagnostics.error(new CallLocation(method.getReference()), "Method {{m0}} marked with {{c1}} " +
"must take exactly one parameter of type {{c2}}",
method.getReference(), ClassScopedMetadataProvider.class.getName(),
PlatformClass.class.getName());
}
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
ClassScopedMetadataProviderNativeGenerator.class.getName())));
method.getAnnotations().add(genAnnot);
}
AnnotationHolder genAnnot = new AnnotationHolder(GeneratedBy.class.getName());
genAnnot.getValues().put("value", new AnnotationValue(ValueType.object(
MetadataProviderNativeGenerator.class.getName())));
method.getAnnotations().add(genAnnot);
}
}
}

View File

@ -25,7 +25,6 @@ import org.teavm.javascript.spi.GeneratorContext;
import org.teavm.javascript.spi.Injector;
import org.teavm.javascript.spi.InjectorContext;
import org.teavm.model.*;
import org.teavm.platform.Platform;
/**
*
@ -45,6 +44,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "asJavaClass":
case "classFromResource":
context.writeExpr(context.getArgument(0));
return;
}
@ -54,34 +54,45 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) {
case "newInstance":
generateNewInstance(context, writer);
generateNewInstance(context, writer, methodRef);
break;
case "lookupClass":
generateLookup(context, writer);
break;
}
}
private void generateNewInstance(GeneratorContext context, SourceWriter writer) throws IOException {
String self = context.getParameterName(0);
writer.append("if").ws().append("(!").appendClass(Platform.class).append(".$$constructors$$)").ws()
.append("{").indent().softNewLine();
writer.appendClass(Platform.class).append(".$$constructors$$").ws().append("=").append("true;").softNewLine();
private void generateNewInstance(GeneratorContext context, SourceWriter writer, MethodReference methodRef)
throws IOException {
writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine();
for (String clsName : context.getClassSource().getClassNames()) {
ClassReader cls = context.getClassSource().get(clsName);
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", void.class));
if (method != null) {
writer.appendClass(clsName).append(".$$constructor$$").ws().append("=").ws()
.appendMethodBody(method.getReference()).append(";").softNewLine();
writer.appendClass(clsName).append("[c]").ws().append("=").ws()
.append(writer.getNaming().getNameForInit(method.getReference()))
.append(";").softNewLine();
}
}
writer.appendMethodBody(methodRef).ws().append("=").ws().append("function(cls)").ws().append("{")
.softNewLine().indent();
writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine();
writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("var cls = " + self + ".$data;").softNewLine();
writer.append("var ctor = cls.$$constructor$$;").softNewLine();
writer.append("if (!ctor) {").indent().softNewLine();
writer.append("var ex = new ").appendClass(InstantiationException.class.getName()).append("();").softNewLine();
writer.appendMethodBody(new MethodReference(InstantiationException.class, "<init>", void.class))
.append("(ex);").softNewLine();
writer.append("$rt_throw(ex);").softNewLine();
writer.append("return cls[c]();").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return ").appendMethodBody(methodRef).append("(")
.append(context.getParameterName(1)).append(");").softNewLine();
}
private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException {
String param = context.getParameterName(1);
writer.append("switch ($rt_ustr(" + param + ")) {").softNewLine().indent();
for (String name : context.getClassSource().getClassNames()) {
writer.append("case \"" + name + "\": ").appendClass(name).append(".$clinit(); ")
.append("return ").appendClass(name).append(";").softNewLine();
}
writer.append("default: return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("var instance = new cls();").softNewLine();
writer.append("ctor(instance);").softNewLine();
writer.append("return instance;").softNewLine();
}
}

View File

@ -31,5 +31,6 @@ public class PlatformPlugin implements TeaVMPlugin {
host.add(new ResourceAccessorDependencyListener());
host.add(new AsyncMethodProcessor());
host.add(new NewInstanceDependencySupport());
host.add(new ClassLookupDependencySupport());
}
}

View File

@ -45,6 +45,34 @@ final class ResourceWriterHelper {
}
}
public static void writeIdentifier(SourceWriter writer, String id) throws IOException {
if (id.isEmpty() || !isIdentifierStart(id.charAt(0))) {
writeString(writer, id);
return;
}
for (int i = 1; i < id.length(); ++i) {
if (isIdentifierPart(id.charAt(i))) {
writeString(writer, id);
return;
}
}
writer.append(id);
}
private static boolean isIdentifierStart(char c) {
if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') {
return true;
}
return c == '$' || c == '_';
}
private static boolean isIdentifierPart(char c) {
if (isIdentifierStart(c)) {
return true;
}
return c >= '0' && c <= '9';
}
public static void writeString(SourceWriter writer, String s) throws IOException {
writer.append('"');
for (int i = 0; i < s.length(); ++i) {