mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Fixes casting between integer values. Fixes naming generation of
methods. Adds more informative exception trace when no method was found during rendering
This commit is contained in:
parent
59615f3165
commit
3f1a44eccb
|
@ -22,7 +22,7 @@ public class SystemTests {
|
|||
|
||||
@Test(expected = IndexOutOfBoundsException.class)
|
||||
public void failsToCopyArraysWithInvalidIndexes() {
|
||||
System.arraycopy(new Object[0], 0, new TObject[0], 0, 1);
|
||||
System.arraycopy(new Object[0], 0, new Object[0], 0, 1);
|
||||
}
|
||||
|
||||
@Test(expected = ArrayStoreException.class)
|
||||
|
@ -37,6 +37,6 @@ public class SystemTests {
|
|||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void failsToCopyToNullTarget() {
|
||||
System.arraycopy(new TObject[1], 0, null, 0, 1);
|
||||
System.arraycopy(new Object[1], 0, null, 0, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,11 +51,12 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
|||
if (method.getDescriptor().getName().equals("<clinit>")) {
|
||||
return "$clinit";
|
||||
}
|
||||
method = getRealMethod(method);
|
||||
if (method == null) {
|
||||
throw new NamingException("Can't provide name for method as it was not found: " + method);
|
||||
}
|
||||
ClassHolder clsHolder = classSource.getClassHolder(method.getClassName());
|
||||
MethodHolder methodHolder = clsHolder.getMethod(method.getDescriptor());
|
||||
if (methodHolder == null) {
|
||||
throw new RuntimeException("Method not found: " + method);
|
||||
}
|
||||
if (methodHolder.getModifiers().contains(ElementModifier.STATIC) ||
|
||||
method.getDescriptor().getName().equals("<init>") ||
|
||||
methodHolder.getLevel() == AccessLevel.PRIVATE) {
|
||||
|
@ -95,13 +96,32 @@ public class DefaultNamingStrategy implements NamingStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
private MethodReference getRealMethod(MethodReference methodRef) {
|
||||
String className = methodRef.getClassName();
|
||||
while (className != null) {
|
||||
ClassHolder cls = classSource.getClassHolder(className);
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
MethodHolder method = cls.getMethod(methodRef.getDescriptor());
|
||||
if (method != null) {
|
||||
if (method.getLevel() == AccessLevel.PRIVATE && !className.equals(methodRef.getClassName())) {
|
||||
return null;
|
||||
}
|
||||
return new MethodReference(className, method.getDescriptor());
|
||||
}
|
||||
className = cls.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getRealFieldOwner(String cls, String field) {
|
||||
String initialCls = cls;
|
||||
while (!fieldExists(cls, field)) {
|
||||
ClassHolder clsHolder = classSource.getClassHolder(cls);
|
||||
cls = clsHolder.getParent();
|
||||
if (cls == null) {
|
||||
throw new IllegalArgumentException("Field not found: " +
|
||||
throw new NamingException("Can't provide name for field as the field not found: " +
|
||||
initialCls + "." + field);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package org.teavm.codegen;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class NamingException extends RuntimeException {
|
||||
private static final long serialVersionUID = 3472322553091962348L;
|
||||
|
||||
public NamingException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public NamingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ import org.teavm.model.MethodReference;
|
|||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface NamingStrategy {
|
||||
String getNameFor(String cls);
|
||||
String getNameFor(String cls) throws NamingException;
|
||||
|
||||
String getNameFor(MethodReference method);
|
||||
String getNameFor(MethodReference method) throws NamingException;
|
||||
|
||||
String getNameFor(FieldReference field);
|
||||
String getNameFor(FieldReference field) throws NamingException;
|
||||
}
|
||||
|
|
|
@ -45,19 +45,19 @@ public class SourceWriter {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SourceWriter appendClass(String cls) {
|
||||
public SourceWriter appendClass(String cls) throws NamingException {
|
||||
appendIndent();
|
||||
sb.append(naming.getNameFor(cls));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SourceWriter appendField(FieldReference field) {
|
||||
public SourceWriter appendField(FieldReference field) throws NamingException {
|
||||
appendIndent();
|
||||
sb.append(naming.getNameFor(field));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SourceWriter appendMethod(MethodReference method) {
|
||||
public SourceWriter appendMethod(MethodReference method) throws NamingException {
|
||||
appendIndent();
|
||||
sb.append(naming.getNameFor(method));
|
||||
return this;
|
||||
|
|
|
@ -270,6 +270,10 @@ class DependencyGraphBuilder {
|
|||
valueNode.connect(receiverNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(AssignInstruction insn) {
|
||||
DependencyNode valueNode = nodes[insn.getAssignee().getIndex()];
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.javascript;
|
||||
|
||||
import org.teavm.codegen.NamingException;
|
||||
import org.teavm.codegen.NamingStrategy;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.javascript.ast.*;
|
||||
|
@ -45,10 +46,14 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
|||
return naming;
|
||||
}
|
||||
|
||||
public void renderRuntime() {
|
||||
renderRuntimeCls();
|
||||
renderRuntimeString();
|
||||
renderRuntimeObjcls();
|
||||
public void renderRuntime() throws RenderingException {
|
||||
try {
|
||||
renderRuntimeCls();
|
||||
renderRuntimeString();
|
||||
renderRuntimeObjcls();
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering runtime methods. See a cause for details", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderRuntimeCls() {
|
||||
|
@ -87,7 +92,7 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
|||
writer.append("var characters = $rt_createNumericArray($rt_charcls(), str.length);").newLine();
|
||||
writer.append("var charsBuffer = characters.data;").newLine();
|
||||
writer.append("for (var i = 0; i < str.length; i = (i + 1) | 0) {").indent().newLine();
|
||||
writer.append("charsBuffer[i] = str.charCodeAt(i);").newLine();
|
||||
writer.append("charsBuffer[i] = str.charCodeAt(i) & 0xFFFF;").newLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
writer.append("return ").appendClass("java.lang.String").append(".")
|
||||
.appendMethod(stringCons).append("(characters);").newLine();
|
||||
|
@ -98,58 +103,62 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
|||
writer.append("$rt_objcls = function() { return ").appendClass("java.lang.Object").append("; }").newLine();
|
||||
}
|
||||
|
||||
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;
|
||||
public void render(ClassNode cls) throws RenderingException {
|
||||
try {
|
||||
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();
|
||||
}
|
||||
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();
|
||||
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;
|
||||
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();
|
||||
}
|
||||
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("name : \"").append(cls.getName()).append("\", ");
|
||||
writer.append("primitive : false, ");
|
||||
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(", ");
|
||||
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("name : \"").append(cls.getName()).append("\", ");
|
||||
writer.append("primitive : false, ");
|
||||
writer.append("supertypes : [");
|
||||
boolean first = true;
|
||||
if (cls.getParentName() != null) {
|
||||
writer.appendClass(cls.getParentName());
|
||||
first = false;
|
||||
}
|
||||
first = false;
|
||||
writer.appendClass(iface);
|
||||
}
|
||||
writer.append("]");
|
||||
writer.append(" };").newLine();
|
||||
for (MethodNode method : cls.getMethods()) {
|
||||
render(method);
|
||||
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);
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,40 +210,45 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
|||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
public void render(MethodNode method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getDescriptor().getName().equals("<init>")) {
|
||||
renderInitializer(method);
|
||||
}
|
||||
renderWorkingMethod(method);
|
||||
int startParam = 0;
|
||||
if (method.getModifiers().contains(NodeModifier.STATIC)) {
|
||||
startParam = 1;
|
||||
}
|
||||
writer.appendClass(ref.getClassName()).append('.');
|
||||
if (startParam == 0) {
|
||||
writer.append("prototype.");
|
||||
}
|
||||
writer.appendMethod(ref).append(" = function(");
|
||||
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||
if (i > 1) {
|
||||
writer.append(", ");
|
||||
public void render(MethodNode method) throws RenderingException {
|
||||
try {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getDescriptor().getName().equals("<init>")) {
|
||||
renderInitializer(method);
|
||||
}
|
||||
writer.append(variableName(i));
|
||||
}
|
||||
writer.append(") {").newLine().indent();
|
||||
writer.append("return ").appendClass(ref.getClassName()).append('_').appendMethod(ref).append("(");
|
||||
if (startParam == 0) {
|
||||
writer.append("this");
|
||||
}
|
||||
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||
if (i > 1 || startParam == 0) {
|
||||
writer.append(", ");
|
||||
renderWorkingMethod(method);
|
||||
int startParam = 0;
|
||||
if (method.getModifiers().contains(NodeModifier.STATIC)) {
|
||||
startParam = 1;
|
||||
}
|
||||
writer.append(variableName(i));
|
||||
writer.appendClass(ref.getClassName()).append('.');
|
||||
if (startParam == 0) {
|
||||
writer.append("prototype.");
|
||||
}
|
||||
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(ref.getClassName()).append('_').appendMethod(ref).append("(");
|
||||
if (startParam == 0) {
|
||||
writer.append("this");
|
||||
}
|
||||
for (int i = 1; i <= ref.parameterCount(); ++i) {
|
||||
if (i > 1 || startParam == 0) {
|
||||
writer.append(", ");
|
||||
}
|
||||
writer.append(variableName(i));
|
||||
}
|
||||
writer.append(");").newLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
} catch (NamingException e) {
|
||||
throw new RenderingException("Error rendering method " + method.getReference() + ". " +
|
||||
"See cause for details", e);
|
||||
}
|
||||
writer.append(");").newLine();
|
||||
writer.outdent().append("}").newLine();
|
||||
}
|
||||
|
||||
private void renderWorkingMethod(MethodNode method) {
|
||||
|
@ -599,6 +613,16 @@ public class Renderer implements ExprVisitor, StatementVisitor {
|
|||
expr.getOperand().acceptVisitor(this);
|
||||
writer.append(')');
|
||||
break;
|
||||
case BYTE_TO_INT:
|
||||
writer.append("$rt_byteToInt(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.append(')');
|
||||
break;
|
||||
case SHORT_TO_INT:
|
||||
writer.append("$rt_shortToInt(");
|
||||
expr.getOperand().acceptVisitor(this);
|
||||
writer.append(')');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package org.teavm.javascript;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class RenderingException extends RuntimeException {
|
||||
private static final long serialVersionUID = 631804556597569547L;
|
||||
|
||||
public RenderingException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RenderingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public RenderingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RenderingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
|
@ -274,6 +274,37 @@ public class StatementGenerator implements InstructionVisitor {
|
|||
assign(value, insn.getReceiver().getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
Expr value = Expr.var(insn.getValue().getIndex());
|
||||
switch (insn.getDirection()) {
|
||||
case FROM_INTEGER:
|
||||
switch (insn.getTargetType()) {
|
||||
case BYTE:
|
||||
value = Expr.binary(BinaryOperation.BITWISE_AND, value, Expr.constant(0xFF));
|
||||
break;
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
value = Expr.binary(BinaryOperation.BITWISE_AND, value, Expr.constant(0xFFFF));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TO_INTEGER:
|
||||
switch (insn.getTargetType()) {
|
||||
case BYTE:
|
||||
value = Expr.unary(UnaryOperation.BYTE_TO_INT, value);
|
||||
break;
|
||||
case SHORT:
|
||||
value = Expr.unary(UnaryOperation.SHORT_TO_INT, value);
|
||||
break;
|
||||
case CHARACTER:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
assign(value, insn.getReceiver().getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
switch (insn.getCondition()) {
|
||||
|
|
|
@ -27,5 +27,7 @@ public enum UnaryOperation {
|
|||
LENGTH,
|
||||
LONG_TO_NUM,
|
||||
NUM_TO_LONG,
|
||||
INT_TO_LONG
|
||||
INT_TO_LONG,
|
||||
BYTE_TO_INT,
|
||||
SHORT_TO_INT
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package org.teavm.model.instructions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public enum CastIntegerDirection {
|
||||
FROM_INTEGER,
|
||||
TO_INTEGER
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.teavm.model.instructions;
|
||||
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.Variable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class CastIntegerInstruction extends Instruction {
|
||||
private Variable value;
|
||||
private Variable receiver;
|
||||
private IntegerSubtype targetType;
|
||||
private CastIntegerDirection direction;
|
||||
|
||||
public CastIntegerInstruction(IntegerSubtype targetType, CastIntegerDirection direction) {
|
||||
this.targetType = targetType;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public Variable getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Variable value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Variable getReceiver() {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public void setReceiver(Variable receiver) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public IntegerSubtype getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public CastIntegerDirection getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(InstructionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
|
@ -31,6 +31,8 @@ public interface InstructionVisitor {
|
|||
|
||||
void visit(CastNumberInstruction insn);
|
||||
|
||||
void visit(CastIntegerInstruction insn);
|
||||
|
||||
void visit(BranchingInstruction insn);
|
||||
|
||||
void visit(BinaryBranchingInstruction insn);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package org.teavm.model.instructions;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public enum IntegerSubtype {
|
||||
BYTE,
|
||||
SHORT,
|
||||
CHARACTER
|
||||
}
|
|
@ -156,6 +156,10 @@ class ClassRefsRenamer implements InstructionVisitor {
|
|||
public void visit(CastNumberInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -163,6 +163,11 @@ public class DefinitionExtractor implements InstructionVisitor {
|
|||
definedVariables = new Variable[] { insn.getReceiver() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
definedVariables = new Variable[] { insn.getReceiver() };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ArrayLengthInstruction insn) {
|
||||
definedVariables = new Variable[] { insn.getReceiver() };
|
||||
|
|
|
@ -307,6 +307,13 @@ public class InstructionStringifier implements InstructionVisitor {
|
|||
.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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UnwrapArrayInstruction insn) {
|
||||
sb.append("@").append(insn.getReceiver().getIndex()).append("@").append(" := @")
|
||||
|
|
|
@ -160,6 +160,11 @@ public class InstructionTransitionExtractor implements InstructionVisitor {
|
|||
targets = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
targets = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ArrayLengthInstruction insn) {
|
||||
targets = null;
|
||||
|
|
|
@ -137,6 +137,11 @@ public class UnusedVariableElimination implements MethodOptimization {
|
|||
requestUsage(insn.getReceiver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
requestUsage(insn.getReceiver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -82,6 +82,10 @@ public class VariableEscapeAnalyzer {
|
|||
public void visit(CastNumberInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
escaping[insn.getOperand().getIndex()] = true;
|
||||
|
|
|
@ -97,6 +97,11 @@ public class VariableUsageGraphBuilder {
|
|||
use(insn.getReceiver(), insn.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
use(insn.getReceiver(), insn.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(BranchingInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ public class ProgramParser {
|
|||
private List<List<Instruction>> targetInstructions;
|
||||
private List<Instruction> builder = new ArrayList<>();
|
||||
private List<BasicBlock> basicBlocks = new ArrayList<>();
|
||||
private int[] localsMap;
|
||||
private int minLocal;
|
||||
private Program program;
|
||||
|
||||
|
@ -71,13 +72,18 @@ public class ProgramParser {
|
|||
if ((method.access & Opcodes.ACC_STATIC) == 0) {
|
||||
getVariable(var++);
|
||||
}
|
||||
ValueType[] desc = MethodDescriptor.parseSignature(method.desc);
|
||||
for (ValueType paramType : desc) {
|
||||
ValueType[] desc = MethodDescriptor.parse(method.desc).getParameterTypes();
|
||||
int mappedLocal = 0;
|
||||
localsMap = new int[desc.length * 2];
|
||||
for (int i = 0; i < desc.length; ++i) {
|
||||
ValueType paramType = desc[i];
|
||||
localsMap[mappedLocal++] = i;
|
||||
getVariable(var++);
|
||||
if (paramType instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive)paramType).getKind()) {
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
localsMap[mappedLocal++] = i;
|
||||
getVariable(var++);
|
||||
break;
|
||||
default:
|
||||
|
@ -85,6 +91,7 @@ public class ProgramParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
localsMap = Arrays.copyOf(localsMap, mappedLocal);
|
||||
}
|
||||
|
||||
private void prepare(MethodNode method) {
|
||||
|
@ -199,6 +206,13 @@ public class ProgramParser {
|
|||
builder.add(insn);
|
||||
}
|
||||
|
||||
private int mapLocal(int local) {
|
||||
if (local < localsMap.length) {
|
||||
local = localsMap[local];
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
private MethodVisitor methodVisitor = new MethodVisitor() {
|
||||
@Override
|
||||
public void visitVarInsn(int opcode, int local) {
|
||||
|
@ -206,24 +220,24 @@ public class ProgramParser {
|
|||
case Opcodes.ILOAD:
|
||||
case Opcodes.FLOAD:
|
||||
case Opcodes.ALOAD:
|
||||
emitAssignInsn(minLocal + local, currentDepth);
|
||||
emitAssignInsn(minLocal + mapLocal(local), currentDepth);
|
||||
currentDepth++;
|
||||
break;
|
||||
case Opcodes.LLOAD:
|
||||
case Opcodes.DLOAD:
|
||||
emitAssignInsn(minLocal + local, currentDepth);
|
||||
emitAssignInsn(minLocal + mapLocal(local), currentDepth);
|
||||
currentDepth += 2;
|
||||
break;
|
||||
case Opcodes.ISTORE:
|
||||
case Opcodes.FSTORE:
|
||||
case Opcodes.ASTORE:
|
||||
currentDepth--;
|
||||
emitAssignInsn(currentDepth, minLocal + local);
|
||||
emitAssignInsn(currentDepth, minLocal + mapLocal(local));
|
||||
break;
|
||||
case Opcodes.LSTORE:
|
||||
case Opcodes.DSTORE:
|
||||
currentDepth -= 2;
|
||||
emitAssignInsn(currentDepth, minLocal + local);
|
||||
emitAssignInsn(currentDepth, minLocal + mapLocal(local));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -499,11 +513,10 @@ public class ProgramParser {
|
|||
builder.add(insn);
|
||||
}
|
||||
|
||||
private void emitCast(ValueType targetType, int value, int result) {
|
||||
CastInstruction insn = new CastInstruction();
|
||||
private void emitNumberCast(NumericOperandType source, NumericOperandType target, int value, int result) {
|
||||
CastNumberInstruction insn = new CastNumberInstruction(source, target);
|
||||
insn.setReceiver(getVariable(result));
|
||||
insn.setValue(getVariable(value));
|
||||
insn.setTargetType(targetType);
|
||||
builder.add(insn);
|
||||
}
|
||||
|
||||
|
@ -783,21 +796,39 @@ public class ProgramParser {
|
|||
case Opcodes.FCONST_2:
|
||||
pushConstant(2F);
|
||||
break;
|
||||
case Opcodes.BALOAD:
|
||||
case Opcodes.BALOAD: {
|
||||
loadArrayElement(1, ArrayElementType.BYTE);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.BYTE,
|
||||
CastIntegerDirection.TO_INTEGER);
|
||||
insn.setValue(getVariable(currentDepth));
|
||||
insn.setReceiver(getVariable(currentDepth));
|
||||
builder.add(insn);
|
||||
break;
|
||||
}
|
||||
case Opcodes.IALOAD:
|
||||
loadArrayElement(1, ArrayElementType.INT);
|
||||
break;
|
||||
case Opcodes.FALOAD:
|
||||
loadArrayElement(1, ArrayElementType.FLOAT);
|
||||
break;
|
||||
case Opcodes.SALOAD:
|
||||
case Opcodes.SALOAD: {
|
||||
loadArrayElement(1, ArrayElementType.SHORT);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.SHORT,
|
||||
CastIntegerDirection.TO_INTEGER);
|
||||
insn.setValue(getVariable(currentDepth));
|
||||
insn.setReceiver(getVariable(currentDepth));
|
||||
builder.add(insn);
|
||||
break;
|
||||
case Opcodes.CALOAD:
|
||||
}
|
||||
case Opcodes.CALOAD: {
|
||||
loadArrayElement(1, ArrayElementType.CHAR);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHARACTER,
|
||||
CastIntegerDirection.TO_INTEGER);
|
||||
insn.setValue(getVariable(currentDepth));
|
||||
insn.setReceiver(getVariable(currentDepth));
|
||||
builder.add(insn);
|
||||
break;
|
||||
}
|
||||
case Opcodes.AALOAD:
|
||||
loadArrayElement(1, ArrayElementType.OBJECT);
|
||||
break;
|
||||
|
@ -1169,85 +1200,97 @@ public class ProgramParser {
|
|||
}
|
||||
case Opcodes.I2B: {
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.BYTE, val, val);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.BYTE,
|
||||
CastIntegerDirection.FROM_INTEGER);
|
||||
insn.setValue(getVariable(val));
|
||||
insn.setReceiver(getVariable(val));
|
||||
builder.add(insn);
|
||||
break;
|
||||
}
|
||||
case Opcodes.I2C: {
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.CHARACTER, val, val);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHARACTER,
|
||||
CastIntegerDirection.FROM_INTEGER);
|
||||
insn.setValue(getVariable(val));
|
||||
insn.setReceiver(getVariable(val));
|
||||
builder.add(insn);
|
||||
break;
|
||||
}
|
||||
case Opcodes.I2S: {
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.SHORT, val, val);
|
||||
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.SHORT,
|
||||
CastIntegerDirection.FROM_INTEGER);
|
||||
insn.setValue(getVariable(val));
|
||||
insn.setReceiver(getVariable(val));
|
||||
builder.add(insn);
|
||||
break;
|
||||
}
|
||||
case Opcodes.I2F: {
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.FLOAT, val, val);
|
||||
emitNumberCast(NumericOperandType.INT, NumericOperandType.FLOAT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.I2L: {
|
||||
int val = currentDepth - 1;
|
||||
++currentDepth;
|
||||
emitCast(ValueType.LONG, val, val);
|
||||
emitNumberCast(NumericOperandType.INT, NumericOperandType.LONG, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.I2D: {
|
||||
int val = currentDepth - 1;
|
||||
++currentDepth;
|
||||
emitCast(ValueType.DOUBLE, val, val);
|
||||
emitNumberCast(NumericOperandType.INT, NumericOperandType.DOUBLE, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.F2I: {
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.INTEGER, val, val);
|
||||
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.INT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.F2L: {
|
||||
int val = currentDepth - 1;
|
||||
++currentDepth;
|
||||
emitCast(ValueType.LONG, val, val);
|
||||
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.LONG, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.F2D: {
|
||||
int val = currentDepth - 1;
|
||||
++currentDepth;
|
||||
emitCast(ValueType.DOUBLE, val, val);
|
||||
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.DOUBLE, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.D2L: {
|
||||
int val = currentDepth - 2;
|
||||
emitCast(ValueType.LONG, val, val);
|
||||
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.LONG, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.D2I: {
|
||||
--currentDepth;
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.INTEGER, val, val);
|
||||
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.INT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.D2F: {
|
||||
--currentDepth;
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.FLOAT, val, val);
|
||||
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.FLOAT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.L2I: {
|
||||
--currentDepth;
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.INTEGER, val, val);
|
||||
emitNumberCast(NumericOperandType.LONG, NumericOperandType.INT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.L2F: {
|
||||
--currentDepth;
|
||||
int val = currentDepth - 1;
|
||||
emitCast(ValueType.FLOAT, val, val);
|
||||
emitNumberCast(NumericOperandType.LONG, NumericOperandType.FLOAT, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.L2D: {
|
||||
int val = currentDepth - 2;
|
||||
emitCast(ValueType.DOUBLE, val, val);
|
||||
emitNumberCast(NumericOperandType.LONG, NumericOperandType.DOUBLE, val, val);
|
||||
break;
|
||||
}
|
||||
case Opcodes.IRETURN:
|
||||
|
@ -1302,6 +1345,7 @@ public class ProgramParser {
|
|||
|
||||
@Override
|
||||
public void visitIincInsn(int var, int increment) {
|
||||
var = mapLocal(var);
|
||||
var += minLocal;
|
||||
IntegerConstantInstruction intInsn = new IntegerConstantInstruction();
|
||||
intInsn.setConstant(increment);
|
||||
|
|
|
@ -351,6 +351,12 @@ public class SSATransformer {
|
|||
insn.setReceiver(define(insn.getReceiver()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(CastIntegerInstruction insn) {
|
||||
insn.setValue(use(insn.getValue()));
|
||||
insn.setReceiver(define(insn.getReceiver()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ArrayLengthInstruction insn) {
|
||||
insn.setArray(use(insn.getArray()));
|
||||
|
|
|
@ -174,6 +174,12 @@ $rt_throw = function(ex) {
|
|||
err.$javaException = ex;
|
||||
throw err;
|
||||
}
|
||||
$rt_byteToInt = function(value) {
|
||||
return value > 0xFF ? value | 0xFFFFFF00 : value;
|
||||
}
|
||||
$rt_shortToInt = function(value) {
|
||||
return value > 0xFFFF ? value | 0xFFFF0000 : value;
|
||||
}
|
||||
|
||||
$rt = {
|
||||
createBooleanArray : function(cls, sz) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user