From 5641a09a0aac1f5ed25d4c5c3edd6fe84dcb7dd1 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Mon, 4 Nov 2013 19:29:07 +0400 Subject: [PATCH] Fixes native method decompilation. Refactoring --- .../java/lang/TObjectNativeGenerator.java | 6 +- .../classlibgen/ClasslibTestGenerator.java | 10 +- .../java/org/teavm/codegen/AliasProvider.java | 7 +- .../teavm/codegen/DefaultAliasProvider.java | 11 +- .../teavm/codegen/DefaultNamingStrategy.java | 26 +- .../org/teavm/codegen/NamingStrategy.java | 7 +- .../java/org/teavm/codegen/SourceWriter.java | 11 +- .../java/org/teavm/javascript/Decompiler.java | 348 ++++++++++++------ .../teavm/javascript/MethodDecompiler.java | 280 -------------- .../java/org/teavm/javascript/Optimizer.java | 6 +- .../java/org/teavm/javascript/Renderer.java | 178 ++++++--- .../teavm/javascript/StatementGenerator.java | 4 +- .../org/teavm/javascript/ast/ClassNode.java | 41 +++ .../org/teavm/javascript/ast/FieldNode.java | 41 +++ ...{RenderableMethod.java => MethodNode.java} | 36 +- .../javascript/ast/MethodNodeVisitor.java | 11 + .../javascript/ast/NativeMethodNode.java | 29 ++ .../teavm/javascript/ast/NodeModifier.java | 9 + .../javascript/ast/RegularMethodNode.java | 37 ++ .../teavm/javascript/ni/GeneratorContext.java | 4 - .../java/org/teavm/model/MethodReference.java | 4 + 21 files changed, 610 insertions(+), 496 deletions(-) delete mode 100644 teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/FieldNode.java rename teavm-core/src/main/java/org/teavm/javascript/ast/{RenderableMethod.java => MethodNode.java} (53%) create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/MethodNodeVisitor.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/NativeMethodNode.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java index b1c327120..94ee4d10e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java @@ -42,9 +42,9 @@ public class TObjectNativeGenerator implements Generator { String classClass = "java.lang.Class"; writer.append("var cls = ").append(thisArg).append(".$class.classObject;").newLine(); writer.append("if (cls === undefined) {").newLine().indent(); - writer.append("cls = ").appendClass(classClass) - .appendMethod(classClass, new MethodDescriptor("createNew", ValueType.object(classClass))) - .append("();").newLine(); + MethodReference createMethodRef = new MethodReference(classClass, new MethodDescriptor("createNew", + ValueType.object(classClass))); + writer.append("cls = ").appendClass(classClass).appendMethod(createMethodRef).append("();").newLine(); writer.append("cls.$data = ").append(thisArg).append(".$class;").newLine().outdent().append("}").newLine(); writer.append("return cls;").newLine(); } 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 a57b48d75..455fa93fc 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -4,6 +4,8 @@ import org.teavm.codegen.DefaultAliasProvider; import org.teavm.codegen.DefaultNamingStrategy; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.Decompiler; +import org.teavm.javascript.Renderer; +import org.teavm.javascript.ast.ClassNode; import org.teavm.model.ClassHolder; import org.teavm.model.resource.ClasspathClassHolderSource; @@ -14,12 +16,14 @@ import org.teavm.model.resource.ClasspathClassHolderSource; public class ClasslibTestGenerator { public static void main(String[] args) { ClasspathClassHolderSource source = new ClasspathClassHolderSource(); + Decompiler decompiler = new Decompiler(source); + ClassHolder cls = source.getClassHolder("java.lang.Object"); + ClassNode clsNode = decompiler.decompile(cls); DefaultAliasProvider aliasProvider = new DefaultAliasProvider(); DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source); SourceWriter writer = new SourceWriter(naming); - Decompiler decompiler = new Decompiler(source, naming, writer); - ClassHolder cls = source.getClassHolder("java.lang.Object"); - decompiler.decompile(cls); + Renderer renderer = new Renderer(writer, source); + renderer.render(clsNode); System.out.println(writer); } } diff --git a/teavm-core/src/main/java/org/teavm/codegen/AliasProvider.java b/teavm-core/src/main/java/org/teavm/codegen/AliasProvider.java index 497c013e9..0f521f5e3 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/AliasProvider.java +++ b/teavm-core/src/main/java/org/teavm/codegen/AliasProvider.java @@ -15,16 +15,17 @@ */ package org.teavm.codegen; -import org.teavm.model.MethodDescriptor; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; /** * * @author Alexey Andreev */ public interface AliasProvider { - String getAlias(String className, String fieldName); + String getAlias(FieldReference field); - String getAlias(String className, MethodDescriptor methodDesc); + String getAlias(MethodReference method); String getAlias(String className); } diff --git a/teavm-core/src/main/java/org/teavm/codegen/DefaultAliasProvider.java b/teavm-core/src/main/java/org/teavm/codegen/DefaultAliasProvider.java index 879e95fe2..22d4a5f7e 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/DefaultAliasProvider.java +++ b/teavm-core/src/main/java/org/teavm/codegen/DefaultAliasProvider.java @@ -15,7 +15,8 @@ */ package org.teavm.codegen; -import org.teavm.model.MethodDescriptor; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; /** * @@ -48,8 +49,8 @@ public class DefaultAliasProvider implements AliasProvider { } @Override - public String getAlias(String cls, MethodDescriptor method) { - String alias = method.getName(); + public String getAlias(MethodReference method) { + String alias = method.getDescriptor().getName(); if (alias.equals("")) { alias = "$init"; } else if (alias.equals("")) { @@ -59,7 +60,7 @@ public class DefaultAliasProvider implements AliasProvider { } @Override - public String getAlias(String cls, String field) { - return field + (lastSuffix++); + public String getAlias(FieldReference field) { + return field.getFieldName() + (lastSuffix++); } } diff --git a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java index aef4babd2..bb58b7361 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java +++ b/teavm-core/src/main/java/org/teavm/codegen/DefaultNamingStrategy.java @@ -47,19 +47,19 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getNameFor(String cls, MethodDescriptor method) { - if (method.getName().equals("")) { + public String getNameFor(MethodReference method) { + if (method.getDescriptor().getName().equals("")) { return "$clinit"; } - ClassHolder clsHolder = classSource.getClassHolder(cls); - MethodHolder methodHolder = clsHolder.getMethod(method); + ClassHolder clsHolder = classSource.getClassHolder(method.getClassName()); + MethodHolder methodHolder = clsHolder.getMethod(method.getDescriptor()); if (methodHolder.getModifiers().contains(ElementModifier.STATIC) || - method.getName().equals("") || + method.getDescriptor().getName().equals("") || methodHolder.getLevel() == AccessLevel.PRIVATE) { - String key = cls + "#" + method.toString(); + String key = method.getClassName() + "#" + method.toString(); String alias = privateAliases.get(key); if (alias == null) { - alias = aliasProvider.getAlias(cls, method); + alias = aliasProvider.getAlias(method); privateAliases.put(key, alias); } return alias; @@ -67,7 +67,7 @@ public class DefaultNamingStrategy implements NamingStrategy { String key = method.toString(); String alias = aliases.get(key); if (alias == null) { - alias = aliasProvider.getAlias(cls, method); + alias = aliasProvider.getAlias(method); aliases.put(key, alias); } return alias; @@ -75,11 +75,11 @@ public class DefaultNamingStrategy implements NamingStrategy { } @Override - public String getNameFor(String cls, String field) { - String realCls = getRealFieldOwner(cls, field); - if (!realCls.equals(cls)) { - String alias = getNameFor(realCls, field); - fieldAliases.put(cls + "#" + field, alias); + public String getNameFor(FieldReference field) { + String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName()); + if (!realCls.equals(field.getClassName())) { + String alias = getNameFor(new FieldReference(realCls, field.getFieldName())); + fieldAliases.put(field.getClassName() + "#" + field, alias); return alias; } else { String key = realCls + "#" + field; diff --git a/teavm-core/src/main/java/org/teavm/codegen/NamingStrategy.java b/teavm-core/src/main/java/org/teavm/codegen/NamingStrategy.java index 9d7c5a953..a770d3f21 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/NamingStrategy.java +++ b/teavm-core/src/main/java/org/teavm/codegen/NamingStrategy.java @@ -15,7 +15,8 @@ */ package org.teavm.codegen; -import org.teavm.model.MethodDescriptor; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; /** * @@ -24,7 +25,7 @@ import org.teavm.model.MethodDescriptor; public interface NamingStrategy { String getNameFor(String cls); - String getNameFor(String cls, MethodDescriptor method); + String getNameFor(MethodReference method); - String getNameFor(String cls, String field); + String getNameFor(FieldReference field); } diff --git a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java index 48d18d999..83a88c2c9 100644 --- a/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java +++ b/teavm-core/src/main/java/org/teavm/codegen/SourceWriter.java @@ -1,6 +1,7 @@ package org.teavm.codegen; -import org.teavm.model.MethodDescriptor; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; /** * @@ -50,15 +51,15 @@ public class SourceWriter { return this; } - public SourceWriter appendField(String cls, String field) { + public SourceWriter appendField(FieldReference field) { appendIndent(); - sb.append(naming.getNameFor(cls, field)); + sb.append(naming.getNameFor(field)); return this; } - public SourceWriter appendMethod(String cls, MethodDescriptor method) { + public SourceWriter appendMethod(MethodReference method) { appendIndent(); - sb.append(naming.getNameFor(cls, method)); + sb.append(naming.getNameFor(method)); return this; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index b3f609c97..98441fa70 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -1,103 +1,98 @@ +/* + * Copyright 2011 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; -import java.util.Set; -import org.teavm.codegen.NamingStrategy; -import org.teavm.codegen.SourceWriter; -import org.teavm.javascript.ast.RenderableMethod; +import java.util.*; +import org.teavm.common.*; +import org.teavm.javascript.ast.*; import org.teavm.javascript.ni.GeneratedBy; import org.teavm.javascript.ni.Generator; -import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.*; +import org.teavm.model.util.ProgramUtils; /** * - * @author Alexey Andreev + * @author Alexey Andreev */ public class Decompiler { - private SourceWriter writer; - private MethodDecompiler methodDecompiler; - private NamingStrategy naming; - private Renderer renderer; + private ClassHolderSource classSource; + private Graph graph; + private LoopGraph loopGraph; + private GraphIndexer indexer; + private int[] loops; + private int[] loopSuccessors; + private Block[] blockMap; + private int lastBlockId; + private RangeTree codeTree; + private RangeTree.Node currentNode; + private RangeTree.Node parentNode; - 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 Decompiler(ClassHolderSource classSource) { + this.classSource = classSource; } - 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(); + public int getGraphSize() { + return this.graph.size(); + } - 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(); - } + class Block { + public final IdentifiedStatement statement; + public final List body; + public final int end; + public final int start; - 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; + public Block(IdentifiedStatement statement, List body, int start, int end) { + this.statement = statement; + this.body = body; + this.start = start; + this.end = end; } - for (String iface : cls.getInterfaces()) { - if (!first) { - writer.append(", "); - } - first = false; - writer.appendClass(iface); + } + + public ClassNode decompile(ClassHolder cls) { + ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent()); + for (FieldHolder field : cls.getFields()) { + FieldNode fieldNode = new FieldNode(field.getName(), field.getType()); + fieldNode.getModifiers().addAll(mapModifiers(field.getModifiers())); + fieldNode.setInitialValue(field.getInitialValue()); + clsNode.getFields().add(fieldNode); } - writer.append("]"); - writer.append(" };").newLine(); for (MethodHolder method : cls.getMethods()) { - Set modifiers = method.getModifiers(); - if (modifiers.contains(ElementModifier.ABSTRACT)) { + if (method.getModifiers().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); - } + clsNode.getMethods().add(decompile(method)); } + clsNode.getInterfaces().addAll(cls.getInterfaces()); + return clsNode; } - private void generateNativeMethod(String generatorClassName, MethodHolder method) { + public MethodNode decompile(MethodHolder method) { + return method.getModifiers().contains(ElementModifier.NATIVE) ? + decompileNative(method) : decompileRegular(method); + } + + public NativeMethodNode decompileNative(MethodHolder method) { + AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName()); + if (annotHolder == null) { + throw new DecompilationException("Method " + method.getOwner().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(); Generator generator; try { Class generatorClass = Class.forName(generatorClassName); @@ -106,44 +101,183 @@ public class Decompiler { 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); + NativeMethodNode methodNode = new NativeMethodNode(new MethodReference(method.getOwner().getName(), + method.getDescriptor())); + methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); + methodNode.setGenerator(generator); + return methodNode; } - 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; + public RegularMethodNode decompileRegular(MethodHolder method) { + lastBlockId = 1; + indexer = new GraphIndexer(ProgramUtils.buildControlFlowGraph(method.getProgram())); + graph = indexer.getGraph(); + loopGraph = new LoopGraph(this.graph); + unflatCode(); + Program program = method.getProgram(); + blockMap = new Block[program.basicBlockCount() * 2 + 1]; + Deque stack = new ArrayDeque<>(); + BlockStatement rootStmt = new BlockStatement(); + rootStmt.setId("root"); + stack.push(new Block(rootStmt, rootStmt.getBody(), -1, -1)); + StatementGenerator generator = new StatementGenerator(); + generator.classSource = classSource; + generator.program = program; + generator.blockMap = blockMap; + generator.indexer = indexer; + generator.outgoings = getPhiOutgoings(program); + parentNode = codeTree.getRoot(); + currentNode = parentNode.getFirstChild(); + for (int i = 0; i < this.graph.size(); ++i) { + Block block = stack.peek(); + while (block.end == i) { + stack.pop(); + block = stack.peek(); + } + while (parentNode.getEnd() == i) { + currentNode = parentNode.getNext(); + parentNode = parentNode.getParent(); + } + for (Block newBlock : createBlocks(i)) { + block.body.add(newBlock.statement); + stack.push(newBlock); + block = newBlock; + } + int node = i < indexer.size() ? indexer.nodeAt(i) : -1; + int next = i + 1; + int head = loops[i]; + if (head != -1 && loopSuccessors[head] == next) { + next = head; + } + if (node >= 0) { + generator.currentBlock = program.basicBlockAt(node); + generator.nextBlock = next < indexer.size() ? program.basicBlockAt(indexer.nodeAt(next)) : null; + generator.statements.clear(); + for (Instruction insn : generator.currentBlock.getInstructions()) { + insn.acceptVisitor(generator); + } + block.body.addAll(generator.statements); } } - return null; + SequentialStatement result = new SequentialStatement(); + result.getSequence().addAll(rootStmt.getBody()); + MethodReference reference = new MethodReference(method.getOwner().getName(), method.getDescriptor()); + RegularMethodNode methodNode = new RegularMethodNode(reference); + methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); + methodNode.setBody(result); + methodNode.setVariableCount(program.variableCount()); + Optimizer optimizer = new Optimizer(); + optimizer.optimize(methodNode); + return methodNode; } - private class Context implements GeneratorContext { - @Override - public String getParameterName(int index) { - return renderer.variableName(index); + private Set mapModifiers(Set modifiers) { + Set result = EnumSet.noneOf(NodeModifier.class); + if (modifiers.contains(ElementModifier.STATIC)) { + result.add(NodeModifier.STATIC); + } + return result; + } + + private Incoming[][] getPhiOutgoings(Program program) { + List> outgoings = new ArrayList<>(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + outgoings.add(new ArrayList()); + } + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock basicBlock = program.basicBlockAt(i); + for (Phi phi : basicBlock.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + outgoings.get(incoming.getSource().getIndex()).add(incoming); + } + } + } + Incoming[][] result = new Incoming[outgoings.size()][]; + for (int i = 0; i < outgoings.size(); ++i) { + result[i] = outgoings.get(i).toArray(new Incoming[0]); + } + return result; + } + + private List createBlocks(int start) { + List result = new ArrayList<>(); + while (currentNode != null && currentNode.getStart() == start) { + Block block; + IdentifiedStatement statement; + if (loopSuccessors[start] == currentNode.getEnd()) { + WhileStatement whileStatement = new WhileStatement(); + statement = whileStatement; + block = new Block(statement, whileStatement.getBody(), start, + currentNode.getEnd()); + } else { + BlockStatement blockStatement = new BlockStatement(); + statement = blockStatement; + block = new Block(statement, blockStatement.getBody(), start, + currentNode.getEnd()); + } + result.add(block); + int mappedIndex = indexer.nodeAt(currentNode.getEnd()); + if (mappedIndex >= 0 && (blockMap[mappedIndex] == null || + !(blockMap[mappedIndex].statement instanceof WhileStatement))) { + blockMap[mappedIndex] = block; + } + if (loopSuccessors[start] == currentNode.getEnd()) { + blockMap[indexer.nodeAt(start)] = block; + } + parentNode = currentNode; + currentNode = currentNode.getFirstChild(); + } + for (Block block : result) { + block.statement.setId("block" + lastBlockId++); + } + return result; + } + + private void unflatCode() { + Graph graph = this.graph; + int sz = graph.size(); + + // Find where each loop ends + // + int[] loopSuccessors = new int[sz]; + Arrays.fill(loopSuccessors, sz + 1); + for (int node = 0; node < sz; ++node) { + Loop loop = loopGraph.loopAt(node); + while (loop != null) { + loopSuccessors[loop.getHead()] = node + 1; + loop = loop.getParent(); + } } - @Override - public NamingStrategy getNaming() { - return naming; + // For each node find head of loop this node belongs to. + // + int[] loops = new int[sz]; + Arrays.fill(loops, -1); + for (int head = 0; head < sz; ++head) { + int end = loopSuccessors[head]; + if (end > sz) { + continue; + } + for (int node = head + 1; node < end; ++node) { + loops[node] = head; + } } + + List ranges = new ArrayList<>(); + for (int node = 0; node < sz; ++node) { + if (loopSuccessors[node] <= sz) { + ranges.add(new RangeTree.Range(node, loopSuccessors[node])); + } + int start = sz; + for (int prev : graph.incomingEdges(node)) { + start = Math.min(start, prev); + } + if (start < node - 1) { + ranges.add(new RangeTree.Range(start, node)); + } + } + codeTree = new RangeTree(sz + 1, ranges); + this.loopSuccessors = loopSuccessors; + this.loops = loops; } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java b/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java deleted file mode 100644 index c40b98489..000000000 --- a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2011 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; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.*; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.tree.ClassNode; -import org.teavm.codegen.DefaultAliasProvider; -import org.teavm.codegen.DefaultNamingStrategy; -import org.teavm.codegen.SourceWriter; -import org.teavm.common.*; -import org.teavm.javascript.ast.*; -import org.teavm.model.*; -import org.teavm.model.util.ProgramUtils; -import org.teavm.parsing.Parser; - -/** - * - * @author Alexey Andreev - */ -public class MethodDecompiler { - private ClassHolderSource classSource; - private Graph graph; - private LoopGraph loopGraph; - private GraphIndexer indexer; - private int[] loops; - private int[] loopSuccessors; - private Block[] blockMap; - private int lastBlockId; - private RangeTree codeTree; - private RangeTree.Node currentNode; - private RangeTree.Node parentNode; - - public MethodDecompiler(ClassHolderSource classSource) { - this.classSource = classSource; - } - - public int getGraphSize() { - return this.graph.size(); - } - - class Block { - public final IdentifiedStatement statement; - public final List body; - public final int end; - public final int start; - - public Block(IdentifiedStatement statement, List body, int start, int end) { - this.statement = statement; - this.body = body; - this.start = start; - this.end = end; - } - } - - public RenderableMethod decompile(MethodHolder method) { - lastBlockId = 1; - indexer = new GraphIndexer(ProgramUtils.buildControlFlowGraph(method.getProgram())); - graph = indexer.getGraph(); - loopGraph = new LoopGraph(this.graph); - unflatCode(); - Program program = method.getProgram(); - blockMap = new Block[program.basicBlockCount() * 2 + 1]; - Deque stack = new ArrayDeque<>(); - BlockStatement rootStmt = new BlockStatement(); - rootStmt.setId("root"); - stack.push(new Block(rootStmt, rootStmt.getBody(), -1, -1)); - StatementGenerator generator = new StatementGenerator(); - generator.classSource = classSource; - generator.program = program; - generator.blockMap = blockMap; - generator.indexer = indexer; - generator.outgoings = getPhiOutgoings(program); - parentNode = codeTree.getRoot(); - currentNode = parentNode.getFirstChild(); - for (int i = 0; i < this.graph.size(); ++i) { - Block block = stack.peek(); - while (block.end == i) { - stack.pop(); - block = stack.peek(); - } - while (parentNode.getEnd() == i) { - currentNode = parentNode.getNext(); - parentNode = parentNode.getParent(); - } - for (Block newBlock : createBlocks(i)) { - block.body.add(newBlock.statement); - stack.push(newBlock); - block = newBlock; - } - int node = i < indexer.size() ? indexer.nodeAt(i) : -1; - int next = i + 1; - int head = loops[i]; - if (head != -1 && loopSuccessors[head] == next) { - next = head; - } - if (node >= 0) { - generator.currentBlock = program.basicBlockAt(node); - generator.nextBlock = next < indexer.size() ? program.basicBlockAt(indexer.nodeAt(next)) : null; - generator.statements.clear(); - for (Instruction insn : generator.currentBlock.getInstructions()) { - insn.acceptVisitor(generator); - } - block.body.addAll(generator.statements); - } - } - SequentialStatement result = new SequentialStatement(); - result.getSequence().addAll(rootStmt.getBody()); - RenderableMethod renderable = new RenderableMethod(method); - renderable.setBody(result); - renderable.setVariableCount(program.variableCount()); - return renderable; - } - - private Incoming[][] getPhiOutgoings(Program program) { - List> outgoings = new ArrayList<>(); - for (int i = 0; i < program.basicBlockCount(); ++i) { - outgoings.add(new ArrayList()); - } - for (int i = 0; i < program.basicBlockCount(); ++i) { - BasicBlock basicBlock = program.basicBlockAt(i); - for (Phi phi : basicBlock.getPhis()) { - for (Incoming incoming : phi.getIncomings()) { - outgoings.get(incoming.getSource().getIndex()).add(incoming); - } - } - } - Incoming[][] result = new Incoming[outgoings.size()][]; - for (int i = 0; i < outgoings.size(); ++i) { - result[i] = outgoings.get(i).toArray(new Incoming[0]); - } - return result; - } - - private List createBlocks(int start) { - List result = new ArrayList<>(); - while (currentNode != null && currentNode.getStart() == start) { - Block block; - IdentifiedStatement statement; - if (loopSuccessors[start] == currentNode.getEnd()) { - WhileStatement whileStatement = new WhileStatement(); - statement = whileStatement; - block = new Block(statement, whileStatement.getBody(), start, - currentNode.getEnd()); - } else { - BlockStatement blockStatement = new BlockStatement(); - statement = blockStatement; - block = new Block(statement, blockStatement.getBody(), start, - currentNode.getEnd()); - } - result.add(block); - int mappedIndex = indexer.nodeAt(currentNode.getEnd()); - if (mappedIndex >= 0 && (blockMap[mappedIndex] == null || - !(blockMap[mappedIndex].statement instanceof WhileStatement))) { - blockMap[mappedIndex] = block; - } - if (loopSuccessors[start] == currentNode.getEnd()) { - blockMap[indexer.nodeAt(start)] = block; - } - parentNode = currentNode; - currentNode = currentNode.getFirstChild(); - } - for (Block block : result) { - block.statement.setId("block" + lastBlockId++); - } - return result; - } - - private void unflatCode() { - Graph graph = this.graph; - int sz = graph.size(); - - // Find where each loop ends - // - int[] loopSuccessors = new int[sz]; - Arrays.fill(loopSuccessors, sz + 1); - for (int node = 0; node < sz; ++node) { - Loop loop = loopGraph.loopAt(node); - while (loop != null) { - loopSuccessors[loop.getHead()] = node + 1; - loop = loop.getParent(); - } - } - - // For each node find head of loop this node belongs to. - // - int[] loops = new int[sz]; - Arrays.fill(loops, -1); - for (int head = 0; head < sz; ++head) { - int end = loopSuccessors[head]; - if (end > sz) { - continue; - } - for (int node = head + 1; node < end; ++node) { - loops[node] = head; - } - } - - List ranges = new ArrayList<>(); - for (int node = 0; node < sz; ++node) { - if (loopSuccessors[node] <= sz) { - ranges.add(new RangeTree.Range(node, loopSuccessors[node])); - } - int start = sz; - for (int prev : graph.incomingEdges(node)) { - start = Math.min(start, prev); - } - if (start < node - 1) { - ranges.add(new RangeTree.Range(start, node)); - } - } - codeTree = new RangeTree(sz + 1, ranges); - this.loopSuccessors = loopSuccessors; - this.loops = loops; - } - - public static void main(String... args) throws IOException { - MutableClassHolderSource source = new MutableClassHolderSource(); - ClassHolder arrayListCls = Parser.parseClass(readClass(ArrayList.class.getName())); - source.putClassHolder(arrayListCls); - source.putClassHolder(Parser.parseClass(readClass(AbstractList.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(StringBuilder.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(IllegalArgumentException.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(IndexOutOfBoundsException.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(Exception.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(RuntimeException.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(Throwable.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(System.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(Object.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(Arrays.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$ListItr"))); - source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$Itr"))); - source.putClassHolder(Parser.parseClass(readClass(ArrayList.class.getName() + "$SubList"))); - source.putClassHolder(Parser.parseClass(readClass(Collection.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(ObjectOutputStream.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(ObjectInputStream.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(ConcurrentModificationException.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(Math.class.getName()))); - source.putClassHolder(Parser.parseClass(readClass(OutOfMemoryError.class.getName()))); - 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(); - for (MethodHolder method : arrayListCls.getMethods()) { - RenderableMethod renderableMethod = decompiler.decompile(method); - optimizer.optimize(renderableMethod); - renderer.render(renderableMethod); - } - System.out.println(writer); - } - - private static ClassNode readClass(String className) throws IOException { - ClassLoader classLoader = MethodDecompiler.class.getClassLoader(); - try (InputStream input = classLoader.getResourceAsStream(className.replace('.', '/') + ".class")) { - ClassReader reader = new ClassReader(input); - ClassNode node = new ClassNode(); - reader.accept(node, 0); - return node; - } - } -} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java index 3b39ad247..98a159b0f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Optimizer.java @@ -15,7 +15,7 @@ */ package org.teavm.javascript; -import org.teavm.javascript.ast.RenderableMethod; +import org.teavm.javascript.ast.RegularMethodNode; import org.teavm.javascript.ast.Statement; import org.teavm.model.MethodHolder; @@ -33,13 +33,13 @@ public class Optimizer { return optimizer.resultStmt; } - public void optimize(RenderableMethod method) { + public void optimize(RegularMethodNode method) { ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariableCount()); method.getBody().acceptVisitor(stats); OptimizingVisitor optimizer = new OptimizingVisitor(stats); method.getBody().acceptVisitor(optimizer); method.setBody(optimizer.resultStmt); - int paramCount = method.getMetadata().parameterCount(); + int paramCount = method.getReference().parameterCount(); UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariableCount()); method.getBody().acceptVisitor(unusedEliminator); method.setVariableCount(unusedEliminator.lastIndex); 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 f3fadca98..56e8eb843 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -18,6 +18,7 @@ package org.teavm.javascript; import org.teavm.codegen.NamingStrategy; import org.teavm.codegen.SourceWriter; import org.teavm.javascript.ast.*; +import org.teavm.javascript.ni.GeneratorContext; import org.teavm.model.*; /** @@ -44,21 +45,97 @@ public class Renderer implements ExprVisitor, StatementVisitor { return naming; } - private void renderInitializer(RenderableMethod method) { - MethodHolder metadata = method.getMetadata(); - writer.appendClass(metadata.getOwner().getName()).append(".") - .appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()) - .append(" = function("); - for (int i = 1; i <= metadata.parameterCount(); ++i) { + public void render(ClassNode cls) { + writer.appendClass(cls.getName()).append(" = function() {").indent().newLine(); + for (FieldNode field : cls.getFields()) { + if (field.getModifiers().contains(NodeModifier.STATIC)) { + continue; + } + Object value = field.getInitialValue(); + if (value == null) { + value = getDefaultValue(field.getType()); + } + writer.append("this.").appendField(new FieldReference(cls.getName(), field.getName())).append(" = ") + .append(constantToString(value)).append(";").newLine(); + } + writer.append("this.$class = ").appendClass(cls.getName()).append(";").newLine(); + writer.outdent().append("}").newLine(); + + for (FieldNode field : cls.getFields()) { + if (!field.getModifiers().contains(NodeModifier.STATIC)) { + continue; + } + Object value = field.getInitialValue(); + if (value == null) { + value = getDefaultValue(field.getType()); + } + writer.appendClass(cls.getName()).append('.') + .appendField(new FieldReference(cls.getName(), field.getName())).append(" = ") + .append(constantToString(value)).append(";").newLine(); + } + + writer.appendClass(cls.getName()).append(".prototype = new ") + .append(cls.getParentName() != null ? naming.getNameFor(cls.getParentName()) : + "Object").append("();").newLine(); + writer.appendClass(cls.getName()).append(".$meta = { "); + writer.append("supertypes : ["); + boolean first = true; + if (cls.getParentName() != null) { + writer.appendClass(cls.getParentName()); + first = false; + } + for (String iface : cls.getInterfaces()) { + if (!first) { + writer.append(", "); + } + first = false; + writer.appendClass(iface); + } + writer.append("]"); + writer.append(" };").newLine(); + for (MethodNode method : cls.getMethods()) { + 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; + } + + private void renderInitializer(MethodNode method) { + MethodReference ref = method.getReference(); + writer.appendClass(ref.getClassName()).append(".").appendMethod(ref).append(" = function("); + for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1) { writer.append(", "); } writer.append(variableName(i)); } writer.append(") {").newLine().indent(); - writer.append("var result = new ").appendClass(metadata.getOwner().getName()).append("();").newLine(); - writer.append("result.").appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append("("); - for (int i = 1; i <= metadata.parameterCount(); ++i) { + writer.append("var result = new ").appendClass(ref.getClassName()).append("();").newLine(); + writer.append("result.").appendMethod(ref).append("("); + for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1) { writer.append(", "); } @@ -69,34 +146,33 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } - public void render(RenderableMethod method) { - MethodHolder metadata = method.getMetadata(); - if (metadata.getName().equals("")) { + public void render(MethodNode method) { + MethodReference ref = method.getReference(); + if (ref.getDescriptor().getName().equals("")) { renderInitializer(method); } renderWorkingMethod(method); int startParam = 0; - if (metadata.getModifiers().contains(ElementModifier.STATIC)) { + if (method.getModifiers().contains(ElementModifier.STATIC)) { startParam = 1; } - writer.appendClass(metadata.getOwner().getName()).append('.'); + writer.appendClass(ref.getClassName()).append('.'); if (startParam == 0) { writer.append("prototype."); } - writer.appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append(" = function("); - for (int i = 1; i <= metadata.parameterCount(); ++i) { + writer.appendMethod(ref).append(" = function("); + for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1) { writer.append(", "); } writer.append(variableName(i)); } writer.append(") {").newLine().indent(); - writer.append("return ").appendClass(metadata.getOwner().getName()).append('_') - .appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append("("); + writer.append("return ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append("("); if (startParam == 0) { writer.append("this"); } - for (int i = 1; i <= metadata.parameterCount(); ++i) { + for (int i = 1; i <= ref.parameterCount(); ++i) { if (i > 1 || startParam == 0) { writer.append(", "); } @@ -106,39 +182,56 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } - private void renderWorkingMethod(RenderableMethod method) { - MethodHolder metadata = method.getMetadata(); - writer.append("function ").appendClass(metadata.getOwner().getName()).append('_') - .appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append('('); + private void renderWorkingMethod(MethodNode method) { + MethodReference ref = method.getReference(); + writer.append("function ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append('('); int startParam = 0; - if (metadata.getModifiers().contains(ElementModifier.STATIC)) { + if (method.getModifiers().contains(ElementModifier.STATIC)) { startParam = 1; } - for (int i = startParam; i <= metadata.parameterCount(); ++i) { + for (int i = startParam; i <= ref.parameterCount(); ++i) { if (i > startParam) { writer.append(", "); } writer.append(variableName(i)); } writer.append(") {").newLine().indent(); - int variableCount = method.getVariableCount(); - boolean hasVars = variableCount > metadata.parameterCount() + 1; - if (hasVars) { - writer.append("var "); - boolean first = true; - for (int i = metadata.parameterCount() + 1; i < variableCount; ++i) { - if (!first) { - writer.append(", "); - } - first = false; - writer.append(variableName(i)); - } - writer.append(";").newLine(); - } - method.getBody().acceptVisitor(this); + method.acceptVisitor(new MethodBodyRenderer()); writer.outdent().append("}").newLine(); } + private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { + @Override + public void visit(NativeMethodNode methodNode) { + methodNode.getGenerator().generate(this, writer, methodNode.getReference()); + } + + @Override + public void visit(RegularMethodNode method) { + MethodReference ref = method.getReference(); + int variableCount = method.getVariableCount(); + boolean hasVars = variableCount > ref.parameterCount() + 1; + if (hasVars) { + writer.append("var "); + boolean first = true; + for (int i = ref.parameterCount() + 1; i < variableCount; ++i) { + if (!first) { + writer.append(", "); + } + first = false; + writer.append(variableName(i)); + } + writer.append(";").newLine(); + } + method.getBody().acceptVisitor(Renderer.this); + } + + @Override + public String getParameterName(int index) { + return variableName(index); + } + }; + @Override public void visit(AssignmentStatement statement) { if (statement.getLeftValue() != null) { @@ -597,7 +690,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(InvocationExpr expr) { String className = naming.getNameFor(expr.getClassName()); - String name = naming.getNameFor(expr.getClassName(), expr.getMethod()); + String name = naming.getNameFor(new MethodReference(expr.getClassName(), expr.getMethod())); switch (expr.getType()) { case STATIC: writer.append(className).append("_").append(name).append("("); @@ -645,8 +738,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { @Override public void visit(QualificationExpr expr) { expr.getQualified().acceptVisitor(this); - writer.append('.'); - writer.append(naming.getNameFor(expr.getClassName(), expr.getField())); + writer.append('.').appendField(new FieldReference(expr.getClassName(), expr.getField())); } @Override diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index 077b9d35b..ddc02fa91 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -18,7 +18,7 @@ public class StatementGenerator implements InstructionVisitor { GraphIndexer indexer; BasicBlock nextBlock; BasicBlock currentBlock; - MethodDecompiler.Block[] blockMap; + Decompiler.Block[] blockMap; Program program; ClassHolderSource classSource; Incoming[][] outgoings; @@ -532,7 +532,7 @@ public class StatementGenerator implements InstructionVisitor { if (nextBlock == target) { return null; } - MethodDecompiler.Block block = blockMap[target.getIndex()]; + Decompiler.Block block = blockMap[target.getIndex()]; if (target.getIndex() == indexer.nodeAt(block.end)) { BreakStatement breakStmt = new BreakStatement(); breakStmt.setTarget(block.statement); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java new file mode 100644 index 000000000..0329d0144 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/ClassNode.java @@ -0,0 +1,41 @@ +package org.teavm.javascript.ast; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Alexey Andreev + */ +public class ClassNode { + private String name; + private String parentName; + private List fields = new ArrayList<>(); + private List methods = new ArrayList<>(); + private List interfaces = new ArrayList<>(); + + public ClassNode(String name, String parentName) { + this.name = name; + this.parentName = parentName; + } + + public String getName() { + return name; + } + + public String getParentName() { + return parentName; + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + + public List getInterfaces() { + return interfaces; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/FieldNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/FieldNode.java new file mode 100644 index 000000000..204379b46 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/FieldNode.java @@ -0,0 +1,41 @@ +package org.teavm.javascript.ast; + +import java.util.EnumSet; +import java.util.Set; +import org.teavm.model.ValueType; + +/** + * + * @author Alexey Andreev + */ +public class FieldNode { + private String name; + private ValueType type; + private Set modifiers = EnumSet.noneOf(NodeModifier.class); + private Object initialValue; + + public FieldNode(String name, ValueType type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Set getModifiers() { + return modifiers; + } + + public ValueType getType() { + return type; + } + + public Object getInitialValue() { + return initialValue; + } + + public void setInitialValue(Object initialValue) { + this.initialValue = initialValue; + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java similarity index 53% rename from teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java rename to teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java index 6ac74590d..aeb47d5c6 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNode.java @@ -15,38 +15,30 @@ */ package org.teavm.javascript.ast; -import org.teavm.model.MethodHolder; +import java.util.EnumSet; +import java.util.Set; +import org.teavm.model.MethodReference; /** * * @author Alexey Andreev */ -public class RenderableMethod { - private Statement body; - private MethodHolder metadata; - private int variableCount; +public abstract class MethodNode { + private MethodReference reference; + private Set modifiers = EnumSet.noneOf(NodeModifier.class); - public RenderableMethod(MethodHolder metadata) { - this.metadata = metadata; + public MethodNode(MethodReference reference) { + this.reference = reference; + this.modifiers = EnumSet.copyOf(modifiers); } - public Statement getBody() { - return body; + public MethodReference getReference() { + return reference; } - public void setBody(Statement body) { - this.body = body; + public Set getModifiers() { + return modifiers; } - public MethodHolder getMetadata() { - return metadata; - } - - public int getVariableCount() { - return variableCount; - } - - public void setVariableCount(int variableCount) { - this.variableCount = variableCount; - } + public abstract void acceptVisitor(MethodNodeVisitor visitor); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNodeVisitor.java b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNodeVisitor.java new file mode 100644 index 000000000..e03dbbf58 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/MethodNodeVisitor.java @@ -0,0 +1,11 @@ +package org.teavm.javascript.ast; + +/** + * + * @author Alexey Andreev + */ +public interface MethodNodeVisitor { + void visit(RegularMethodNode methodNode); + + void visit(NativeMethodNode methodNode); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NativeMethodNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NativeMethodNode.java new file mode 100644 index 000000000..98a77aca5 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NativeMethodNode.java @@ -0,0 +1,29 @@ +package org.teavm.javascript.ast; + +import org.teavm.javascript.ni.Generator; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class NativeMethodNode extends MethodNode { + private Generator generator; + + public NativeMethodNode(MethodReference reference) { + super(reference); + } + + public Generator getGenerator() { + return generator; + } + + public void setGenerator(Generator generator) { + this.generator = generator; + } + + @Override + public void acceptVisitor(MethodNodeVisitor visitor) { + visitor.visit(this); + } +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java new file mode 100644 index 000000000..b6f86eede --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/NodeModifier.java @@ -0,0 +1,9 @@ +package org.teavm.javascript.ast; + +/** + * + * @author Alexey Andreev + */ +public enum NodeModifier { + STATIC +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java new file mode 100644 index 000000000..54562aa19 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java @@ -0,0 +1,37 @@ +package org.teavm.javascript.ast; + +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class RegularMethodNode extends MethodNode { + private Statement body; + private int variableCount; + + public RegularMethodNode(MethodReference reference) { + super(reference); + } + + public Statement getBody() { + return body; + } + + public void setBody(Statement body) { + this.body = body; + } + + public int getVariableCount() { + return variableCount; + } + + public void setVariableCount(int variableCount) { + this.variableCount = variableCount; + } + + @Override + public void acceptVisitor(MethodNodeVisitor visitor) { + visitor.visit(this); + } +} 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 21e3d47c2..ab215c949 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,13 +1,9 @@ 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/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java index 6c22af75f..ddfff8d62 100644 --- a/teavm-core/src/main/java/org/teavm/model/MethodReference.java +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -21,6 +21,10 @@ public class MethodReference { return descriptor; } + public int parameterCount() { + return descriptor.getParameterTypes().length; + } + @Override public int hashCode() { return className.hashCode() ^ descriptor.hashCode();