From d63171e9356f8f66b6d46bc121b1aa9294c728bb Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 28 Oct 2013 22:59:56 +0400 Subject: [PATCH] Adds annotation parsing. Adds native method generation --- .../classlibgen/ClasslibTestGenerator.java | 16 +- .../javascript/DecompilationException.java | 25 +++ .../java/org/teavm/javascript/Decompiler.java | 149 ++++++++++++++ .../java/org/teavm/javascript/Renderer.java | 85 +------- .../teavm/javascript/ast/RenderableClass.java | 54 ----- .../teavm/javascript/ni/GeneratorContext.java | 4 + .../org/teavm/model/AnnotationContainer.java | 35 ++++ .../org/teavm/model/AnnotationHolder.java | 4 +- .../java/org/teavm/model/AnnotationValue.java | 184 ++++++++++++++++++ .../teavm/model/AnnotationValueHolder.java | 48 ----- .../java/org/teavm/model/ElementHolder.java | 6 +- .../main/java/org/teavm/parsing/Parser.java | 124 +++++++----- 12 files changed, 478 insertions(+), 256 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/DecompilationException.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/Decompiler.java delete mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/RenderableClass.java create mode 100644 teavm-core/src/main/java/org/teavm/model/AnnotationContainer.java create mode 100644 teavm-core/src/main/java/org/teavm/model/AnnotationValue.java delete mode 100644 teavm-core/src/main/java/org/teavm/model/AnnotationValueHolder.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java index bf68024ae..a57b48d75 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -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); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/DecompilationException.java b/teavm-core/src/main/java/org/teavm/javascript/DecompilationException.java new file mode 100644 index 000000000..1b9672c44 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/DecompilationException.java @@ -0,0 +1,25 @@ +package org.teavm.javascript; + +/** + * + * @author Alexey Andreev + */ +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); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java new file mode 100644 index 000000000..b3f609c97 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -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 + */ +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 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; + } + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index 260abd5fc..f3fadca98 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -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(); - } - - 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 modifiers = methodMetadata.getModifiers(); - if (modifiers.contains(ElementModifier.ABSTRACT)) { - continue; - } - render(method); - } + public SourceWriter getWriter() { + return writer; } - 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) { diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableClass.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableClass.java deleted file mode 100644 index d015feb5e..000000000 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableClass.java +++ /dev/null @@ -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 methods; - private List fields; - - public RenderableClass(ClassHolder metadata) { - this.metadata = metadata; - } - - public ClassHolder getMetadata() { - return metadata; - } - - public List getMethods() { - return methods; - } - - public void setMethods(List methods) { - this.methods = methods; - } - - public List getFields() { - return fields; - } - - public void setFields(List fields) { - this.fields = fields; - } -} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java index ab215c949..21e3d47c2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java @@ -1,9 +1,13 @@ package org.teavm.javascript.ni; +import org.teavm.codegen.NamingStrategy; + /** * * @author Alexey Andreev */ public interface GeneratorContext { String getParameterName(int index); + + NamingStrategy getNaming(); } diff --git a/teavm-core/src/main/java/org/teavm/model/AnnotationContainer.java b/teavm-core/src/main/java/org/teavm/model/AnnotationContainer.java new file mode 100644 index 000000000..fe6bcdba6 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/AnnotationContainer.java @@ -0,0 +1,35 @@ +package org.teavm.model; + +import java.util.Map; +import java.util.HashMap; + +/** + * + * @author Alexey Andreev + */ +public class AnnotationContainer { + private Map 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); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/AnnotationHolder.java b/teavm-core/src/main/java/org/teavm/model/AnnotationHolder.java index 57a4010de..daa7c6f5c 100644 --- a/teavm-core/src/main/java/org/teavm/model/AnnotationHolder.java +++ b/teavm-core/src/main/java/org/teavm/model/AnnotationHolder.java @@ -24,7 +24,7 @@ import java.util.Map; */ public class AnnotationHolder { private String type; - private Map values = new HashMap<>(); + private Map values = new HashMap<>(); public AnnotationHolder(String type) { this.type = type; @@ -34,7 +34,7 @@ public class AnnotationHolder { return type; } - public Map getValues() { + public Map getValues() { return values; } } diff --git a/teavm-core/src/main/java/org/teavm/model/AnnotationValue.java b/teavm-core/src/main/java/org/teavm/model/AnnotationValue.java new file mode 100644 index 000000000..5b775ac76 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/AnnotationValue.java @@ -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 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 getList() { + if (type != LIST) { + throw new IllegalStateException("There is no List value"); + } + return (List)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; + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/AnnotationValueHolder.java b/teavm-core/src/main/java/org/teavm/model/AnnotationValueHolder.java deleted file mode 100644 index 45397872b..000000000 --- a/teavm-core/src/main/java/org/teavm/model/AnnotationValueHolder.java +++ /dev/null @@ -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 getList(); - - String getEnumValue(); - - AnnotationHolder getAnnotation(); -} diff --git a/teavm-core/src/main/java/org/teavm/model/ElementHolder.java b/teavm-core/src/main/java/org/teavm/model/ElementHolder.java index be771fb19..290a186b5 100644 --- a/teavm-core/src/main/java/org/teavm/model/ElementHolder.java +++ b/teavm-core/src/main/java/org/teavm/model/ElementHolder.java @@ -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 modifiers = EnumSet.noneOf(ElementModifier.class); - private Map 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 getAnnotations() { + public AnnotationContainer getAnnotations() { return annotations; } } diff --git a/teavm-core/src/main/java/org/teavm/parsing/Parser.java b/teavm-core/src/main/java/org/teavm/parsing/Parser.java index add343368..e51426f0d 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/Parser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/Parser.java @@ -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 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 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(", "); - } else { - first = false; - } - System.out.print(iface); + } + + @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 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 { + 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(); } }