Continues implementation of metadata providers

This commit is contained in:
konsoletyper 2014-06-04 16:30:42 +04:00
parent c23f62ced7
commit 10a8e0261a
13 changed files with 328 additions and 25 deletions

View File

@ -31,10 +31,13 @@ public class SourceWriter implements Appendable {
private NamingStrategy naming;
private boolean lineStart;
private boolean minified;
private int lineWidth;
private int pos;
SourceWriter(NamingStrategy naming, Appendable innerWriter) {
SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) {
this.naming = naming;
this.innerWriter = innerWriter;
this.lineWidth = lineWidth;
}
void setMinified(boolean minified) {
@ -42,8 +45,7 @@ public class SourceWriter implements Appendable {
}
public SourceWriter append(String value) throws IOException {
appendIndent();
innerWriter.append(value);
append((CharSequence)value);
return this;
}
@ -59,23 +61,43 @@ public class SourceWriter implements Appendable {
public SourceWriter append(char value) throws IOException {
appendIndent();
innerWriter.append(value);
if (value == '\n') {
newLine();
} else {
pos++;
}
return this;
}
@Override
public SourceWriter append(CharSequence csq) throws IOException {
appendIndent();
innerWriter.append(csq);
append(csq, 0, csq.length());
return this;
}
@Override
public SourceWriter append(CharSequence csq, int start, int end) throws IOException {
appendIndent();
innerWriter.append(csq, start, end);
int last = start;
for (int i = start; i < end; ++i) {
if (csq.charAt(i) == '\n') {
appendSingleLine(csq, last, i);
newLine();
last = i;
}
}
appendSingleLine(csq, last, end);
return this;
}
private void appendSingleLine(CharSequence csq, int start, int end) throws IOException {
if (start == end) {
return;
}
appendIndent();
pos += end - start;
innerWriter.append(csq, start, end);
}
public SourceWriter appendClass(String cls) throws NamingException, IOException {
return append(naming.getNameFor(cls));
}
@ -109,6 +131,7 @@ public class SourceWriter implements Appendable {
if (lineStart) {
for (int i = 0; i < indentSize; ++i) {
innerWriter.append(" ");
pos += 4;
}
lineStart = false;
}
@ -116,13 +139,26 @@ public class SourceWriter implements Appendable {
public SourceWriter newLine() throws IOException{
innerWriter.append('\n');
pos = 0;
lineStart = true;
return this;
}
public SourceWriter ws() throws IOException{
if (!minified) {
innerWriter.append(' ');
public SourceWriter ws() throws IOException {
if (pos >= lineWidth) {
newLine();
} else {
if (!minified) {
innerWriter.append(' ');
pos++;
}
}
return this;
}
public SourceWriter tokenBoundary() throws IOException {
if (pos >= lineWidth) {
newLine();
}
return this;
}
@ -130,6 +166,7 @@ public class SourceWriter implements Appendable {
public SourceWriter softNewLine() throws IOException{
if (!minified) {
innerWriter.append('\n');
pos = 0;
lineStart = true;
}
return this;

View File

@ -22,6 +22,7 @@ package org.teavm.codegen;
public class SourceWriterBuilder {
private NamingStrategy naming;
private boolean minified;
private int lineWidth = 512;
public SourceWriterBuilder(NamingStrategy naming) {
this.naming = naming;
@ -35,8 +36,12 @@ public class SourceWriterBuilder {
this.minified = minified;
}
public void setLineWidth(int lineWidth) {
this.lineWidth = lineWidth;
}
public SourceWriter build(Appendable innerWriter) {
SourceWriter writer = new SourceWriter(naming, innerWriter);
SourceWriter writer = new SourceWriter(naming, innerWriter, lineWidth);
writer.setMinified(minified);
return writer;
}

View File

@ -43,6 +43,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private ClassLoader classLoader;
private boolean minifying;
private Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
private Properties properties = new Properties();
private static class InjectorHolder {
public final Injector injector;
@ -88,6 +89,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
return classLoader;
}
@Override
public Properties getProperties() {
return new Properties(properties);
}
public void setProperties(Properties properties) {
this.properties.clear();
this.properties.putAll(properties);
}
public void renderRuntime() throws RenderingException {
try {
renderRuntimeCls();
@ -483,6 +494,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public ListableClassReaderSource getClassSource() {
return classSource;
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public Properties getProperties() {
return null;
}
}
@Override

View File

@ -15,6 +15,7 @@
*/
package org.teavm.javascript;
import java.util.Properties;
import org.teavm.codegen.NamingStrategy;
import org.teavm.codegen.SourceWriter;
import org.teavm.model.ListableClassReaderSource;
@ -33,4 +34,6 @@ public interface RenderingContext {
ListableClassReaderSource getClassSource();
ClassLoader getClassLoader();
Properties getProperties();
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.javascript.ni;
import java.util.Properties;
import org.teavm.model.ListableClassReaderSource;
/**
@ -25,4 +26,8 @@ public interface GeneratorContext {
String getParameterName(int index);
ListableClassReaderSource getClassSource();
ClassLoader getClassLoader();
Properties getProperties();
}

View File

@ -15,15 +15,17 @@
*/
package org.teavm.platform.plugin;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.teavm.codegen.SourceWriter;
import org.teavm.platform.metadata.ResourceArray;
/**
*
* @author Alexey Andreev
*/
class BuildTimeResourceArray<T> implements ResourceArray<T> {
class BuildTimeResourceArray<T> implements ResourceArray<T>, ResourceWriter {
private List<T> data = new ArrayList<>();
@Override
@ -40,4 +42,16 @@ class BuildTimeResourceArray<T> implements ResourceArray<T> {
public void add(T elem) {
data.add(elem);
}
@Override
public void write(SourceWriter writer) throws IOException {
writer.append('[').tokenBoundary();
for (int i = 0; i < data.size(); ++i) {
if (i > 0) {
writer.append(',').ws();
}
ResourceWriterHelper.write(writer, data.get(i));
}
writer.append(']').tokenBoundary();
}
}

View File

@ -15,15 +15,17 @@
*/
package org.teavm.platform.plugin;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.teavm.codegen.SourceWriter;
import org.teavm.platform.metadata.ResourceMap;
/**
*
* @author Alexey Andreev
*/
class BuildTimeResourceMap<T> implements ResourceMap<T> {
class BuildTimeResourceMap<T> implements ResourceMap<T>, ResourceWriter {
private Map<String, T> data = new HashMap<>();
@Override
@ -40,4 +42,20 @@ class BuildTimeResourceMap<T> implements ResourceMap<T> {
public void put(String key, T value) {
data.put(key, value);
}
@Override
public void write(SourceWriter writer) throws IOException {
writer.append('{');
boolean first = true;
for (Map.Entry<String, T> entry : data.entrySet()) {
if (!first) {
writer.append(",").ws();
}
first = false;
ResourceWriterHelper.writeString(writer, entry.getKey());
writer.ws().append(':').ws();
ResourceWriterHelper.write(writer, entry.getValue());
}
writer.append('}').tokenBoundary();
}
}

View File

@ -31,6 +31,16 @@ class BuildTimeResourceProxyBuilder {
boolean.class, Boolean.class, byte.class, Byte.class, short.class, Short.class,
int.class, Integer.class, float.class, Float.class, double.class, Double.class,
String.class, ResourceArray.class, ResourceMap.class));
private static Map<Class<?>, Object> defaultValues = new HashMap<>();
static {
defaultValues.put(boolean.class, false);
defaultValues.put(byte.class, (byte)0);
defaultValues.put(short.class, (short)0);
defaultValues.put(int.class, 0);
defaultValues.put(float.class, 0F);
defaultValues.put(double.class, 0.0);
}
public BuildTimeResourceProxy buildProxy(Class<?> iface) {
BuildTimeResourceProxyFactory factory = factories.get(iface);
@ -50,8 +60,8 @@ class BuildTimeResourceProxyBuilder {
Map<String, Class<?>> getters = new HashMap<>();
Map<String, Class<?>> setters = new HashMap<>();
Map<Method, BuildTimeResourceMethod> methods = new HashMap<>();
private List<Object> initialData = new ArrayList<>();
private Map<String, Integer> propertyIndexes = new HashMap<>();
private Object[] initialData;
public ProxyFactoryCreation(Class<?> iface) {
this.rootIface = iface;
@ -63,7 +73,7 @@ class BuildTimeResourceProxyBuilder {
" that is not an interface");
}
scanIface(rootIface);
return new BuildTimeResourceProxyFactory(methods, initialData.toArray(new Object[initialData.size()]));
return new BuildTimeResourceProxyFactory(methods, initialData);
}
private void scanIface(Class<?> iface) {
@ -109,18 +119,18 @@ class BuildTimeResourceProxyBuilder {
}
// Verify types of properties and fill default values
initialData = new Object[propertyIndexes.size()];
for (Map.Entry<String, Class<?>> property : getters.entrySet()) {
String propertyName = property.getKey();
Class<?> propertyType = property.getValue();
if (allowedPropertyTypes.contains(propertyType)) {
continue;
}
if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) {
throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
" has an illegal type " + propertyType.getName());
if (!allowedPropertyTypes.contains(propertyType)) {
if (!propertyType.isInterface() || !propertyType.isAnnotationPresent(Resource.class)) {
throw new IllegalArgumentException("Property " + iface.getName() + "." + propertyName +
" has an illegal type " + propertyType.getName());
}
}
initialData[propertyIndexes.get(propertyName)] = defaultValues.get(propertyType);
}
// TODO: fill default values
// Scan superinterfaces
for (Class<?> superIface : iface.getInterfaces()) {

View File

@ -0,0 +1,46 @@
/*
* 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.codegen.SourceWriter;
/**
*
* @author Alexey Andreev
*/
class BuildTimeResourceWriterMethod implements BuildTimeResourceMethod {
private String[] propertyNames;
public BuildTimeResourceWriterMethod(String[] propertyNames) {
this.propertyNames = propertyNames;
}
@Override
public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable {
SourceWriter writer = (SourceWriter)args[0];
writer.append('{');
for (int i = 0; i < propertyNames.length; ++i) {
if (i > 0) {
writer.append(',').ws();
}
ResourceWriterHelper.writeString(writer, propertyNames[i]);
writer.ws().append(':').ws();
ResourceWriterHelper.write(writer, proxy.data[i]);
}
writer.append('}').tokenBoundary();
return null;
}
}

View File

@ -32,7 +32,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
private Properties properties;
private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder();
private DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader,
public DefaultMetadataGeneratorContext(ListableClassReaderSource classSource, ClassLoader classLoader,
Properties properties) {
this.classSource = classSource;
this.classLoader = classLoader;
@ -56,7 +56,8 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
@Override
public <T> T createResource(Class<T> resourceType) {
return resourceType.cast(Proxy.newProxyInstance(classLoader, new Class<?>[] { resourceType },
return resourceType.cast(Proxy.newProxyInstance(classLoader,
new Class<?>[] { resourceType, ResourceWriter.class },
proxyBuilder.buildProxy(resourceType)));
}

View File

@ -16,19 +16,23 @@
package org.teavm.platform.plugin;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.*;
import org.teavm.platform.metadata.MetadataGenerator;
import org.teavm.platform.metadata.MetadataProvider;
/**
*
* @author Alexey Andreev
*/
class MetadataProviderNativeGenerator implements Generator {
public class MetadataProviderNativeGenerator 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(MetadataProvider.class.getName());
@ -39,5 +43,43 @@ class MetadataProviderNativeGenerator implements Generator {
throw new IllegalStateException("Method " + method.getReference() + " was marked with " +
MetadataProvider.class.getName() + " but it is not native");
}
// 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) {
throw new RuntimeException("Can't find metadata generator class: " + generatorClassName, e);
}
Constructor<?> cons;
try {
cons = generatorClass.getConstructor();
} catch (NoSuchMethodException e) {
throw new RuntimeException("Metadata generator " + generatorClassName + " does not have a public " +
"no-arg constructor", e);
}
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());
}
DefaultMetadataGeneratorContext metadataContext = new DefaultMetadataGeneratorContext(context.getClassSource(),
context.getClassLoader(), context.getProperties());
// Generate resource loader
Object resource = generator.generateMetadata(metadataContext, methodRef);
writer.append("if (!").appendMethodBody(methodRef).append("$$resource === undefined").append(") {")
.indent().softNewLine();
writer.appendMethodBody(methodRef).append("$$resource = ");
ResourceWriterHelper.write(writer, resource);
writer.append(';').softNewLine();
writer.outdent().append('}').softNewLine();
writer.append("return ").appendMethodBody(methodRef).append("$$resource;").softNewLine();
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.codegen.SourceWriter;
/**
*
* @author Alexey Andreev
*/
interface ResourceWriter {
void write(SourceWriter writer) throws IOException;
}

View File

@ -0,0 +1,74 @@
/*
* 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.codegen.SourceWriter;
/**
*
* @author Alexey Andreev
*/
final class ResourceWriterHelper {
public static void write(SourceWriter writer, Object resource) throws IOException {
if (resource == null) {
writer.append("null");
} else {
if (resource instanceof ResourceWriter) {
((ResourceWriter)resource).write(writer);
} else if (resource instanceof Number) {
writer.append(resource);
} else if (resource instanceof Boolean) {
writer.append(resource == Boolean.TRUE ? "true" : "false");
} else if (resource instanceof String) {
writeString(writer, (String)resource);
} else {
throw new RuntimeException("Error compiling resources. Value of illegal type found: " +
resource.getClass());
}
}
}
public static void writeString(SourceWriter writer, String s) throws IOException {
writer.append('"');
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
switch (c) {
case '\0':
writer.append("\\0");
break;
case '\n':
writer.append("\\n");
break;
case '\r':
writer.append("\\r");
break;
case '\t':
writer.append("\\t");
break;
default:
if (c < 32) {
writer.append("\\u00").append(Character.forDigit(c / 16, 16))
.append(Character.forDigit(c % 16, 16));
} else {
writer.append(c);
}
break;
}
}
writer.append('"');
}
}