Fixes native method decompilation. Refactoring

This commit is contained in:
konsoletyper 2013-11-04 19:29:07 +04:00
parent d63171e935
commit 5641a09a0a
21 changed files with 610 additions and 496 deletions

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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++);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
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 ClassNode decompile(ClassHolder cls) {
ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent());
for (FieldHolder field : cls.getFields()) {
if (field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
FieldNode fieldNode = new FieldNode(field.getName(), field.getType());
fieldNode.getModifiers().addAll(mapModifiers(field.getModifiers()));
fieldNode.setInitialValue(field.getInitialValue());
clsNode.getFields().add(fieldNode);
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.append("this.").appendField(cls.getName(), field.getName()).append(" = ")
.append(renderer.constantToString(value)).append(";").newLine();
}
writer.append("this.$class = ").appendClass(cls.getName()).append(";").newLine();
writer.outdent().append("}").newLine();
for (FieldHolder field : cls.getFields()) {
if (!field.getModifiers().contains(ElementModifier.STATIC)) {
continue;
}
Object value = field.getInitialValue();
if (value == null) {
value = getDefaultValue(field.getType());
}
writer.appendClass(cls.getName()).append('.')
.appendField(cls.getName(), field.getName()).append(" = ")
.append(renderer.constantToString(value)).append(";").newLine();
}
writer.appendClass(cls.getName()).append(".prototype = new ")
.append(cls.getParent() != null ? naming.getNameFor(cls.getParent()) :
"Object").append("();").newLine();
writer.appendClass(cls.getName()).append(".$meta = { ");
writer.append("supertypes : [");
boolean first = true;
if (cls.getParent() != null) {
writer.appendClass(cls.getParent());
first = false;
}
for (String iface : cls.getInterfaces()) {
if (!first) {
writer.append(", ");
}
first = false;
writer.appendClass(iface);
}
writer.append("]");
writer.append(" };").newLine();
for (MethodHolder method : cls.getMethods()) {
Set<ElementModifier> modifiers = method.getModifiers();
if (modifiers.contains(ElementModifier.ABSTRACT)) {
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
continue;
}
if (modifiers.contains(ElementModifier.NATIVE)) {
clsNode.getMethods().add(decompile(method));
}
clsNode.getInterfaces().addAll(cls.getInterfaces());
return clsNode;
}
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 " + cls.getName() + "." + method.getDescriptor() +
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();
generateNativeMethod(generatorClassName, method);
} else {
RenderableMethod renderableMethod = methodDecompiler.decompile(method);
Optimizer optimizer = new Optimizer();
optimizer.optimize(renderableMethod);
renderer.render(renderableMethod);
}
}
}
private void generateNativeMethod(String generatorClassName, MethodHolder method) {
Generator generator;
try {
Class<?> generatorClass = Class.forName(generatorClassName);
@ -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;
}
@Override
public NamingStrategy getNaming() {
return naming;
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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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,27 +182,39 @@ 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();
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 > metadata.parameterCount() + 1;
boolean hasVars = variableCount > ref.parameterCount() + 1;
if (hasVars) {
writer.append("var ");
boolean first = true;
for (int i = metadata.parameterCount() + 1; i < variableCount; ++i) {
for (int i = ref.parameterCount() + 1; i < variableCount; ++i) {
if (!first) {
writer.append(", ");
}
@ -135,10 +223,15 @@ public class Renderer implements ExprVisitor, StatementVisitor {
}
writer.append(";").newLine();
}
method.getBody().acceptVisitor(this);
writer.outdent().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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -0,0 +1,9 @@
package org.teavm.javascript.ast;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public enum NodeModifier {
STATIC
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();