Adds annotation parsing. Adds native method generation

This commit is contained in:
konsoletyper 2013-10-28 22:59:56 +04:00
parent df05104e3c
commit d63171e935
12 changed files with 478 additions and 256 deletions

View File

@ -3,12 +3,8 @@ package org.teavm.classlibgen;
import org.teavm.codegen.DefaultAliasProvider;
import org.teavm.codegen.DefaultNamingStrategy;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.MethodDecompiler;
import org.teavm.javascript.Optimizer;
import org.teavm.javascript.Renderer;
import org.teavm.javascript.ast.RenderableMethod;
import org.teavm.javascript.Decompiler;
import org.teavm.model.ClassHolder;
import org.teavm.model.MethodHolder;
import org.teavm.model.resource.ClasspathClassHolderSource;
/**
@ -18,18 +14,12 @@ import org.teavm.model.resource.ClasspathClassHolderSource;
public class ClasslibTestGenerator {
public static void main(String[] args) {
ClasspathClassHolderSource source = new ClasspathClassHolderSource();
MethodDecompiler decompiler = new MethodDecompiler(source);
DefaultAliasProvider aliasProvider = new DefaultAliasProvider();
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source);
SourceWriter writer = new SourceWriter(naming);
Renderer renderer = new Renderer(writer, source);
Optimizer optimizer = new Optimizer();
Decompiler decompiler = new Decompiler(source, naming, writer);
ClassHolder cls = source.getClassHolder("java.lang.Object");
for (MethodHolder method : cls.getMethods()) {
RenderableMethod renderableMethod = decompiler.decompile(method);
optimizer.optimize(renderableMethod);
renderer.render(renderableMethod);
}
decompiler.decompile(cls);
System.out.println(writer);
}
}

View File

@ -0,0 +1,25 @@
package org.teavm.javascript;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class DecompilationException extends RuntimeException {
private static final long serialVersionUID = -1400142974526572669L;
public DecompilationException() {
super();
}
public DecompilationException(String message, Throwable cause) {
super(message, cause);
}
public DecompilationException(String message) {
super(message);
}
public DecompilationException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,149 @@
package org.teavm.javascript;
import java.util.Set;
import org.teavm.codegen.NamingStrategy;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ast.RenderableMethod;
import org.teavm.javascript.ni.GeneratedBy;
import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class Decompiler {
private SourceWriter writer;
private MethodDecompiler methodDecompiler;
private NamingStrategy naming;
private Renderer renderer;
public Decompiler(ClassHolderSource classSource, NamingStrategy naming, SourceWriter writer) {
this.methodDecompiler = new MethodDecompiler(classSource);
this.renderer = new Renderer(writer, classSource);
this.writer = writer;
this.naming = naming;
}
public void decompile(ClassHolder cls) {
writer.appendClass(cls.getName()).append(" = function() {\n").indent().newLine();
for (FieldHolder field : cls.getFields()) {
if (field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.append("this.").appendField(cls.getName(), field.getName()).append(" = ")
.append(renderer.constantToString(value)).append(";").newLine();
}
writer.append("this.$class = ").appendClass(cls.getName()).append(";").newLine();
writer.outdent().append("}").newLine();
for (FieldHolder field : cls.getFields()) {
if (!field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.appendClass(cls.getName()).append('.')
.appendField(cls.getName(), field.getName()).append(" = ")
.append(renderer.constantToString(value)).append(";").newLine();
}
writer.appendClass(cls.getName()).append(".prototype = new ")
.append(cls.getParent() != null ? naming.getNameFor(cls.getParent()) :
"Object").append("();").newLine();
writer.appendClass(cls.getName()).append(".$meta = { ");
writer.append("supertypes : [");
boolean first = true;
if (cls.getParent() != null) {
writer.appendClass(cls.getParent());
first = false;
}
for (String iface : cls.getInterfaces()) {
if (!first) {
writer.append(", ");
}
first = false;
writer.appendClass(iface);
}
writer.append("]");
writer.append(" };").newLine();
for (MethodHolder method : cls.getMethods()) {
Set<ElementModifier> modifiers = method.getModifiers();
if (modifiers.contains(ElementModifier.ABSTRACT)) {
continue;
}
if (modifiers.contains(ElementModifier.NATIVE)) {
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
if (annotHolder == null) {
throw new DecompilationException("Method " + cls.getName() + "." + method.getDescriptor() +
" is native, but no " + GeneratedBy.class.getName() + " annotation found");
}
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
String generatorClassName = ((ValueType.Object)annotValue).getClassName();
generateNativeMethod(generatorClassName, method);
} else {
RenderableMethod renderableMethod = methodDecompiler.decompile(method);
Optimizer optimizer = new Optimizer();
optimizer.optimize(renderableMethod);
renderer.render(renderableMethod);
}
}
}
private void generateNativeMethod(String generatorClassName, MethodHolder method) {
Generator generator;
try {
Class<?> generatorClass = Class.forName(generatorClassName);
generator = (Generator)generatorClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new DecompilationException("Error instantiating generator " + generatorClassName +
" for native method " + method.getOwner().getName() + "." + method.getDescriptor());
}
MethodReference ref = new MethodReference(method.getOwner().getName(), method.getDescriptor());
generator.generate(new Context(), writer, ref);
}
private static Object getDefaultValue(ValueType type) {
if (type instanceof ValueType.Primitive) {
ValueType.Primitive primitive = (ValueType.Primitive)type;
switch (primitive.getKind()) {
case BOOLEAN:
return false;
case BYTE:
return (byte)0;
case SHORT:
return (short)0;
case INTEGER:
return 0;
case CHARACTER:
return '\0';
case LONG:
return 0L;
case FLOAT:
return 0F;
case DOUBLE:
return 0.0;
}
}
return null;
}
private class Context implements GeneratorContext {
@Override
public String getParameterName(int index) {
return renderer.variableName(index);
}
@Override
public NamingStrategy getNaming() {
return naming;
}
}
}

View File

@ -15,7 +15,6 @@
*/
package org.teavm.javascript;
import java.util.Set;
import org.teavm.codegen.NamingStrategy;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ast.*;
@ -37,88 +36,12 @@ public class Renderer implements ExprVisitor, StatementVisitor {
this.classSource = classSource;
}
public void render(RenderableClass cls) {
ClassHolder metadata = cls.getMetadata();
writer.appendClass(metadata.getName()).append(" = function() {\n").indent().newLine();
for (FieldHolder field : cls.getFields()) {
if (field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.append("this.").appendField(metadata.getName(), field.getName()).append(" = ")
.append(constantToString(value)).append(";").newLine();
}
writer.append("this.$class = ").appendClass(metadata.getName()).append(";").newLine();
writer.outdent().append("}").newLine();
for (FieldHolder field : cls.getFields()) {
if (!field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.appendClass(metadata.getName()).append('.')
.appendField(metadata.getName(), field.getName()).append(" = ")
.append(constantToString(value)).append(";").newLine();
public SourceWriter getWriter() {
return writer;
}
writer.appendClass(metadata.getName()).append(".prototype = new ")
.append(metadata.getParent() != null ? naming.getNameFor(metadata.getParent()) :
"Object").append("();").newLine();
writer.appendClass(metadata.getName()).append(".$meta = { ");
writer.append("supertypes : [");
boolean first = true;
if (metadata.getParent() != null) {
writer.appendClass(metadata.getParent());
first = false;
}
for (String iface : metadata.getInterfaces()) {
if (!first) {
writer.append(", ");
}
first = false;
writer.appendClass(iface);
}
writer.append("]");
writer.append(" };").newLine();
for (RenderableMethod method : cls.getMethods()) {
MethodHolder methodMetadata = method.getMetadata();
Set<ElementModifier> modifiers = methodMetadata.getModifiers();
if (modifiers.contains(ElementModifier.ABSTRACT)) {
continue;
}
render(method);
}
}
private static Object getDefaultValue(ValueType type) {
if (type instanceof ValueType.Primitive) {
ValueType.Primitive primitive = (ValueType.Primitive)type;
switch (primitive.getKind()) {
case BOOLEAN:
return false;
case BYTE:
return (byte)0;
case SHORT:
return (short)0;
case INTEGER:
return 0;
case CHARACTER:
return '\0';
case LONG:
return 0L;
case FLOAT:
return 0F;
case DOUBLE:
return 0.0;
}
}
return null;
public NamingStrategy getNaming() {
return naming;
}
private void renderInitializer(RenderableMethod method) {

View File

@ -1,54 +0,0 @@
/*
* Copyright 2012 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.javascript.ast;
import java.util.List;
import org.teavm.model.ClassHolder;
import org.teavm.model.FieldHolder;
/**
*
* @author Alexey Andreev
*/
public class RenderableClass {
private ClassHolder metadata;
private List<RenderableMethod> methods;
private List<FieldHolder> fields;
public RenderableClass(ClassHolder metadata) {
this.metadata = metadata;
}
public ClassHolder getMetadata() {
return metadata;
}
public List<RenderableMethod> getMethods() {
return methods;
}
public void setMethods(List<RenderableMethod> methods) {
this.methods = methods;
}
public List<FieldHolder> getFields() {
return fields;
}
public void setFields(List<FieldHolder> fields) {
this.fields = fields;
}
}

View File

@ -1,9 +1,13 @@
package org.teavm.javascript.ni;
import org.teavm.codegen.NamingStrategy;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface GeneratorContext {
String getParameterName(int index);
NamingStrategy getNaming();
}

View File

@ -0,0 +1,35 @@
package org.teavm.model;
import java.util.Map;
import java.util.HashMap;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class AnnotationContainer {
private Map<String, AnnotationHolder> annotations = new HashMap<>();
public void add(AnnotationHolder annotation) {
if (annotations.containsKey(annotation.getType())) {
throw new IllegalArgumentException("Annotation of type " + annotation.getType() + " is already there");
}
annotations.put(annotation.getType(), annotation);
}
public AnnotationHolder get(String type) {
return annotations.get(type);
}
public void remove(AnnotationHolder annotation) {
AnnotationHolder existingAnnot = get(annotation.getType());
if (existingAnnot != annotation) {
throw new IllegalArgumentException("There is no such annotation");
}
annotations.remove(annotation.getType());
}
public void remove(String type) {
annotations.remove(type);
}
}

View File

@ -24,7 +24,7 @@ import java.util.Map;
*/
public class AnnotationHolder {
private String type;
private Map<String, AnnotationValueHolder> values = new HashMap<>();
private Map<String, AnnotationValue> values = new HashMap<>();
public AnnotationHolder(String type) {
this.type = type;
@ -34,7 +34,7 @@ public class AnnotationHolder {
return type;
}
public Map<String, AnnotationValueHolder> getValues() {
public Map<String, AnnotationValue> getValues() {
return values;
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright 2013 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.model;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class AnnotationValue {
private static final byte BOOLEAN = 0;
private static final byte BYTE = 1;
private static final byte SHORT = 2;
private static final byte INT = 3;
private static final byte LONG = 4;
private static final byte FLOAT = 5;
private static final byte DOUBLE = 6;
private static final byte STRING = 7;
private static final byte CLASS = 8;
private static final byte LIST = 9;
private static final byte ENUM = 10;
private static final byte ANNOTATION = 11;
private byte type;
private Object value;
public AnnotationValue(boolean value) {
this.type = BOOLEAN;
this.value = value;
}
public AnnotationValue(byte value) {
this.type = BYTE;
this.value = value;
}
public AnnotationValue(short value) {
this.type = SHORT;
this.value = value;
}
public AnnotationValue(int value) {
this.type = INT;
this.value = value;
}
public AnnotationValue(long value) {
this.type = LONG;
this.value = value;
}
public AnnotationValue(float value) {
this.type = FLOAT;
this.value = value;
}
public AnnotationValue(double value) {
this.type = DOUBLE;
this.value = value;
}
public AnnotationValue(String value) {
this.type = STRING;
this.value = value;
}
public AnnotationValue(ValueType value) {
this.type = CLASS;
this.value = value;
}
public AnnotationValue(List<AnnotationValue> value) {
this.type = LIST;
this.value = value;
}
public AnnotationValue(AnnotationHolder value) {
this.type = ANNOTATION;
this.value = value;
}
public AnnotationValue(FieldReference value) {
this.type = ENUM;
this.value = value;
}
public boolean getBoolean() {
if (type != BOOLEAN) {
throw new IllegalStateException("There is no boolean value");
}
return (Boolean)value;
}
public byte getByte() {
if (type != BYTE) {
throw new IllegalStateException("There is no byte value");
}
return (Byte)value;
}
public short getShort() {
if (type != SHORT) {
throw new IllegalStateException("There is no short value");
}
return (Short)value;
}
public int getInt() {
if (type != INT) {
throw new IllegalStateException("There is no int value");
}
return (Integer)value;
}
public long getLong() {
if (type != LONG) {
throw new IllegalStateException("There is no long value");
}
return (Long)value;
}
public float getFloat() {
if (type != FLOAT) {
throw new IllegalStateException("There is no float value");
}
return (Float)value;
}
public double getDouble() {
if (type != DOUBLE) {
throw new IllegalStateException("There is no double value");
}
return (Double)value;
}
public String getString() {
if (type != STRING) {
throw new IllegalStateException("There is no String value");
}
return (String)value;
}
public ValueType getJavaClass() {
if (type != CLASS) {
throw new IllegalStateException("There is no ValueType value");
}
return (ValueType)value;
}
@SuppressWarnings("unchecked")
public List<AnnotationValue> getList() {
if (type != LIST) {
throw new IllegalStateException("There is no List value");
}
return (List<AnnotationValue>)value;
}
public FieldReference getEnumValue() {
if (type != ENUM) {
throw new IllegalStateException("There is no enum value");
}
return (FieldReference)value;
}
public AnnotationHolder getAnnotation() {
if (type != ANNOTATION) {
throw new IllegalStateException("There is no annotation value");
}
return (AnnotationHolder)value;
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2013 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.model;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public interface AnnotationValueHolder {
boolean getBoolean();
byte getByte();
short getShort();
int getInteger();
long getLong();
float getFloat();
double getDouble();
String getString();
ValueType getJavaClass();
List<AnnotationValueHolder> getList();
String getEnumValue();
AnnotationHolder getAnnotation();
}

View File

@ -16,8 +16,6 @@
package org.teavm.model;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
*
@ -25,7 +23,7 @@ import java.util.Map;
*/
public abstract class ElementHolder {
private EnumSet<ElementModifier> modifiers = EnumSet.noneOf(ElementModifier.class);
private Map<String, AnnotationHolder> annotations = new HashMap<>();
private AnnotationContainer annotations = new AnnotationContainer();
private AccessLevel level = AccessLevel.PACKAGE_PRIVATE;
private String name;
@ -49,7 +47,7 @@ public abstract class ElementHolder {
return name;
}
public Map<String, AnnotationHolder> getAnnotations() {
public AnnotationContainer getAnnotations() {
return annotations;
}
}

View File

@ -1,16 +1,11 @@
package org.teavm.parsing;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import org.objectweb.asm.ClassReader;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.teavm.model.*;
import org.teavm.model.util.ListingBuilder;
import org.teavm.optimization.UnreachableBasicBlockEliminator;
/**
@ -28,6 +23,7 @@ public class Parser {
SSATransformer ssaProducer = new SSATransformer();
ssaProducer.transformToSSA(program, method.getParameterTypes());
method.setProgram(program);
parseAnnotations(method.getAnnotations(), node);
return method;
}
@ -50,6 +46,7 @@ public class Parser {
MethodNode methodNode = (MethodNode)obj;
cls.addMethod(parseMethod(methodNode));
}
parseAnnotations(cls.getAnnotations(), node);
return cls;
}
@ -58,6 +55,7 @@ public class Parser {
field.setType(ValueType.parse(node.desc));
field.setInitialValue(node.value);
parseModifiers(node.access, field);
parseAnnotations(field.getAnnotations(), node);
return field;
}
@ -120,59 +118,77 @@ public class Parser {
}
}
public static void main(String[] args) throws IOException {
Class<?>[] classesToParse = { Object.class, String.class, ArrayList.class,
StringBuilder.class, HashMap.class };
ClassLoader classLoader = Parser.class.getClassLoader();
for (Class<?> cls : classesToParse) {
try (InputStream input = classLoader.getResourceAsStream(cls.getName().replace('.', '/') + ".class")) {
ClassReader reader = new ClassReader(input);
ClassNode node = new ClassNode();
reader.accept(node, 0);
display(parseClass(node));
@SuppressWarnings("unchecked")
private static void parseAnnotations(AnnotationContainer annotations, MemberNode node) {
List<Object> annotNodes = new ArrayList<>();
if (node.visibleAnnotations != null) {
annotNodes.addAll(node.visibleAnnotations);
}
if (node.invisibleAnnotations != null) {
annotNodes.addAll(node.invisibleAnnotations);
}
for (Object obj : annotNodes) {
AnnotationNode annotNode = (AnnotationNode)obj;
String desc = annotNode.desc;
if (desc.startsWith("L") && desc.endsWith(";")) {
desc = desc.substring(1, desc.length() - 1);
}
desc = desc.replace('/', '.');
AnnotationHolder annot = new AnnotationHolder(desc);
parseAnnotationValues(annot, annotNode.values);
annotations.add(annot);
}
}
private static void display(ClassHolder cls) {
System.out.print(cls.getLevel());
for (ElementModifier modifier : cls.getModifiers()) {
System.out.print(" " + modifier);
private static void parseAnnotationValues(AnnotationHolder annot, List<Object> values) {
if (values == null) {
return;
}
System.out.print(" class " + cls.getName());
if (cls.getParent() != null) {
System.out.print(" extends " + cls.getParent());
for (int i = 0; i < values.size(); i += 2) {
String key = (String)values.get(i);
Object value = values.get(i + 1);
annot.getValues().put(key, parseAnnotationValue(value));
}
if (!cls.getInterfaces().isEmpty()) {
System.out.print(" implements ");
boolean first = true;
for (String iface : cls.getInterfaces()) {
if (!first) {
System.out.print(", ");
}
@SuppressWarnings("unchecked")
private static AnnotationValue parseAnnotationValue(Object value) {
if (value instanceof String[]) {
String[] enumInfo = (String[])value;
return new AnnotationValue(new FieldReference(enumInfo[0], enumInfo[1]));
} else if (value instanceof Type) {
Type cls = (Type)value;
return new AnnotationValue(ValueType.parse(cls.getDescriptor()));
} else if (value instanceof List<?>) {
List<?> originalList = (List<?>)value;
List<AnnotationValue> resultList = new ArrayList<>();
for (Object item : originalList) {
resultList.add(parseAnnotationValue(item));
}
return new AnnotationValue(resultList);
} else if (value instanceof AnnotationNode) {
AnnotationNode annotNode = (AnnotationNode)value;
AnnotationHolder annotation = new AnnotationHolder(annotNode.desc.replace('.', '/'));
parseAnnotationValues(annotation, annotNode.values);
return new AnnotationValue(annotation);
} else if (value instanceof String) {
return new AnnotationValue((String)value);
} else if (value instanceof Boolean) {
return new AnnotationValue((Boolean)value);
} else if (value instanceof Byte) {
return new AnnotationValue((Byte)value);
} else if (value instanceof Short) {
return new AnnotationValue((Short)value);
} else if (value instanceof Integer) {
return new AnnotationValue((Integer)value);
} else if (value instanceof Long) {
return new AnnotationValue((Long)value);
} else if (value instanceof Float) {
return new AnnotationValue((Float)value);
} else if (value instanceof Double) {
return new AnnotationValue((Double)value);
} else {
first = false;
}
System.out.print(iface);
throw new AssertionError();
}
}
System.out.println();
for (FieldHolder field : cls.getFields()) {
System.out.print(" " + field.getLevel());
for (ElementModifier modifier : field.getModifiers()) {
System.out.print(" " + modifier);
}
System.out.println(" " + field.getName() + " : " + field.getType());
}
ListingBuilder listingBuilder = new ListingBuilder();
for (MethodHolder method : cls.getMethods()) {
System.out.print(" " + method.getLevel());
for (ElementModifier modifier : method.getModifiers()) {
System.out.print(" " + modifier);
}
System.out.println(" " + method.getDescriptor());
System.out.println(listingBuilder.buildListing(method.getProgram(), " "));
}
System.out.println();
System.out.println();
}
}