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";
|
String classClass = "java.lang.Class";
|
||||||
writer.append("var cls = ").append(thisArg).append(".$class.classObject;").newLine();
|
writer.append("var cls = ").append(thisArg).append(".$class.classObject;").newLine();
|
||||||
writer.append("if (cls === undefined) {").newLine().indent();
|
writer.append("if (cls === undefined) {").newLine().indent();
|
||||||
writer.append("cls = ").appendClass(classClass)
|
MethodReference createMethodRef = new MethodReference(classClass, new MethodDescriptor("createNew",
|
||||||
.appendMethod(classClass, new MethodDescriptor("createNew", ValueType.object(classClass)))
|
ValueType.object(classClass)));
|
||||||
.append("();").newLine();
|
writer.append("cls = ").appendClass(classClass).appendMethod(createMethodRef).append("();").newLine();
|
||||||
writer.append("cls.$data = ").append(thisArg).append(".$class;").newLine().outdent().append("}").newLine();
|
writer.append("cls.$data = ").append(thisArg).append(".$class;").newLine().outdent().append("}").newLine();
|
||||||
writer.append("return cls;").newLine();
|
writer.append("return cls;").newLine();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import org.teavm.codegen.DefaultAliasProvider;
|
||||||
import org.teavm.codegen.DefaultNamingStrategy;
|
import org.teavm.codegen.DefaultNamingStrategy;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.codegen.SourceWriter;
|
||||||
import org.teavm.javascript.Decompiler;
|
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.ClassHolder;
|
||||||
import org.teavm.model.resource.ClasspathClassHolderSource;
|
import org.teavm.model.resource.ClasspathClassHolderSource;
|
||||||
|
|
||||||
|
@ -14,12 +16,14 @@ import org.teavm.model.resource.ClasspathClassHolderSource;
|
||||||
public class ClasslibTestGenerator {
|
public class ClasslibTestGenerator {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ClasspathClassHolderSource source = new ClasspathClassHolderSource();
|
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();
|
DefaultAliasProvider aliasProvider = new DefaultAliasProvider();
|
||||||
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source);
|
DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source);
|
||||||
SourceWriter writer = new SourceWriter(naming);
|
SourceWriter writer = new SourceWriter(naming);
|
||||||
Decompiler decompiler = new Decompiler(source, naming, writer);
|
Renderer renderer = new Renderer(writer, source);
|
||||||
ClassHolder cls = source.getClassHolder("java.lang.Object");
|
renderer.render(clsNode);
|
||||||
decompiler.decompile(cls);
|
|
||||||
System.out.println(writer);
|
System.out.println(writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,16 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.codegen;
|
package org.teavm.codegen;
|
||||||
|
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public interface AliasProvider {
|
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);
|
String getAlias(String className);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.codegen;
|
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
|
@Override
|
||||||
public String getAlias(String cls, MethodDescriptor method) {
|
public String getAlias(MethodReference method) {
|
||||||
String alias = method.getName();
|
String alias = method.getDescriptor().getName();
|
||||||
if (alias.equals("<init>")) {
|
if (alias.equals("<init>")) {
|
||||||
alias = "$init";
|
alias = "$init";
|
||||||
} else if (alias.equals("<clinit>")) {
|
} else if (alias.equals("<clinit>")) {
|
||||||
|
@ -59,7 +60,7 @@ public class DefaultAliasProvider implements AliasProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAlias(String cls, String field) {
|
public String getAlias(FieldReference field) {
|
||||||
return field + (lastSuffix++);
|
return field.getFieldName() + (lastSuffix++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,19 +47,19 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameFor(String cls, MethodDescriptor method) {
|
public String getNameFor(MethodReference method) {
|
||||||
if (method.getName().equals("<clinit>")) {
|
if (method.getDescriptor().getName().equals("<clinit>")) {
|
||||||
return "$clinit";
|
return "$clinit";
|
||||||
}
|
}
|
||||||
ClassHolder clsHolder = classSource.getClassHolder(cls);
|
ClassHolder clsHolder = classSource.getClassHolder(method.getClassName());
|
||||||
MethodHolder methodHolder = clsHolder.getMethod(method);
|
MethodHolder methodHolder = clsHolder.getMethod(method.getDescriptor());
|
||||||
if (methodHolder.getModifiers().contains(ElementModifier.STATIC) ||
|
if (methodHolder.getModifiers().contains(ElementModifier.STATIC) ||
|
||||||
method.getName().equals("<init>") ||
|
method.getDescriptor().getName().equals("<init>") ||
|
||||||
methodHolder.getLevel() == AccessLevel.PRIVATE) {
|
methodHolder.getLevel() == AccessLevel.PRIVATE) {
|
||||||
String key = cls + "#" + method.toString();
|
String key = method.getClassName() + "#" + method.toString();
|
||||||
String alias = privateAliases.get(key);
|
String alias = privateAliases.get(key);
|
||||||
if (alias == null) {
|
if (alias == null) {
|
||||||
alias = aliasProvider.getAlias(cls, method);
|
alias = aliasProvider.getAlias(method);
|
||||||
privateAliases.put(key, alias);
|
privateAliases.put(key, alias);
|
||||||
}
|
}
|
||||||
return alias;
|
return alias;
|
||||||
|
@ -67,7 +67,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
String key = method.toString();
|
String key = method.toString();
|
||||||
String alias = aliases.get(key);
|
String alias = aliases.get(key);
|
||||||
if (alias == null) {
|
if (alias == null) {
|
||||||
alias = aliasProvider.getAlias(cls, method);
|
alias = aliasProvider.getAlias(method);
|
||||||
aliases.put(key, alias);
|
aliases.put(key, alias);
|
||||||
}
|
}
|
||||||
return alias;
|
return alias;
|
||||||
|
@ -75,11 +75,11 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNameFor(String cls, String field) {
|
public String getNameFor(FieldReference field) {
|
||||||
String realCls = getRealFieldOwner(cls, field);
|
String realCls = getRealFieldOwner(field.getClassName(), field.getFieldName());
|
||||||
if (!realCls.equals(cls)) {
|
if (!realCls.equals(field.getClassName())) {
|
||||||
String alias = getNameFor(realCls, field);
|
String alias = getNameFor(new FieldReference(realCls, field.getFieldName()));
|
||||||
fieldAliases.put(cls + "#" + field, alias);
|
fieldAliases.put(field.getClassName() + "#" + field, alias);
|
||||||
return alias;
|
return alias;
|
||||||
} else {
|
} else {
|
||||||
String key = realCls + "#" + field;
|
String key = realCls + "#" + field;
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.codegen;
|
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 {
|
public interface NamingStrategy {
|
||||||
String getNameFor(String cls);
|
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;
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceWriter appendField(String cls, String field) {
|
public SourceWriter appendField(FieldReference field) {
|
||||||
appendIndent();
|
appendIndent();
|
||||||
sb.append(naming.getNameFor(cls, field));
|
sb.append(naming.getNameFor(field));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceWriter appendMethod(String cls, MethodDescriptor method) {
|
public SourceWriter appendMethod(MethodReference method) {
|
||||||
appendIndent();
|
appendIndent();
|
||||||
sb.append(naming.getNameFor(cls, method));
|
sb.append(naming.getNameFor(method));
|
||||||
return this;
|
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;
|
package org.teavm.javascript;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.*;
|
||||||
import org.teavm.codegen.NamingStrategy;
|
import org.teavm.common.*;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.javascript.ast.*;
|
||||||
import org.teavm.javascript.ast.RenderableMethod;
|
|
||||||
import org.teavm.javascript.ni.GeneratedBy;
|
import org.teavm.javascript.ni.GeneratedBy;
|
||||||
import org.teavm.javascript.ni.Generator;
|
import org.teavm.javascript.ni.Generator;
|
||||||
import org.teavm.javascript.ni.GeneratorContext;
|
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class Decompiler {
|
public class Decompiler {
|
||||||
private SourceWriter writer;
|
private ClassHolderSource classSource;
|
||||||
private MethodDecompiler methodDecompiler;
|
private Graph graph;
|
||||||
private NamingStrategy naming;
|
private LoopGraph loopGraph;
|
||||||
private Renderer renderer;
|
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) {
|
public Decompiler(ClassHolderSource classSource) {
|
||||||
this.methodDecompiler = new MethodDecompiler(classSource);
|
this.classSource = classSource;
|
||||||
this.renderer = new Renderer(writer, classSource);
|
|
||||||
this.writer = writer;
|
|
||||||
this.naming = naming;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decompile(ClassHolder cls) {
|
public int getGraphSize() {
|
||||||
writer.appendClass(cls.getName()).append(" = function() {\n").indent().newLine();
|
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.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()) {
|
class Block {
|
||||||
if (!field.getModifiers().contains(ElementModifier.STATIC)) {
|
public final IdentifiedStatement statement;
|
||||||
continue;
|
public final List<Statement> body;
|
||||||
}
|
public final int end;
|
||||||
Object value = field.getInitialValue();
|
public final int start;
|
||||||
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 ")
|
public Block(IdentifiedStatement statement, List<Statement> body, int start, int end) {
|
||||||
.append(cls.getParent() != null ? naming.getNameFor(cls.getParent()) :
|
this.statement = statement;
|
||||||
"Object").append("();").newLine();
|
this.body = body;
|
||||||
writer.appendClass(cls.getName()).append(".$meta = { ");
|
this.start = start;
|
||||||
writer.append("supertypes : [");
|
this.end = end;
|
||||||
boolean first = true;
|
|
||||||
if (cls.getParent() != null) {
|
|
||||||
writer.appendClass(cls.getParent());
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
for (String iface : cls.getInterfaces()) {
|
}
|
||||||
if (!first) {
|
|
||||||
writer.append(", ");
|
public ClassNode decompile(ClassHolder cls) {
|
||||||
}
|
ClassNode clsNode = new ClassNode(cls.getName(), cls.getParent());
|
||||||
first = false;
|
for (FieldHolder field : cls.getFields()) {
|
||||||
writer.appendClass(iface);
|
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()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
Set<ElementModifier> modifiers = method.getModifiers();
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
if (modifiers.contains(ElementModifier.ABSTRACT)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (modifiers.contains(ElementModifier.NATIVE)) {
|
clsNode.getMethods().add(decompile(method));
|
||||||
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.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;
|
Generator generator;
|
||||||
try {
|
try {
|
||||||
Class<?> generatorClass = Class.forName(generatorClassName);
|
Class<?> generatorClass = Class.forName(generatorClassName);
|
||||||
|
@ -106,44 +101,183 @@ public class Decompiler {
|
||||||
throw new DecompilationException("Error instantiating generator " + generatorClassName +
|
throw new DecompilationException("Error instantiating generator " + generatorClassName +
|
||||||
" for native method " + method.getOwner().getName() + "." + method.getDescriptor());
|
" for native method " + method.getOwner().getName() + "." + method.getDescriptor());
|
||||||
}
|
}
|
||||||
MethodReference ref = new MethodReference(method.getOwner().getName(), method.getDescriptor());
|
NativeMethodNode methodNode = new NativeMethodNode(new MethodReference(method.getOwner().getName(),
|
||||||
generator.generate(new Context(), writer, ref);
|
method.getDescriptor()));
|
||||||
|
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
|
||||||
|
methodNode.setGenerator(generator);
|
||||||
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object getDefaultValue(ValueType type) {
|
public RegularMethodNode decompileRegular(MethodHolder method) {
|
||||||
if (type instanceof ValueType.Primitive) {
|
lastBlockId = 1;
|
||||||
ValueType.Primitive primitive = (ValueType.Primitive)type;
|
indexer = new GraphIndexer(ProgramUtils.buildControlFlowGraph(method.getProgram()));
|
||||||
switch (primitive.getKind()) {
|
graph = indexer.getGraph();
|
||||||
case BOOLEAN:
|
loopGraph = new LoopGraph(this.graph);
|
||||||
return false;
|
unflatCode();
|
||||||
case BYTE:
|
Program program = method.getProgram();
|
||||||
return (byte)0;
|
blockMap = new Block[program.basicBlockCount() * 2 + 1];
|
||||||
case SHORT:
|
Deque<Block> stack = new ArrayDeque<>();
|
||||||
return (short)0;
|
BlockStatement rootStmt = new BlockStatement();
|
||||||
case INTEGER:
|
rootStmt.setId("root");
|
||||||
return 0;
|
stack.push(new Block(rootStmt, rootStmt.getBody(), -1, -1));
|
||||||
case CHARACTER:
|
StatementGenerator generator = new StatementGenerator();
|
||||||
return '\0';
|
generator.classSource = classSource;
|
||||||
case LONG:
|
generator.program = program;
|
||||||
return 0L;
|
generator.blockMap = blockMap;
|
||||||
case FLOAT:
|
generator.indexer = indexer;
|
||||||
return 0F;
|
generator.outgoings = getPhiOutgoings(program);
|
||||||
case DOUBLE:
|
parentNode = codeTree.getRoot();
|
||||||
return 0.0;
|
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 {
|
private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {
|
||||||
@Override
|
Set<NodeModifier> result = EnumSet.noneOf(NodeModifier.class);
|
||||||
public String getParameterName(int index) {
|
if (modifiers.contains(ElementModifier.STATIC)) {
|
||||||
return renderer.variableName(index);
|
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
|
// For each node find head of loop this node belongs to.
|
||||||
public NamingStrategy getNaming() {
|
//
|
||||||
return naming;
|
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;
|
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.javascript.ast.Statement;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
|
|
||||||
|
@ -33,13 +33,13 @@ public class Optimizer {
|
||||||
return optimizer.resultStmt;
|
return optimizer.resultStmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void optimize(RenderableMethod method) {
|
public void optimize(RegularMethodNode method) {
|
||||||
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariableCount());
|
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariableCount());
|
||||||
method.getBody().acceptVisitor(stats);
|
method.getBody().acceptVisitor(stats);
|
||||||
OptimizingVisitor optimizer = new OptimizingVisitor(stats);
|
OptimizingVisitor optimizer = new OptimizingVisitor(stats);
|
||||||
method.getBody().acceptVisitor(optimizer);
|
method.getBody().acceptVisitor(optimizer);
|
||||||
method.setBody(optimizer.resultStmt);
|
method.setBody(optimizer.resultStmt);
|
||||||
int paramCount = method.getMetadata().parameterCount();
|
int paramCount = method.getReference().parameterCount();
|
||||||
UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariableCount());
|
UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariableCount());
|
||||||
method.getBody().acceptVisitor(unusedEliminator);
|
method.getBody().acceptVisitor(unusedEliminator);
|
||||||
method.setVariableCount(unusedEliminator.lastIndex);
|
method.setVariableCount(unusedEliminator.lastIndex);
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.javascript;
|
||||||
import org.teavm.codegen.NamingStrategy;
|
import org.teavm.codegen.NamingStrategy;
|
||||||
import org.teavm.codegen.SourceWriter;
|
import org.teavm.codegen.SourceWriter;
|
||||||
import org.teavm.javascript.ast.*;
|
import org.teavm.javascript.ast.*;
|
||||||
|
import org.teavm.javascript.ni.GeneratorContext;
|
||||||
import org.teavm.model.*;
|
import org.teavm.model.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,21 +45,97 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
return naming;
|
return naming;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderInitializer(RenderableMethod method) {
|
public void render(ClassNode cls) {
|
||||||
MethodHolder metadata = method.getMetadata();
|
writer.appendClass(cls.getName()).append(" = function() {").indent().newLine();
|
||||||
writer.appendClass(metadata.getOwner().getName()).append(".")
|
for (FieldNode field : cls.getFields()) {
|
||||||
.appendMethod(metadata.getOwner().getName(), metadata.getDescriptor())
|
if (field.getModifiers().contains(NodeModifier.STATIC)) {
|
||||||
.append(" = function(");
|
continue;
|
||||||
for (int i = 1; i <= metadata.parameterCount(); ++i) {
|
}
|
||||||
|
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) {
|
if (i > 1) {
|
||||||
writer.append(", ");
|
writer.append(", ");
|
||||||
}
|
}
|
||||||
writer.append(variableName(i));
|
writer.append(variableName(i));
|
||||||
}
|
}
|
||||||
writer.append(") {").newLine().indent();
|
writer.append(") {").newLine().indent();
|
||||||
writer.append("var result = new ").appendClass(metadata.getOwner().getName()).append("();").newLine();
|
writer.append("var result = new ").appendClass(ref.getClassName()).append("();").newLine();
|
||||||
writer.append("result.").appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append("(");
|
writer.append("result.").appendMethod(ref).append("(");
|
||||||
for (int i = 1; i <= metadata.parameterCount(); ++i) {
|
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
writer.append(", ");
|
writer.append(", ");
|
||||||
}
|
}
|
||||||
|
@ -69,34 +146,33 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
writer.outdent().append("}").newLine();
|
writer.outdent().append("}").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(RenderableMethod method) {
|
public void render(MethodNode method) {
|
||||||
MethodHolder metadata = method.getMetadata();
|
MethodReference ref = method.getReference();
|
||||||
if (metadata.getName().equals("<init>")) {
|
if (ref.getDescriptor().getName().equals("<init>")) {
|
||||||
renderInitializer(method);
|
renderInitializer(method);
|
||||||
}
|
}
|
||||||
renderWorkingMethod(method);
|
renderWorkingMethod(method);
|
||||||
int startParam = 0;
|
int startParam = 0;
|
||||||
if (metadata.getModifiers().contains(ElementModifier.STATIC)) {
|
if (method.getModifiers().contains(ElementModifier.STATIC)) {
|
||||||
startParam = 1;
|
startParam = 1;
|
||||||
}
|
}
|
||||||
writer.appendClass(metadata.getOwner().getName()).append('.');
|
writer.appendClass(ref.getClassName()).append('.');
|
||||||
if (startParam == 0) {
|
if (startParam == 0) {
|
||||||
writer.append("prototype.");
|
writer.append("prototype.");
|
||||||
}
|
}
|
||||||
writer.appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append(" = function(");
|
writer.appendMethod(ref).append(" = function(");
|
||||||
for (int i = 1; i <= metadata.parameterCount(); ++i) {
|
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
writer.append(", ");
|
writer.append(", ");
|
||||||
}
|
}
|
||||||
writer.append(variableName(i));
|
writer.append(variableName(i));
|
||||||
}
|
}
|
||||||
writer.append(") {").newLine().indent();
|
writer.append(") {").newLine().indent();
|
||||||
writer.append("return ").appendClass(metadata.getOwner().getName()).append('_')
|
writer.append("return ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append("(");
|
||||||
.appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append("(");
|
|
||||||
if (startParam == 0) {
|
if (startParam == 0) {
|
||||||
writer.append("this");
|
writer.append("this");
|
||||||
}
|
}
|
||||||
for (int i = 1; i <= metadata.parameterCount(); ++i) {
|
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||||
if (i > 1 || startParam == 0) {
|
if (i > 1 || startParam == 0) {
|
||||||
writer.append(", ");
|
writer.append(", ");
|
||||||
}
|
}
|
||||||
|
@ -106,39 +182,56 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
writer.outdent().append("}").newLine();
|
writer.outdent().append("}").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderWorkingMethod(RenderableMethod method) {
|
private void renderWorkingMethod(MethodNode method) {
|
||||||
MethodHolder metadata = method.getMetadata();
|
MethodReference ref = method.getReference();
|
||||||
writer.append("function ").appendClass(metadata.getOwner().getName()).append('_')
|
writer.append("function ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append('(');
|
||||||
.appendMethod(metadata.getOwner().getName(), metadata.getDescriptor()).append('(');
|
|
||||||
int startParam = 0;
|
int startParam = 0;
|
||||||
if (metadata.getModifiers().contains(ElementModifier.STATIC)) {
|
if (method.getModifiers().contains(ElementModifier.STATIC)) {
|
||||||
startParam = 1;
|
startParam = 1;
|
||||||
}
|
}
|
||||||
for (int i = startParam; i <= metadata.parameterCount(); ++i) {
|
for (int i = startParam; i <= ref.parameterCount(); ++i) {
|
||||||
if (i > startParam) {
|
if (i > startParam) {
|
||||||
writer.append(", ");
|
writer.append(", ");
|
||||||
}
|
}
|
||||||
writer.append(variableName(i));
|
writer.append(variableName(i));
|
||||||
}
|
}
|
||||||
writer.append(") {").newLine().indent();
|
writer.append(") {").newLine().indent();
|
||||||
int variableCount = method.getVariableCount();
|
method.acceptVisitor(new MethodBodyRenderer());
|
||||||
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);
|
|
||||||
writer.outdent().append("}").newLine();
|
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
|
@Override
|
||||||
public void visit(AssignmentStatement statement) {
|
public void visit(AssignmentStatement statement) {
|
||||||
if (statement.getLeftValue() != null) {
|
if (statement.getLeftValue() != null) {
|
||||||
|
@ -597,7 +690,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(InvocationExpr expr) {
|
public void visit(InvocationExpr expr) {
|
||||||
String className = naming.getNameFor(expr.getClassName());
|
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()) {
|
switch (expr.getType()) {
|
||||||
case STATIC:
|
case STATIC:
|
||||||
writer.append(className).append("_").append(name).append("(");
|
writer.append(className).append("_").append(name).append("(");
|
||||||
|
@ -645,8 +738,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(QualificationExpr expr) {
|
public void visit(QualificationExpr expr) {
|
||||||
expr.getQualified().acceptVisitor(this);
|
expr.getQualified().acceptVisitor(this);
|
||||||
writer.append('.');
|
writer.append('.').appendField(new FieldReference(expr.getClassName(), expr.getField()));
|
||||||
writer.append(naming.getNameFor(expr.getClassName(), expr.getField()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class StatementGenerator implements InstructionVisitor {
|
||||||
GraphIndexer indexer;
|
GraphIndexer indexer;
|
||||||
BasicBlock nextBlock;
|
BasicBlock nextBlock;
|
||||||
BasicBlock currentBlock;
|
BasicBlock currentBlock;
|
||||||
MethodDecompiler.Block[] blockMap;
|
Decompiler.Block[] blockMap;
|
||||||
Program program;
|
Program program;
|
||||||
ClassHolderSource classSource;
|
ClassHolderSource classSource;
|
||||||
Incoming[][] outgoings;
|
Incoming[][] outgoings;
|
||||||
|
@ -532,7 +532,7 @@ public class StatementGenerator implements InstructionVisitor {
|
||||||
if (nextBlock == target) {
|
if (nextBlock == target) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
MethodDecompiler.Block block = blockMap[target.getIndex()];
|
Decompiler.Block block = blockMap[target.getIndex()];
|
||||||
if (target.getIndex() == indexer.nodeAt(block.end)) {
|
if (target.getIndex() == indexer.nodeAt(block.end)) {
|
||||||
BreakStatement breakStmt = new BreakStatement();
|
BreakStatement breakStmt = new BreakStatement();
|
||||||
breakStmt.setTarget(block.statement);
|
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;
|
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
|
* @author Alexey Andreev
|
||||||
*/
|
*/
|
||||||
public class RenderableMethod {
|
public abstract class MethodNode {
|
||||||
private Statement body;
|
private MethodReference reference;
|
||||||
private MethodHolder metadata;
|
private Set<NodeModifier> modifiers = EnumSet.noneOf(NodeModifier.class);
|
||||||
private int variableCount;
|
|
||||||
|
|
||||||
public RenderableMethod(MethodHolder metadata) {
|
public MethodNode(MethodReference reference) {
|
||||||
this.metadata = metadata;
|
this.reference = reference;
|
||||||
|
this.modifiers = EnumSet.copyOf(modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Statement getBody() {
|
public MethodReference getReference() {
|
||||||
return body;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBody(Statement body) {
|
public Set<NodeModifier> getModifiers() {
|
||||||
this.body = body;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodHolder getMetadata() {
|
public abstract void acceptVisitor(MethodNodeVisitor visitor);
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVariableCount() {
|
|
||||||
return variableCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVariableCount(int variableCount) {
|
|
||||||
this.variableCount = variableCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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;
|
package org.teavm.javascript.ni;
|
||||||
|
|
||||||
import org.teavm.codegen.NamingStrategy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
*/
|
*/
|
||||||
public interface GeneratorContext {
|
public interface GeneratorContext {
|
||||||
String getParameterName(int index);
|
String getParameterName(int index);
|
||||||
|
|
||||||
NamingStrategy getNaming();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ public class MethodReference {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int parameterCount() {
|
||||||
|
return descriptor.getParameterTypes().length;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return className.hashCode() ^ descriptor.hashCode();
|
return className.hashCode() ^ descriptor.hashCode();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user