mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Fixes native method decompilation. Refactoring
This commit is contained in:
parent
d63171e935
commit
5641a09a0a
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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("<init>")) {
|
||||
alias = "$init";
|
||||
} else if (alias.equals("<clinit>")) {
|
||||
|
@ -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++);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,19 +47,19 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getNameFor(String cls, MethodDescriptor method) {
|
||||
if (method.getName().equals("<clinit>")) {
|
||||
public String getNameFor(MethodReference method) {
|
||||
if (method.getDescriptor().getName().equals("<clinit>")) {
|
||||
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("<init>") ||
|
||||
method.getDescriptor().getName().equals("<init>") ||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <konsoletyper@gmail.com>
|
||||
* @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<Statement> 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<Statement> 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<ElementModifier> 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<Block> 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<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {
|
||||
Set<NodeModifier> result = EnumSet.noneOf(NodeModifier.class);
|
||||
if (modifiers.contains(ElementModifier.STATIC)) {
|
||||
result.add(NodeModifier.STATIC);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Incoming[][] getPhiOutgoings(Program program) {
|
||||
List<List<Incoming>> outgoings = new ArrayList<>();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
outgoings.add(new ArrayList<Incoming>());
|
||||
}
|
||||
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<Block> createBlocks(int start) {
|
||||
List<Block> 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<RangeTree.Range> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Statement> body;
|
||||
public final int end;
|
||||
public final int start;
|
||||
|
||||
public Block(IdentifiedStatement statement, List<Statement> 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<Block> 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<List<Incoming>> outgoings = new ArrayList<>();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
outgoings.add(new ArrayList<Incoming>());
|
||||
}
|
||||
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<Block> createBlocks(int start) {
|
||||
List<Block> 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<RangeTree.Range> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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("<init>")) {
|
||||
public void render(MethodNode method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getDescriptor().getName().equals("<init>")) {
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.teavm.javascript.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class ClassNode {
|
||||
private String name;
|
||||
private String parentName;
|
||||
private List<FieldNode> fields = new ArrayList<>();
|
||||
private List<MethodNode> methods = new ArrayList<>();
|
||||
private List<String> 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<FieldNode> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
public List<MethodNode> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public List<String> getInterfaces() {
|
||||
return interfaces;
|
||||
}
|
||||
}
|
|
@ -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 <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class FieldNode {
|
||||
private String name;
|
||||
private ValueType type;
|
||||
private Set<NodeModifier> 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<NodeModifier> getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public ValueType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Object getInitialValue() {
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
public void setInitialValue(Object initialValue) {
|
||||
this.initialValue = initialValue;
|
||||
}
|
||||
}
|
|
@ -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<NodeModifier> 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<NodeModifier> 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);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.teavm.javascript.ast;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface MethodNodeVisitor {
|
||||
void visit(RegularMethodNode methodNode);
|
||||
|
||||
void visit(NativeMethodNode methodNode);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.teavm.javascript.ast;
|
||||
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.teavm.javascript.ast;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public enum NodeModifier {
|
||||
STATIC
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.teavm.javascript.ast;
|
||||
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,9 @@
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user