diff --git a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java index c16e863fa..92780ffea 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java +++ b/teavm-core/src/main/java/org/teavm/javascript/JavascriptBuilder.java @@ -19,11 +19,13 @@ import java.io.*; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.teavm.codegen.*; import org.teavm.dependency.DependencyChecker; import org.teavm.javascript.ast.ClassNode; import org.teavm.model.*; import org.teavm.model.resource.ClasspathClassHolderSource; +import org.teavm.model.util.ListingBuilder; import org.teavm.optimization.ClassSetOptimizer; /** @@ -35,6 +37,8 @@ public class JavascriptBuilder { private DependencyChecker dependencyChecker; private ClassLoader classLoader; private boolean minifying = true; + private boolean bytecodeLogging; + private OutputStream logStream = System.out; private Map entryPoints = new HashMap<>(); private Map exportedClasses = new HashMap<>(); @@ -60,6 +64,14 @@ public class JavascriptBuilder { this.minifying = minifying; } + public boolean isBytecodeLogging() { + return bytecodeLogging; + } + + public void setBytecodeLogging(boolean bytecodeLogging) { + this.bytecodeLogging = bytecodeLogging; + } + public JavascriptEntryPoint entryPoint(String name, MethodReference ref) { if (entryPoints.containsKey(name)) { throw new IllegalArgumentException("Entry point with public name `" + name + "' already defined " + @@ -97,6 +109,13 @@ public class JavascriptBuilder { ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID))); dependencyChecker.checkDependencies(); ListableClassHolderSource classSet = dependencyChecker.cutUnachievableClasses(); + if (bytecodeLogging) { + try { + logBytecode(new PrintWriter(new OutputStreamWriter(logStream, "UTF-8")), classSet); + } catch (IOException e) { + // Just don't do anything + } + } Decompiler decompiler = new Decompiler(classSet, classLoader); ClassSetOptimizer optimizer = new ClassSetOptimizer(); optimizer.optimizeAll(classSet); @@ -119,6 +138,102 @@ public class JavascriptBuilder { } } + private void logBytecode(PrintWriter writer, ListableClassHolderSource classes) { + for (String className : classes.getClassNames()) { + ClassHolder classHolder = classes.getClassHolder(className); + printModifiers(writer, classHolder); + writer.println("class " + className); + for (MethodHolder method : classHolder.getMethods()) { + logMethodBytecode(writer, method); + } + } + } + + private void logMethodBytecode(PrintWriter writer, MethodHolder method) { + writer.print(" "); + printModifiers(writer, method); + writer.print(method.getName() + "("); + ValueType[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; ++i) { + if (i > 0) { + writer.print(", "); + } + printType(writer, parameterTypes[i]); + } + writer.println(")"); + if (method.getProgram() != null) { + ListingBuilder builder = new ListingBuilder(); + writer.println(builder.buildListing(method.getProgram(), " ")); + } else { + writer.println(); + } + } + + private void printType(PrintWriter writer, ValueType type) { + if (type instanceof ValueType.Object) { + writer.print(((ValueType.Object)type).getClassName()); + } else if (type instanceof ValueType.Array) { + printType(writer, ((ValueType.Array)type).getItemType()); + writer.print("[]"); + } else if (type instanceof ValueType.Primitive) { + switch (((ValueType.Primitive)type).getKind()) { + case BOOLEAN: + writer.print("boolean"); + break; + case SHORT: + writer.print("short"); + break; + case BYTE: + writer.print("byte"); + break; + case CHARACTER: + writer.print("character"); + break; + case DOUBLE: + writer.print("double"); + break; + case FLOAT: + writer.print("float"); + break; + case INTEGER: + writer.print("int"); + break; + case LONG: + writer.print("long"); + break; + } + } + } + + private void printModifiers(PrintWriter writer, ElementHolder element) { + switch (element.getLevel()) { + case PRIVATE: + writer.print("private "); + break; + case PUBLIC: + writer.print("public "); + break; + case PROTECTED: + writer.print("protected "); + break; + default: + break; + } + Set modifiers = element.getModifiers(); + if (modifiers.contains(ElementModifier.ABSTRACT)) { + writer.print("abstract "); + } + if (modifiers.contains(ElementModifier.FINAL)) { + writer.print("final "); + } + if (modifiers.contains(ElementModifier.STATIC)) { + writer.print("static "); + } + if (modifiers.contains(ElementModifier.NATIVE)) { + writer.print("native "); + } + } + public void build(File file) throws RenderingException { try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) { build(writer); diff --git a/teavm-core/src/main/java/org/teavm/model/Program.java b/teavm-core/src/main/java/org/teavm/model/Program.java index 470012c4e..cf7715463 100644 --- a/teavm-core/src/main/java/org/teavm/model/Program.java +++ b/teavm-core/src/main/java/org/teavm/model/Program.java @@ -37,6 +37,7 @@ public class Program { public Variable createVariable() { Variable variable = new Variable(this); variable.setIndex(variables.size()); + variable.setRepresentative(variable.getIndex()); variables.add(variable); return variable; } diff --git a/teavm-core/src/main/java/org/teavm/model/Variable.java b/teavm-core/src/main/java/org/teavm/model/Variable.java index d363aed42..9c97b42a6 100644 --- a/teavm-core/src/main/java/org/teavm/model/Variable.java +++ b/teavm-core/src/main/java/org/teavm/model/Variable.java @@ -22,6 +22,7 @@ package org.teavm.model; public class Variable { private Program program; private int index; + private int representative; Variable(Program program) { this.program = program; @@ -42,4 +43,12 @@ public class Variable { void setProgram(Program program) { this.program = program; } + + public int getRepresentative() { + return representative; + } + + public void setRepresentative(int representative) { + this.representative = representative; + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java index ae7285fe7..3e480948b 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java @@ -30,6 +30,10 @@ public class InstructionStringifier implements InstructionVisitor { this.sb = sb; } + private StringBuilder appendVar(Variable var) { + return sb.append('@').append(var.getIndex()).append(":").append(var.getRepresentative()); + } + @Override public void visit(EmptyInstruction insn) { sb.append("nop"); @@ -37,49 +41,43 @@ public class InstructionStringifier implements InstructionVisitor { @Override public void visit(ClassConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := classOf ") - .append(insn.getConstant()); + appendVar(insn.getReceiver()).append(" := classOf ").append(insn.getConstant()); } @Override public void visit(NullConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := null"); + appendVar(insn.getReceiver()).append(" := null"); } @Override public void visit(IntegerConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := ") - .append(insn.getConstant()); + appendVar(insn.getReceiver()).append(" := ").append(insn.getConstant()); } @Override public void visit(LongConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := ") - .append(insn.getConstant()); + appendVar(insn.getReceiver()).append(" := ").append(insn.getConstant()); } @Override public void visit(FloatConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := ") - .append(insn.getConstant()); + appendVar(insn.getReceiver()).append(" := ").append(insn.getConstant()); } @Override public void visit(DoubleConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := ") - .append(insn.getConstant()); + appendVar(insn.getReceiver()).append(" := ").append(insn.getConstant()); } @Override public void visit(StringConstantInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := '") - .append(insn.getConstant()).append("'"); + appendVar(insn.getReceiver()).append(" := '").append(insn.getConstant()).append("'"); } @Override public void visit(BinaryInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := @") - .append(insn.getFirstOperand().getIndex()).append(" "); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getFirstOperand()).append(" "); switch (insn.getOperation()) { case ADD: sb.append("+"); @@ -118,24 +116,26 @@ public class InstructionStringifier implements InstructionVisitor { sb.append("^"); break; } - sb.append(" @").append(insn.getSecondOperand().getIndex()); + sb.append(' '); + appendVar(insn.getSecondOperand()); } @Override public void visit(NegateInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := -") - .append(" @").append(insn.getOperand().getIndex()); + appendVar(insn.getReceiver()).append(" := -"); + appendVar(insn.getOperand()); } @Override public void visit(AssignInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := @") - .append(insn.getAssignee().getIndex()); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getAssignee()); } @Override public void visit(BranchingInstruction insn) { - sb.append("if @").append(insn.getOperand().getIndex()).append(" "); + sb.append("if "); + appendVar(insn.getOperand()).append(" "); switch (insn.getCondition()) { case EQUAL: sb.append("== 0"); @@ -168,7 +168,8 @@ public class InstructionStringifier implements InstructionVisitor { @Override public void visit(BinaryBranchingInstruction insn) { - sb.append("if @").append(insn.getFirstOperand().getIndex()).append(" "); + sb.append("if "); + appendVar(insn.getFirstOperand()).append(" "); switch (insn.getCondition()) { case EQUAL: case REFERENCE_EQUAL: @@ -179,8 +180,7 @@ public class InstructionStringifier implements InstructionVisitor { sb.append("!="); break; } - sb.append("@").append(insn.getSecondOperand().getIndex()) - .append(" then goto $").append(insn.getConsequent().getIndex()) + appendVar(insn.getSecondOperand()).append(" then goto $").append(insn.getConsequent().getIndex()) .append(" else goto $").append(insn.getAlternative().getIndex()); } @@ -191,15 +191,15 @@ public class InstructionStringifier implements InstructionVisitor { @Override public void visit(SwitchInstruction insn) { - sb.append("switch @").append(insn.getCondition().getIndex()).append(" "); + sb.append("switch "); + appendVar(insn.getCondition()).append(" "); List entries = insn.getEntries(); for (int i = 0; i < entries.size(); ++i) { if (i > 0) { sb.append("; "); } SwitchTableEntry entry = entries.get(i); - sb.append("case ").append(entry.getCondition()).append(": goto $") - .append(entry.getTarget()); + sb.append("case ").append(entry.getCondition()).append(": goto $").append(entry.getTarget()); } sb.append(", default: goto $").append(insn.getDefaultTarget()); } @@ -208,48 +208,46 @@ public class InstructionStringifier implements InstructionVisitor { public void visit(ExitInstruction insn) { sb.append("return"); if (insn.getValueToReturn() != null) { - sb.append(" @").append(insn.getValueToReturn().getIndex()); + sb.append(" "); + appendVar(insn.getValueToReturn()); } } @Override public void visit(RaiseInstruction insn) { - sb.append("throw @").append(insn.getException().getIndex()); + sb.append("throw "); + appendVar(insn.getException()); } @Override public void visit(ConstructArrayInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" = new ") - .append(insn.getItemType()).append("[@").append(insn.getSize().getIndex()) - .append(']'); + appendVar(insn.getReceiver()).append(" = new ").append(insn.getItemType()).append("["); + appendVar(insn.getSize()).append(']'); } @Override public void visit(ConstructInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" = new ") - .append(insn.getType()).append("()"); + appendVar(insn.getReceiver()).append(" = new ").append(insn.getType()).append("()"); } @Override public void visit(ConstructMultiArrayInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" = new ") - .append(insn.getItemType()).append("["); + appendVar(insn.getReceiver()).append(" = new ").append(insn.getItemType()).append("["); List dimensions = insn.getDimensions(); for (int i = 0; i < dimensions.size(); ++i) { if (i > 0) { sb.append(", "); } - Variable dimension = dimensions.get(i); - sb.append("@").append(dimension.getIndex()); + appendVar(dimensions.get(i)); } sb.append("]"); } @Override public void visit(GetFieldInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := "); + appendVar(insn.getReceiver()).append(" := "); if (insn.getInstance() != null) { - sb.append("@").append(insn.getInstance().getIndex()); + appendVar(insn.getInstance()); } else { sb.append(insn.getField().getClassName()); } @@ -259,34 +257,35 @@ public class InstructionStringifier implements InstructionVisitor { @Override public void visit(PutFieldInstruction insn) { if (insn.getInstance() != null) { - sb.append("@").append(insn.getInstance().getIndex()); + appendVar(insn.getInstance()); } else { sb.append(insn.getField().getClassName()); } - sb.append(".").append(insn.getField()).append(" := @").append(insn.getValue().getIndex()); + sb.append(".").append(insn.getField()).append(" := "); + appendVar(insn.getValue()); } @Override public void visit(GetElementInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := @") - .append(insn.getArray().getIndex()).append("[@") - .append(insn.getIndex().getIndex()).append("]"); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getArray()).append("["); + appendVar(insn.getIndex()).append("]"); } @Override public void visit(PutElementInstruction insn) { - sb.append("@").append(insn.getArray().getIndex()).append("[@") - .append(insn.getIndex().getIndex()).append("] := @") - .append(insn.getValue().getIndex()); + appendVar(insn.getArray()).append("["); + appendVar(insn.getIndex()).append("] := "); + appendVar(insn.getValue()); } @Override public void visit(InvokeInstruction insn) { if (insn.getReceiver() != null) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := "); + appendVar(insn.getReceiver()).append(" := "); } if (insn.getInstance() != null) { - sb.append("@").append(insn.getInstance().getIndex()); + appendVar(insn.getInstance()); } else { sb.append(insn.getMethod().getClassName()); } @@ -296,55 +295,52 @@ public class InstructionStringifier implements InstructionVisitor { if (i > 0) { sb.append(", "); } - sb.append("@").append(arguments.get(i).getIndex()); + appendVar(arguments.get(i)); } sb.append(")"); } @Override public void visit(IsInstanceInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := @") - .append(insn.getValue().getIndex()).append(" instanceof ").append(insn.getType()); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getValue()).append(" instanceof ").append(insn.getType()); } @Override public void visit(CastInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := cast @") - .append(insn.getValue().getIndex()).append(" to ") - .append(insn.getTargetType()); + appendVar(insn.getReceiver()).append(" := cast "); + appendVar(insn.getValue()).append(" to ").append(insn.getTargetType()); } @Override public void visit(CastNumberInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := cast @") - .append(insn.getValue().getIndex()) - .append(" from ").append(insn.getSourceType()) + appendVar(insn.getReceiver()).append(" := cast "); + appendVar(insn.getValue()).append(" from ").append(insn.getSourceType()) .append(" to ").append(insn.getTargetType()); } @Override public void visit(CastIntegerInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := cast @") - .append(insn.getValue().getIndex()) - .append(" from INT to ").append(insn.getTargetType()); + appendVar(insn.getReceiver()).append(" := cast "); + appendVar(insn.getValue()).append(" from INT to ").append(insn.getTargetType()); } @Override public void visit(UnwrapArrayInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append("@").append(" := @") - .append(insn.getArray()).append(".data"); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getArray()).append(".data"); } @Override public void visit(ArrayLengthInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append(" := @") - .append(insn.getArray().getIndex()).append(".length"); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getArray()).append(".length"); } @Override public void visit(CloneArrayInstruction insn) { - sb.append("@").append(insn.getReceiver().getIndex()).append("@") - .append(insn.getArray().getIndex()).append(".clone()"); + appendVar(insn.getReceiver()).append(" := "); + appendVar(insn.getArray()).append(".clone()"); } @Override diff --git a/teavm-core/src/main/java/org/teavm/model/util/ListingBuilder.java b/teavm-core/src/main/java/org/teavm/model/util/ListingBuilder.java index c11872c09..233920900 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ListingBuilder.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ListingBuilder.java @@ -31,14 +31,16 @@ public class ListingBuilder { sb.append(prefix).append("$").append(i).append(":\n"); for (Phi phi : block.getPhis()) { sb.append(prefix).append(" "); - sb.append("@").append(phi.getReceiver().getIndex()).append(" := "); + sb.append("@").append(phi.getReceiver().getIndex()).append(":") + .append(phi.getReceiver().getRepresentative()).append(" := "); List incomings = phi.getIncomings(); for (int j = 0; j < incomings.size(); ++j) { if (j > 0) { sb.append(", "); } Incoming incoming = incomings.get(j); - sb.append("@").append(incoming.getValue().getIndex()).append(" from ") + sb.append("@").append(incoming.getValue().getIndex()).append(":") + .append(incoming.getValue().getRepresentative()).append(" from ") .append("$").append(incoming.getSource().getIndex()); } sb.append("\n"); diff --git a/teavm-core/src/main/java/org/teavm/model/util/PhiEliminator.java b/teavm-core/src/main/java/org/teavm/model/util/PhiEliminator.java new file mode 100644 index 000000000..c578fec9f --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/util/PhiEliminator.java @@ -0,0 +1,24 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model.util; + +/** + * + * @author Alexey Andreev + */ +public class PhiEliminator { + +} diff --git a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java index 0a933bab5..ad9343b3e 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java +++ b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java @@ -77,6 +77,9 @@ public class SSATransformer { } } } + for (int i = 0; i <= arguments.length; ++i) { + program.variableAt(i).setRepresentative(i); + } arguments = null; } @@ -123,6 +126,7 @@ public class SSATransformer { for (Phi phi : currentBlock.getPhis()) { Variable var = program.createVariable(); variableMap[phi.getReceiver().getIndex()] = var; + var.setRepresentative(phi.getReceiver().getIndex()); phi.setReceiver(var); } for (Instruction insn : currentBlock.getInstructions()) { @@ -181,6 +185,7 @@ public class SSATransformer { private Variable define(Variable var) { Variable result = program.createVariable(); + result.setRepresentative(var.getIndex()); variableMap[var.getIndex()] = result; return result; } diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java index 1fd1cfcce..fc8867773 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/BuildJavascriptMojo.java @@ -68,6 +68,9 @@ public class BuildJavascriptMojo extends AbstractMojo { @Parameter private boolean mainPageIncluded; + @Parameter + private boolean bytecodeLogging; + public void setProject(MavenProject project) { this.project = project; } @@ -84,6 +87,10 @@ public class BuildJavascriptMojo extends AbstractMojo { this.minifying = minifying; } + public void setBytecodeLogging(boolean bytecodeLogging) { + this.bytecodeLogging = bytecodeLogging; + } + public void setRuntimeSuppressed(boolean runtimeSuppressed) { this.runtimeSuppressed = runtimeSuppressed; } @@ -100,6 +107,7 @@ public class BuildJavascriptMojo extends AbstractMojo { log.info("Building JavaScript file"); JavascriptBuilder builder = new JavascriptBuilder(classLoader); builder.setMinifying(minifying); + builder.setBytecodeLogging(bytecodeLogging); MethodDescriptor mainMethodDesc = new MethodDescriptor("main", ValueType.arrayOf( ValueType.object("java.lang.String")), ValueType.VOID); builder.entryPoint("main", new MethodReference(mainClass, mainMethodDesc)) diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml index 3c95c5504..f00911da4 100644 --- a/teavm-samples/pom.xml +++ b/teavm-samples/pom.xml @@ -47,6 +47,7 @@ false org.teavm.samples.HelloWorld true + true