Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.teavm.parsing.ProgramParser Maven / Gradle / Ivy
/*
* 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.parsing;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.teavm.model.BasicBlock;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.InvokeDynamicInstruction;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHandle;
import org.teavm.model.Program;
import org.teavm.model.ReferenceCache;
import org.teavm.model.RuntimeConstant;
import org.teavm.model.TextLocation;
import org.teavm.model.TryCatchBlock;
import org.teavm.model.ValueType;
import org.teavm.model.Variable;
import org.teavm.model.instructions.ArrayElementType;
import org.teavm.model.instructions.ArrayLengthInstruction;
import org.teavm.model.instructions.AssignInstruction;
import org.teavm.model.instructions.BinaryBranchingCondition;
import org.teavm.model.instructions.BinaryBranchingInstruction;
import org.teavm.model.instructions.BinaryInstruction;
import org.teavm.model.instructions.BinaryOperation;
import org.teavm.model.instructions.BranchingCondition;
import org.teavm.model.instructions.BranchingInstruction;
import org.teavm.model.instructions.CastInstruction;
import org.teavm.model.instructions.CastIntegerDirection;
import org.teavm.model.instructions.CastIntegerInstruction;
import org.teavm.model.instructions.CastNumberInstruction;
import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.CloneArrayInstruction;
import org.teavm.model.instructions.ConstructArrayInstruction;
import org.teavm.model.instructions.ConstructInstruction;
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.EmptyInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.GetElementInstruction;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.IntegerSubtype;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.IsInstanceInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.MonitorEnterInstruction;
import org.teavm.model.instructions.MonitorExitInstruction;
import org.teavm.model.instructions.NegateInstruction;
import org.teavm.model.instructions.NullConstantInstruction;
import org.teavm.model.instructions.NumericOperandType;
import org.teavm.model.instructions.PutElementInstruction;
import org.teavm.model.instructions.PutFieldInstruction;
import org.teavm.model.instructions.RaiseInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
import org.teavm.model.instructions.SwitchInstruction;
import org.teavm.model.instructions.SwitchTableEntry;
import org.teavm.model.instructions.UnwrapArrayInstruction;
import org.teavm.model.util.TransitionExtractor;
public class ProgramParser {
private ReferenceCache referenceCache;
private static final byte ROOT = 0;
private static final byte SINGLE = 1;
private static final byte DOUBLE_FIRST_HALF = 2;
private static final byte DOUBLE_SECOND_HALF = 3;
private String fileName;
private StackFrame[] stackBefore;
private StackFrame[] stackAfter;
private StackFrame stack;
private int index;
private int[] nextIndexes;
private Map labelIndexes;
private Map lineNumbers;
private List> targetInstructions;
private List builder = new ArrayList<>();
private List basicBlocks = new ArrayList<>();
private int minLocal;
private Program program;
private Map> localVariableMap = new HashMap<>();
private Map> variableDebugNames = new HashMap<>();
public ProgramParser(ReferenceCache methodReferenceCache) {
this.referenceCache = methodReferenceCache;
}
private static class Step {
public final int source;
public final int target;
public Step(int source, int target) {
this.source = source;
this.target = target;
}
}
private static class StackFrame {
final StackFrame next;
final byte type;
final int depth;
StackFrame(int depth) {
this.next = null;
this.type = ROOT;
this.depth = depth;
}
StackFrame(StackFrame next, byte type) {
this.next = next;
this.type = type;
this.depth = next != null ? next.depth + 1 : 0;
}
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public Program parse(MethodNode method) {
program = new Program();
InsnList instructions = method.instructions;
if (instructions.size() == 0) {
return program;
}
prepare(method);
program.createBasicBlock();
getBasicBlock(0);
JumpInstruction insn = new JumpInstruction();
insn.setTarget(program.basicBlockAt(1));
program.basicBlockAt(0).add(insn);
doAnalyze(method);
assemble(method);
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlock block = program.basicBlockAt(i);
for (int j = 0; j < block.getTryCatchBlocks().size(); ++j) {
TryCatchBlock tryCatch = block.getTryCatchBlocks().get(j);
if (tryCatch.getHandler() == block) {
block.getTryCatchBlocks().remove(j--);
}
}
}
int signatureVars = countSignatureVariables(method.desc);
while (program.variableCount() <= signatureVars) {
program.createVariable();
}
return program;
}
private int countSignatureVariables(String desc) {
int count = 1;
for (Type paramType : Type.getArgumentTypes(desc)) {
count += paramType.getSize();
}
return count;
}
private int pushSingle() {
stack = new StackFrame(stack, SINGLE);
return stack.depth;
}
private int pushDouble() {
stack = new StackFrame(stack, DOUBLE_FIRST_HALF);
stack = new StackFrame(stack, DOUBLE_SECOND_HALF);
return stack.next.depth;
}
private int popSingle() {
if (stack == null || stack.type != SINGLE) {
throw new AssertionError("Illegal stack state at " + index);
}
int depth = stack.depth;
stack = stack.next;
return depth;
}
private int popDouble() {
if (stack == null || stack.type != DOUBLE_SECOND_HALF) {
throw new AssertionError("***Illegal stack state at " + index);
}
stack = stack.next;
if (stack == null || stack.type != DOUBLE_FIRST_HALF) {
throw new AssertionError("***Illegal stack state at " + index);
}
int depth = stack.depth;
stack = stack.next;
return depth;
}
public Map getDebugNames(Instruction insn) {
Map map = variableDebugNames.get(insn);
return map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap();
}
private void prepare(MethodNode method) {
InsnList instructions = method.instructions;
minLocal = 0;
if ((method.access & Opcodes.ACC_STATIC) != 0) {
minLocal = 1;
}
labelIndexes = new HashMap<>();
lineNumbers = new HashMap<>();
for (int i = 0; i < instructions.size(); ++i) {
AbstractInsnNode node = instructions.get(i);
if (node instanceof LabelNode) {
labelIndexes.put(((LabelNode) node).getLabel(), i);
}
if (node instanceof LineNumberNode) {
LineNumberNode lineNumberNode = (LineNumberNode) node;
lineNumbers.put(lineNumberNode.start.getLabel(), lineNumberNode.line);
}
}
for (LocalVariableNode localVar : method.localVariables) {
int location = labelIndexes.get(localVar.start.getLabel());
localVariableMap.computeIfAbsent(location, k -> new ArrayList<>()).add(localVar);
}
targetInstructions = new ArrayList<>(instructions.size());
targetInstructions.addAll(Collections.nCopies(instructions.size(), null));
basicBlocks.addAll(Collections.nCopies(instructions.size(), null));
stackBefore = new StackFrame[instructions.size()];
stackAfter = new StackFrame[instructions.size()];
}
private void doAnalyze(MethodNode method) {
InsnList instructions = method.instructions;
Deque workStack = new ArrayDeque<>();
for (Object obj : method.tryCatchBlocks) {
TryCatchBlockNode tryCatchNode = (TryCatchBlockNode) obj;
if (tryCatchNode.start == tryCatchNode.handler) {
continue;
}
workStack.push(new Step(-2, labelIndexes.get(tryCatchNode.handler.getLabel())));
}
workStack.push(new Step(-1, 0));
while (!workStack.isEmpty()) {
Step step = workStack.pop();
index = step.target;
if (stackBefore[index] != null) {
continue;
}
switch (step.source) {
case -1:
stack = new StackFrame(minLocal + method.maxLocals - 1);
break;
case -2:
stack = new StackFrame(minLocal + method.maxLocals - 1);
pushSingle();
break;
default:
stack = stackAfter[step.source];
break;
}
stackBefore[index] = stack;
nextIndexes = new int[] { index + 1 };
instructions.get(index).accept(methodVisitor);
stackAfter[index] = stack;
flushInstructions();
if (nextIndexes.length != 1) {
emitNextBasicBlock();
}
for (int next : nextIndexes) {
workStack.push(new Step(index, next));
}
}
for (Object obj : method.tryCatchBlocks) {
TryCatchBlockNode tryCatchNode = (TryCatchBlockNode) obj;
if (tryCatchNode.start == tryCatchNode.handler) {
continue;
}
int start = labelIndexes.get(tryCatchNode.start.getLabel());
int end = labelIndexes.get(tryCatchNode.end.getLabel());
getBasicBlock(start);
getBasicBlock(end);
for (int i = start; i < end; ++i) {
BasicBlock block = basicBlocks.get(i);
if (block != null) {
TryCatchBlock tryCatch = new TryCatchBlock();
if (tryCatchNode.type != null) {
tryCatch.setExceptionType(referenceCache.getCached(tryCatchNode.type.replace('/', '.')));
}
tryCatch.setHandler(getBasicBlock(labelIndexes.get(tryCatchNode.handler.getLabel())));
tryCatch.getHandler().setExceptionVariable(program.variableAt(minLocal + method.maxLocals));
block.getTryCatchBlocks().add(tryCatch);
}
}
}
}
private void assemble(MethodNode methodNode) {
BasicBlock basicBlock = null;
Map accumulatedDebugNames = new HashMap<>();
Integer lastLineNumber = null;
TextLocation lastLocation = TextLocation.EMPTY;
for (int i = 0; i < basicBlocks.size(); ++i) {
BasicBlock newBasicBlock = basicBlocks.get(i);
if (newBasicBlock != null) {
if (basicBlock != null && !hasProperLastInstruction(basicBlock)) {
JumpInstruction insn = new JumpInstruction();
insn.setTarget(newBasicBlock);
basicBlock.add(insn);
}
basicBlock = newBasicBlock;
if (basicBlock.instructionCount() > 0) {
Map debugNames = new HashMap<>(accumulatedDebugNames);
variableDebugNames.put(basicBlock.getFirstInstruction(), debugNames);
}
}
List builtInstructions = targetInstructions.get(i);
List localVarNodes = localVariableMap.get(i);
if (localVarNodes != null) {
if (builtInstructions == null || builtInstructions.isEmpty()) {
builtInstructions = Arrays.asList(new EmptyInstruction());
}
Map debugNames = new HashMap<>();
variableDebugNames.put(builtInstructions.get(0), debugNames);
for (LocalVariableNode localVar : localVarNodes) {
debugNames.put(localVar.index + minLocal, referenceCache.getCached(localVar.name));
}
accumulatedDebugNames.putAll(debugNames);
}
AbstractInsnNode insnNode = methodNode.instructions.get(i);
if (insnNode instanceof LabelNode) {
Label label = ((LabelNode) insnNode).getLabel();
Integer lineNumber = lineNumbers.get(label);
if (lineNumber != null && !lineNumber.equals(lastLineNumber)) {
lastLineNumber = lineNumber;
lastLocation = new TextLocation(fileName, lastLineNumber);
}
}
if (builtInstructions != null) {
for (Instruction insn : builtInstructions) {
insn.setLocation(lastLocation);
}
basicBlock.addAll(builtInstructions);
}
}
}
private boolean hasProperLastInstruction(BasicBlock basicBlock) {
Instruction lastInsn = basicBlock.getLastInstruction();
if (lastInsn == null) {
return false;
}
TransitionExtractor extractor = new TransitionExtractor();
lastInsn.acceptVisitor(extractor);
return extractor.getTargets() != null;
}
private void flushInstructions() {
targetInstructions.set(index, builder);
builder = new ArrayList<>();
}
private BasicBlock getBasicBlock(int index) {
BasicBlock block = basicBlocks.get(index);
if (block == null) {
block = program.createBasicBlock();
basicBlocks.set(index, block);
}
return block;
}
private void emitNextBasicBlock() {
if (index + 1 < basicBlocks.size()) {
getBasicBlock(index + 1);
}
}
private Variable getVariable(int index) {
while (index >= program.variableCount()) {
program.createVariable();
}
return program.variableAt(index);
}
private void emitAssignInsn(int source, int target) {
AssignInstruction insn = new AssignInstruction();
insn.setAssignee(getVariable(source));
insn.setReceiver(getVariable(target));
addInstruction(insn);
}
private void addInstruction(Instruction insn) {
builder.add(insn);
}
private int mapLocal(int local) {
return local;
}
// TODO: invokedynamic support (a great task, involving not only parser, but every layer of TeaVM)
private MethodVisitor methodVisitor = new MethodVisitor(AsmUtil.API_VERSION) {
@Override
public void visitVarInsn(int opcode, int local) {
switch (opcode) {
case Opcodes.ILOAD:
case Opcodes.FLOAD:
case Opcodes.ALOAD:
emitAssignInsn(minLocal + mapLocal(local), pushSingle());
break;
case Opcodes.LLOAD:
case Opcodes.DLOAD:
emitAssignInsn(minLocal + mapLocal(local), pushDouble());
break;
case Opcodes.ISTORE:
case Opcodes.FSTORE:
case Opcodes.ASTORE:
emitAssignInsn(popSingle(), minLocal + mapLocal(local));
break;
case Opcodes.LSTORE:
case Opcodes.DSTORE:
emitAssignInsn(popDouble(), minLocal + mapLocal(local));
break;
}
}
private ValueType parseType(String type) {
if (type.startsWith("[")) {
return referenceCache.parseValueTypeCached(type);
} else {
return referenceCache.getCached(ValueType.object(type.replace('/', '.')));
}
}
@Override
public void visitTypeInsn(int opcode, String type) {
switch (opcode) {
case Opcodes.NEW: {
String cls = type.replace('/', '.');
ConstructInstruction insn = new ConstructInstruction();
insn.setReceiver(getVariable(pushSingle()));
insn.setType(referenceCache.getCached(cls));
addInstruction(insn);
break;
}
case Opcodes.ANEWARRAY: {
ValueType valueType = parseType(type);
ConstructArrayInstruction insn = new ConstructArrayInstruction();
insn.setSize(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setItemType(valueType);
addInstruction(insn);
break;
}
case Opcodes.INSTANCEOF: {
IsInstanceInstruction insn = new IsInstanceInstruction();
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setType(parseType(type));
addInstruction(insn);
break;
}
case Opcodes.CHECKCAST: {
CastInstruction insn = new CastInstruction();
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setTargetType(parseType(type));
addInstruction(insn);
break;
}
}
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
SwitchTableEntry[] table = new SwitchTableEntry[labels.length];
nextIndexes = new int[labels.length + 1];
for (int i = 0; i < labels.length; ++i) {
Label label = labels[i];
int target = labelIndexes.get(label);
SwitchTableEntry entry = new SwitchTableEntry();
entry.setCondition(i + min);
entry.setTarget(getBasicBlock(target));
table[i] = entry;
nextIndexes[i] = target;
}
SwitchInstruction insn = new SwitchInstruction();
insn.setCondition(getVariable(popSingle()));
insn.getEntries().addAll(Arrays.asList(table));
addInstruction(insn);
int defaultIndex = labelIndexes.get(dflt);
insn.setDefaultTarget(getBasicBlock(defaultIndex));
nextIndexes[labels.length] = defaultIndex;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
ValueType arrayType = parseType(desc);
Variable[] dimensions = new Variable[dims];
for (int i = dims - 1; i >= 0; --i) {
dimensions[i] = getVariable(popSingle());
}
ConstructMultiArrayInstruction insn = new ConstructMultiArrayInstruction();
insn.setItemType(arrayType);
insn.setReceiver(getVariable(pushSingle()));
insn.getDimensions().addAll(Arrays.asList(dimensions));
addInstruction(insn);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
InvokeDynamicInstruction insn = new InvokeDynamicInstruction();
insn.setBootstrapMethod(parseHandle(bsm));
switch (insn.getBootstrapMethod().getKind()) {
case GET_STATIC_FIELD:
case PUT_STATIC_FIELD:
case INVOKE_STATIC:
case INVOKE_CONSTRUCTOR:
break;
default:
insn.setInstance(getVariable(popSingle()));
break;
}
Type[] types = Type.getArgumentTypes(desc);
Variable[] args = new Variable[types.length];
int j = args.length;
for (int i = types.length - 1; i >= 0; --i) {
args[--j] = types[i].getSize() == 2 ? getVariable(popDouble()) : getVariable(popSingle());
}
insn.getArguments().addAll(Arrays.asList(args));
Type returnType = Type.getReturnType(desc);
if (returnType.getSize() > 0) {
insn.setReceiver(getVariable(returnType.getSize() == 2 ? pushDouble() : pushSingle()));
}
insn.setMethod(referenceCache.getCached(
new MethodDescriptor(name, MethodDescriptor.parseSignature(desc))));
for (Object bsmArg : bsmArgs) {
insn.getBootstrapArguments().add(convertConstant(bsmArg));
}
addInstruction(insn);
}
private RuntimeConstant convertConstant(Object value) {
if (value instanceof Integer) {
return new RuntimeConstant((Integer) value);
} else if (value instanceof Long) {
return new RuntimeConstant((Long) value);
} else if (value instanceof Float) {
return new RuntimeConstant((Float) value);
} else if (value instanceof Double) {
return new RuntimeConstant((Double) value);
} else if (value instanceof String) {
return new RuntimeConstant((String) value);
} else if (value instanceof Type) {
Type type = (Type) value;
if (type.getSort() == Type.METHOD) {
return new RuntimeConstant(parseSignature(type.getDescriptor()));
} else {
return new RuntimeConstant(referenceCache.parseValueTypeCached(type.getDescriptor()));
}
} else if (value instanceof Handle) {
return new RuntimeConstant(parseHandle((Handle) value));
} else {
throw new IllegalArgumentException("Unknown runtime constant: " + value);
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
switch (opcode) {
case Opcodes.INVOKEINTERFACE:
case Opcodes.INVOKEVIRTUAL:
case Opcodes.INVOKESPECIAL:
case Opcodes.INVOKESTATIC: {
String ownerCls;
if (owner.startsWith("[")) {
if (name.equals("clone") && desc.startsWith("()")) {
CloneArrayInstruction insn = new CloneArrayInstruction();
insn.setArray(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
ownerCls = "java.lang.Object";
} else {
ownerCls = owner.replace('/', '.');
}
Type[] types = Type.getArgumentTypes(desc);
Variable[] args = new Variable[types.length];
int j = args.length;
for (int i = types.length - 1; i >= 0; --i) {
args[--j] = types[i].getSize() == 2 ? getVariable(popDouble()) : getVariable(popSingle());
}
MethodDescriptor method = referenceCache.getCached(
new MethodDescriptor(name, MethodDescriptor.parseSignature(desc)));
int instance = -1;
if (opcode != Opcodes.INVOKESTATIC) {
instance = popSingle();
}
Type returnType = Type.getReturnType(desc);
int result = -1;
if (returnType.getSize() > 0) {
result = returnType.getSize() == 2 ? pushDouble() : pushSingle();
}
if (instance == -1) {
InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL);
insn.setMethod(referenceCache.getCached(ownerCls, method));
if (result >= 0) {
insn.setReceiver(getVariable(result));
}
insn.setArguments(args);
addInstruction(insn);
} else {
InvokeInstruction insn = new InvokeInstruction();
if (opcode == Opcodes.INVOKESPECIAL) {
insn.setType(InvocationType.SPECIAL);
} else {
insn.setType(InvocationType.VIRTUAL);
}
insn.setMethod(referenceCache.getCached(ownerCls, method));
if (result >= 0) {
insn.setReceiver(getVariable(result));
}
insn.setInstance(getVariable(instance));
insn.setArguments(args);
addInstruction(insn);
}
break;
}
}
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
SwitchTableEntry[] table = new SwitchTableEntry[labels.length];
nextIndexes = new int[labels.length + 1];
for (int i = 0; i < labels.length; ++i) {
Label label = labels[i];
int target = labelIndexes.get(label);
SwitchTableEntry entry = new SwitchTableEntry();
entry.setCondition(keys[i]);
entry.setTarget(getBasicBlock(target));
table[i] = entry;
nextIndexes[i] = target;
}
SwitchInstruction insn = new SwitchInstruction();
insn.setCondition(getVariable(popSingle()));
insn.getEntries().addAll(Arrays.asList(table));
addInstruction(insn);
int defaultTarget = labelIndexes.get(dflt);
insn.setDefaultTarget(getBasicBlock(defaultTarget));
nextIndexes[labels.length] = labelIndexes.get(dflt);
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
@Override
public void visitLineNumber(int line, Label start) {
}
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Integer) {
pushConstant((Integer) cst);
} else if (cst instanceof Float) {
pushConstant((Float) cst);
} else if (cst instanceof Long) {
pushConstant((Long) cst);
} else if (cst instanceof Double) {
pushConstant((Double) cst);
} else if (cst instanceof String) {
StringConstantInstruction insn = new StringConstantInstruction();
insn.setConstant(referenceCache.getCached((String) cst));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
} else if (cst instanceof Type) {
ClassConstantInstruction insn = new ClassConstantInstruction();
insn.setConstant(referenceCache.getCached(ValueType.parse(((Type) cst).getDescriptor())));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
} else {
throw new IllegalArgumentException();
}
}
@Override
public void visitLabel(Label label) {
}
private void emitBranching(BranchingCondition condition, int value, int target) {
BranchingInstruction insn = new BranchingInstruction(condition);
insn.setOperand(getVariable(value));
insn.setConsequent(getBasicBlock(target));
insn.setAlternative(getBasicBlock(index + 1));
addInstruction(insn);
}
private void emitBranching(BinaryBranchingCondition condition, int first, int second,
int target) {
BinaryBranchingInstruction insn = new BinaryBranchingInstruction(condition);
insn.setFirstOperand(getVariable(first));
insn.setSecondOperand(getVariable(second));
insn.setConsequent(getBasicBlock(target));
insn.setAlternative(getBasicBlock(index + 1));
addInstruction(insn);
}
private void emitBinary(BinaryOperation operation, NumericOperandType operandType,
int first, int second, int receiver) {
BinaryInstruction insn = new BinaryInstruction(operation, operandType);
insn.setFirstOperand(getVariable(first));
insn.setSecondOperand(getVariable(second));
insn.setReceiver(getVariable(receiver));
addInstruction(insn);
}
private void emitNeg(NumericOperandType operandType, int operand, int receiver) {
NegateInstruction insn = new NegateInstruction(operandType);
insn.setOperand(getVariable(operand));
insn.setReceiver(getVariable(receiver));
addInstruction(insn);
}
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));
addInstruction(insn);
}
@Override
public void visitJumpInsn(int opcode, Label label) {
int target = labelIndexes.get(label);
switch (opcode) {
case Opcodes.IFEQ:
emitBranching(BranchingCondition.EQUAL, popSingle(), target);
break;
case Opcodes.IFNE:
emitBranching(BranchingCondition.NOT_EQUAL, popSingle(), target);
break;
case Opcodes.IFNULL:
emitBranching(BranchingCondition.NULL, popSingle(), target);
break;
case Opcodes.IFNONNULL:
emitBranching(BranchingCondition.NOT_NULL, popSingle(), target);
break;
case Opcodes.IFGT:
emitBranching(BranchingCondition.GREATER, popSingle(), target);
break;
case Opcodes.IFGE:
emitBranching(BranchingCondition.GREATER_OR_EQUAL, popSingle(), target);
break;
case Opcodes.IFLT:
emitBranching(BranchingCondition.LESS, popSingle(), target);
break;
case Opcodes.IFLE:
emitBranching(BranchingCondition.LESS_OR_EQUAL, popSingle(), target);
break;
case Opcodes.IF_ACMPEQ: {
int b = popSingle();
int a = popSingle();
emitBranching(BinaryBranchingCondition.REFERENCE_EQUAL, a, b, target);
break;
}
case Opcodes.IF_ACMPNE: {
int b = popSingle();
int a = popSingle();
emitBranching(BinaryBranchingCondition.REFERENCE_NOT_EQUAL, a, b, target);
break;
}
case Opcodes.IF_ICMPEQ: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.EQUAL, a, target);
break;
}
case Opcodes.IF_ICMPNE: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.NOT_EQUAL, a, target);
break;
}
case Opcodes.IF_ICMPGE: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.GREATER_OR_EQUAL, a, target);
break;
}
case Opcodes.IF_ICMPGT: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.GREATER, a, target);
break;
}
case Opcodes.IF_ICMPLE: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.LESS_OR_EQUAL, a, target);
break;
}
case Opcodes.IF_ICMPLT: {
int b = popSingle();
int a = popSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.INT, a, b, a);
emitBranching(BranchingCondition.LESS, a, target);
break;
}
case Opcodes.GOTO: {
JumpInstruction insn = new JumpInstruction();
insn.setTarget(getBasicBlock(target));
addInstruction(insn);
nextIndexes = new int[] { labelIndexes.get(label) };
return;
}
default:
throw new RuntimeException("Unknown opcode");
}
nextIndexes = new int[] { labelIndexes.get(label), index + 1 };
}
@Override
public void visitIntInsn(int opcode, int operand) {
switch (opcode) {
case Opcodes.BIPUSH:
case Opcodes.SIPUSH: {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setConstant(operand);
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.NEWARRAY: {
ValueType itemType;
switch (operand) {
case Opcodes.T_BOOLEAN:
itemType = ValueType.BOOLEAN;
break;
case Opcodes.T_BYTE:
itemType = ValueType.BYTE;
break;
case Opcodes.T_SHORT:
itemType = ValueType.SHORT;
break;
case Opcodes.T_LONG:
itemType = ValueType.LONG;
break;
case Opcodes.T_INT:
itemType = ValueType.INTEGER;
break;
case Opcodes.T_CHAR:
itemType = ValueType.CHARACTER;
break;
case Opcodes.T_DOUBLE:
itemType = ValueType.DOUBLE;
break;
case Opcodes.T_FLOAT:
itemType = ValueType.FLOAT;
break;
default:
throw new RuntimeException("Illegal opcode");
}
ConstructArrayInstruction insn = new ConstructArrayInstruction();
insn.setSize(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
insn.setItemType(itemType);
addInstruction(insn);
break;
}
}
}
private void pushConstant(int value) {
IntegerConstantInstruction insn = new IntegerConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
}
private void pushConstant(long value) {
LongConstantInstruction insn = new LongConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushDouble()));
addInstruction(insn);
}
private void pushConstant(double value) {
DoubleConstantInstruction insn = new DoubleConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushDouble()));
addInstruction(insn);
}
private void pushConstant(float value) {
FloatConstantInstruction insn = new FloatConstantInstruction();
insn.setConstant(value);
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
}
private void loadArrayElement(int sz, ArrayElementType type) {
int arrIndex = popSingle();
int array = popSingle();
int var = sz == 1 ? pushSingle() : pushDouble();
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(type);
unwrapInsn.setArray(getVariable(array));
unwrapInsn.setReceiver(unwrapInsn.getArray());
addInstruction(unwrapInsn);
GetElementInstruction insn = new GetElementInstruction(type);
insn.setArray(getVariable(array));
insn.setIndex(getVariable(arrIndex));
insn.setReceiver(getVariable(var));
addInstruction(insn);
}
private void storeArrayElement(int sz, ArrayElementType type) {
int value = sz == 1 ? popSingle() : popDouble();
int arrIndex = popSingle();
int array = popSingle();
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(type);
unwrapInsn.setArray(getVariable(array));
unwrapInsn.setReceiver(unwrapInsn.getArray());
addInstruction(unwrapInsn);
PutElementInstruction insn = new PutElementInstruction(type);
insn.setArray(getVariable(array));
insn.setIndex(getVariable(arrIndex));
insn.setValue(getVariable(value));
addInstruction(insn);
}
@Override
public void visitInsn(int opcode) {
switch (opcode) {
case Opcodes.ACONST_NULL: {
NullConstantInstruction insn = new NullConstantInstruction();
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.ICONST_M1:
pushConstant(-1);
break;
case Opcodes.ICONST_0:
pushConstant(0);
break;
case Opcodes.ICONST_1:
pushConstant(1);
break;
case Opcodes.ICONST_2:
pushConstant(2);
break;
case Opcodes.ICONST_3:
pushConstant(3);
break;
case Opcodes.ICONST_4:
pushConstant(4);
break;
case Opcodes.ICONST_5:
pushConstant(5);
break;
case Opcodes.LCONST_0:
pushConstant(0L);
break;
case Opcodes.LCONST_1:
pushConstant(1L);
break;
case Opcodes.DCONST_0:
pushConstant(0.0);
break;
case Opcodes.DCONST_1:
pushConstant(1.0);
break;
case Opcodes.FCONST_0:
pushConstant(0F);
break;
case Opcodes.FCONST_1:
pushConstant(1F);
break;
case Opcodes.FCONST_2:
pushConstant(2F);
break;
case Opcodes.BALOAD: {
loadArrayElement(1, ArrayElementType.BYTE);
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.BYTE,
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.IALOAD:
loadArrayElement(1, ArrayElementType.INT);
break;
case Opcodes.FALOAD:
loadArrayElement(1, ArrayElementType.FLOAT);
break;
case Opcodes.SALOAD: {
loadArrayElement(1, ArrayElementType.SHORT);
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.SHORT,
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.CALOAD: {
loadArrayElement(1, ArrayElementType.CHAR);
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHAR,
CastIntegerDirection.TO_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.AALOAD:
loadArrayElement(1, ArrayElementType.OBJECT);
break;
case Opcodes.DALOAD:
loadArrayElement(2, ArrayElementType.DOUBLE);
break;
case Opcodes.LALOAD:
loadArrayElement(2, ArrayElementType.LONG);
break;
case Opcodes.BASTORE:
storeArrayElement(1, ArrayElementType.BYTE);
break;
case Opcodes.IASTORE:
storeArrayElement(1, ArrayElementType.INT);
break;
case Opcodes.FASTORE:
storeArrayElement(1, ArrayElementType.FLOAT);
break;
case Opcodes.SASTORE:
storeArrayElement(1, ArrayElementType.SHORT);
break;
case Opcodes.CASTORE:
storeArrayElement(1, ArrayElementType.CHAR);
break;
case Opcodes.AASTORE:
storeArrayElement(1, ArrayElementType.OBJECT);
break;
case Opcodes.DASTORE:
storeArrayElement(2, ArrayElementType.DOUBLE);
break;
case Opcodes.LASTORE:
storeArrayElement(2, ArrayElementType.LONG);
break;
case Opcodes.POP:
popSingle();
break;
case Opcodes.POP2:
if (stack.type == SINGLE) {
popSingle();
popSingle();
} else {
popDouble();
}
break;
case Opcodes.DUP: {
popSingle();
int orig = pushSingle();
int copy = pushSingle();
emitAssignInsn(orig, copy);
break;
}
case Opcodes.DUP_X1: {
popSingle();
popSingle();
int ins = pushSingle();
int b = pushSingle();
int a = pushSingle();
emitAssignInsn(a - 1, a);
emitAssignInsn(b - 1, b);
emitAssignInsn(a, ins);
break;
}
case Opcodes.DUP_X2: {
popSingle();
if (stack.type == SINGLE) {
popSingle();
popSingle();
int ins = pushSingle();
int c = pushSingle();
int b = pushSingle();
int a = pushSingle();
emitAssignInsn(a - 1, a);
emitAssignInsn(b - 1, b);
emitAssignInsn(c - 1, c);
emitAssignInsn(a, ins);
} else {
popDouble();
int ins = pushSingle();
int b = pushDouble();
int a = pushSingle();
emitAssignInsn(a - 1, a);
emitAssignInsn(b - 1, b);
emitAssignInsn(a, ins);
}
break;
}
case Opcodes.DUP2: {
if (stack.type == SINGLE) {
popSingle();
popSingle();
int origA = pushSingle();
int origB = pushSingle();
int copyA = pushSingle();
int copyB = pushSingle();
emitAssignInsn(origA, copyA);
emitAssignInsn(origB, copyB);
} else {
popDouble();
int orig = pushDouble();
int copy = pushDouble();
emitAssignInsn(orig, copy);
}
break;
}
case Opcodes.DUP2_X1: {
if (stack.type == SINGLE) {
popSingle();
popSingle();
popSingle();
int ins1 = pushSingle();
int ins2 = pushSingle();
int b = pushSingle();
int a1 = pushSingle();
int a2 = pushSingle();
emitAssignInsn(a2 - 2, a2);
emitAssignInsn(a1 - 2, a1);
emitAssignInsn(b - 2, b);
emitAssignInsn(a1, ins1);
emitAssignInsn(a2, ins2);
break;
} else {
popDouble();
popSingle();
int ins = pushDouble();
int b = pushSingle();
int a = pushDouble();
emitAssignInsn(a - 2, a);
emitAssignInsn(b - 2, b);
emitAssignInsn(a, ins);
break;
}
}
case Opcodes.DUP2_X2: {
if (stack.type == SINGLE) {
popSingle();
popSingle();
if (stack.type == SINGLE) {
popSingle();
popSingle();
int ins1 = pushSingle();
int ins2 = pushSingle();
int c = pushSingle();
int b = pushSingle();
int a1 = pushSingle();
int a2 = pushSingle();
emitAssignInsn(a2 - 2, a2);
emitAssignInsn(a1 - 2, a1);
emitAssignInsn(b - 2, b);
emitAssignInsn(c - 2, c);
emitAssignInsn(a1, ins1);
emitAssignInsn(a2, ins2);
} else {
popDouble();
int ins1 = pushSingle();
int ins2 = pushSingle();
int b = pushDouble();
int a1 = pushSingle();
int a2 = pushSingle();
emitAssignInsn(a2 - 2, a2);
emitAssignInsn(a1 - 2, a1);
emitAssignInsn(b - 2, b);
emitAssignInsn(a1, ins1);
emitAssignInsn(a2, ins2);
}
} else {
popDouble();
if (stack.type == SINGLE) {
popSingle();
popSingle();
int ins = pushDouble();
int c = pushSingle();
int b = pushSingle();
int a = pushDouble();
emitAssignInsn(a - 2, a);
emitAssignInsn(b - 2, b);
emitAssignInsn(c - 2, c);
emitAssignInsn(a, ins);
} else {
popDouble();
int ins = pushDouble();
int b = pushDouble();
int a = pushDouble();
emitAssignInsn(a - 2, a);
emitAssignInsn(b - 2, b);
emitAssignInsn(a, ins);
}
}
break;
}
case Opcodes.SWAP: {
int b = popSingle();
int a = popSingle();
pushSingle();
pushSingle();
int tmp = b + 1;
emitAssignInsn(a, tmp);
emitAssignInsn(b, a);
emitAssignInsn(tmp, b);
break;
}
case Opcodes.ISUB: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.SUBTRACT, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.FSUB: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.SUBTRACT, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.IADD: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.ADD, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.FADD: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.ADD, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.IMUL: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.MULTIPLY, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.FMUL: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.MULTIPLY, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.IDIV: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.DIVIDE, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.FDIV: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.DIVIDE, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.IREM: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.MODULO, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.FREM: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.MODULO, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.LADD: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.ADD, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.DADD: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.ADD, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.LSUB: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.SUBTRACT, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.DSUB: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.SUBTRACT, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.LMUL: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.MULTIPLY, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.DMUL: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.MULTIPLY, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.DDIV: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.DIVIDE, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.LDIV: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.DIVIDE, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.LREM: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.MODULO, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.DREM: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.MODULO, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.INEG: {
int a = popSingle();
int r = pushSingle();
emitNeg(NumericOperandType.INT, a, r);
break;
}
case Opcodes.FNEG: {
int a = popSingle();
int r = pushSingle();
emitNeg(NumericOperandType.FLOAT, a, r);
break;
}
case Opcodes.LNEG: {
int a = popDouble();
int r = pushDouble();
emitNeg(NumericOperandType.LONG, a, r);
break;
}
case Opcodes.DNEG: {
int a = popDouble();
int r = pushDouble();
emitNeg(NumericOperandType.DOUBLE, a, r);
break;
}
case Opcodes.FCMPG:
case Opcodes.FCMPL: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.FLOAT, a, b, r);
break;
}
case Opcodes.LCMP: {
int b = popDouble();
int a = popDouble();
int r = pushSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.DCMPG:
case Opcodes.DCMPL: {
int b = popDouble();
int a = popDouble();
int r = pushSingle();
emitBinary(BinaryOperation.COMPARE, NumericOperandType.DOUBLE, a, b, r);
break;
}
case Opcodes.ISHL: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.SHIFT_LEFT, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.ISHR: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.SHIFT_RIGHT, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.IUSHR: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.SHIFT_RIGHT_UNSIGNED,
NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.LSHL: {
int b = popSingle();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.SHIFT_LEFT, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.LSHR: {
int b = popSingle();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.SHIFT_RIGHT, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.LUSHR: {
int b = popSingle();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.SHIFT_RIGHT_UNSIGNED, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.IAND: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.AND, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.IOR: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.OR, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.IXOR: {
int b = popSingle();
int a = popSingle();
int r = pushSingle();
emitBinary(BinaryOperation.XOR, NumericOperandType.INT, a, b, r);
break;
}
case Opcodes.LAND: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.AND, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.LOR: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.OR, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.LXOR: {
int b = popDouble();
int a = popDouble();
int r = pushDouble();
emitBinary(BinaryOperation.XOR, NumericOperandType.LONG, a, b, r);
break;
}
case Opcodes.I2B: {
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.BYTE,
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.I2C: {
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.CHAR,
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.I2S: {
CastIntegerInstruction insn = new CastIntegerInstruction(IntegerSubtype.SHORT,
CastIntegerDirection.FROM_INTEGER);
insn.setValue(getVariable(popSingle()));
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
break;
}
case Opcodes.I2F: {
int a = popSingle();
int r = pushSingle();
emitNumberCast(NumericOperandType.INT, NumericOperandType.FLOAT, a, r);
break;
}
case Opcodes.I2L: {
int a = popSingle();
int r = pushDouble();
emitNumberCast(NumericOperandType.INT, NumericOperandType.LONG, a, r);
break;
}
case Opcodes.I2D: {
int a = popSingle();
int r = pushDouble();
emitNumberCast(NumericOperandType.INT, NumericOperandType.DOUBLE, a, r);
break;
}
case Opcodes.F2I: {
int a = popSingle();
int r = pushSingle();
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.INT, a, r);
break;
}
case Opcodes.F2L: {
int a = popSingle();
int r = pushDouble();
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.LONG, a, r);
break;
}
case Opcodes.F2D: {
int a = popSingle();
int r = pushDouble();
emitNumberCast(NumericOperandType.FLOAT, NumericOperandType.DOUBLE, a, r);
break;
}
case Opcodes.D2L: {
int a = popDouble();
int r = pushDouble();
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.LONG, a, r);
break;
}
case Opcodes.D2I: {
int a = popDouble();
int r = pushSingle();
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.INT, a, r);
break;
}
case Opcodes.D2F: {
int a = popDouble();
int r = pushSingle();
emitNumberCast(NumericOperandType.DOUBLE, NumericOperandType.FLOAT, a, r);
break;
}
case Opcodes.L2I: {
int a = popDouble();
int r = pushSingle();
emitNumberCast(NumericOperandType.LONG, NumericOperandType.INT, a, r);
break;
}
case Opcodes.L2F: {
int a = popDouble();
int r = pushSingle();
emitNumberCast(NumericOperandType.LONG, NumericOperandType.FLOAT, a, r);
break;
}
case Opcodes.L2D: {
int a = popDouble();
int r = pushDouble();
emitNumberCast(NumericOperandType.LONG, NumericOperandType.DOUBLE, a, r);
break;
}
case Opcodes.IRETURN:
case Opcodes.FRETURN:
case Opcodes.ARETURN: {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(getVariable(popSingle()));
addInstruction(insn);
nextIndexes = new int[0];
return;
}
case Opcodes.LRETURN:
case Opcodes.DRETURN: {
ExitInstruction insn = new ExitInstruction();
insn.setValueToReturn(getVariable(popDouble()));
addInstruction(insn);
nextIndexes = new int[0];
return;
}
case Opcodes.RETURN: {
ExitInstruction insn = new ExitInstruction();
addInstruction(insn);
nextIndexes = new int[0];
return;
}
case Opcodes.ARRAYLENGTH: {
int a = popSingle();
int r = pushSingle();
UnwrapArrayInstruction unwrapInsn = new UnwrapArrayInstruction(ArrayElementType.OBJECT);
unwrapInsn.setArray(getVariable(a));
unwrapInsn.setReceiver(getVariable(r));
addInstruction(unwrapInsn);
ArrayLengthInstruction insn = new ArrayLengthInstruction();
insn.setArray(getVariable(a));
insn.setReceiver(getVariable(r));
addInstruction(insn);
break;
}
case Opcodes.ATHROW: {
RaiseInstruction insn = new RaiseInstruction();
insn.setException(getVariable(popSingle()));
addInstruction(insn);
nextIndexes = new int[0];
return;
}
case Opcodes.MONITORENTER: {
MonitorEnterInstruction insn = new MonitorEnterInstruction();
insn.setObjectRef(getVariable(popSingle()));
addInstruction(insn);
break;
}
case Opcodes.MONITOREXIT: {
MonitorExitInstruction insn = new MonitorExitInstruction();
insn.setObjectRef(getVariable(popSingle()));
addInstruction(insn);
break;
}
}
}
@Override
public void visitIincInsn(int var, int increment) {
var = minLocal + mapLocal(var);
int tmp = pushSingle();
popSingle();
IntegerConstantInstruction intInsn = new IntegerConstantInstruction();
intInsn.setConstant(increment);
intInsn.setReceiver(getVariable(tmp));
addInstruction(intInsn);
emitBinary(BinaryOperation.ADD, NumericOperandType.INT, var, tmp, var);
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
String ownerCls = owner.replace('/', '.');
switch (opcode) {
case Opcodes.GETFIELD: {
int instance = popSingle();
ValueType type = referenceCache.parseValueTypeCached(desc);
int value = desc.equals("D") || desc.equals("J") ? pushDouble() : pushSingle();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setInstance(getVariable(instance));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
addInstruction(insn);
break;
}
case Opcodes.PUTFIELD: {
int value = desc.equals("D") || desc.equals("J") ? popDouble() : popSingle();
int instance = popSingle();
PutFieldInstruction insn = new PutFieldInstruction();
insn.setInstance(getVariable(instance));
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setValue(getVariable(value));
insn.setFieldType(referenceCache.parseValueTypeCached(desc));
addInstruction(insn);
break;
}
case Opcodes.GETSTATIC: {
ValueType primitiveClassLiteral = getPrimitiveTypeField(owner + "." + name);
if (primitiveClassLiteral != null) {
ClassConstantInstruction insn = new ClassConstantInstruction();
insn.setConstant(primitiveClassLiteral);
insn.setReceiver(getVariable(pushSingle()));
addInstruction(insn);
} else {
ValueType type = referenceCache.parseValueTypeCached(desc);
int value = desc.equals("D") || desc.equals("J") ? pushDouble() : pushSingle();
GetFieldInstruction insn = new GetFieldInstruction();
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setFieldType(type);
insn.setReceiver(getVariable(value));
addInstruction(insn);
}
break;
}
case Opcodes.PUTSTATIC: {
int value = desc.equals("D") || desc.equals("J") ? popDouble() : popSingle();
PutFieldInstruction insn = new PutFieldInstruction();
insn.setField(referenceCache.getCached(new FieldReference(ownerCls, name)));
insn.setValue(getVariable(value));
insn.setFieldType(referenceCache.parseValueTypeCached(desc));
addInstruction(insn);
break;
}
}
}
@Override
public void visitEnd() {
}
@Override
public void visitCode() {
}
@Override
public void visitAttribute(Attribute attr) {
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
};
private MethodHandle parseHandle(Handle handle) {
String owner = referenceCache.getCached(handle.getOwner().replace('/', '.'));
String name = referenceCache.getCached(handle.getName());
switch (handle.getTag()) {
case Opcodes.H_GETFIELD:
return MethodHandle.fieldGetter(owner, name,
referenceCache.getCached(ValueType.parse(handle.getDesc())));
case Opcodes.H_GETSTATIC:
return MethodHandle.staticFieldGetter(owner, name,
referenceCache.getCached(ValueType.parse(handle.getDesc())));
case Opcodes.H_PUTFIELD:
return MethodHandle.fieldSetter(owner, name,
referenceCache.getCached(ValueType.parse(handle.getDesc())));
case Opcodes.H_PUTSTATIC:
return MethodHandle.staticFieldSetter(owner, name,
referenceCache.getCached(ValueType.parse(handle.getDesc())));
case Opcodes.H_INVOKEVIRTUAL:
return MethodHandle.virtualCaller(owner, name, parseSignature(handle.getDesc()));
case Opcodes.H_INVOKESTATIC:
return MethodHandle.staticCaller(owner, name, parseSignature(handle.getDesc()));
case Opcodes.H_INVOKESPECIAL:
return MethodHandle.specialCaller(owner, name, parseSignature(handle.getDesc()));
case Opcodes.H_NEWINVOKESPECIAL:
return MethodHandle.constructorCaller(owner, name, parseSignature(handle.getDesc()));
case Opcodes.H_INVOKEINTERFACE:
return MethodHandle.interfaceCaller(owner, name, parseSignature(handle.getDesc()));
default:
throw new IllegalArgumentException("Unknown handle tag: " + handle.getTag());
}
}
private ValueType[] parseSignature(String desc) {
ValueType[] signature = MethodDescriptor.parseSignature(desc);
for (int i = 0; i < signature.length; ++i) {
signature[i] = referenceCache.getCached(signature[i]);
}
return signature;
}
private static ValueType getPrimitiveTypeField(String fieldName) {
switch (fieldName) {
case "java/lang/Boolean.TYPE":
return ValueType.BOOLEAN;
case "java/lang/Byte.TYPE":
return ValueType.BYTE;
case "java/lang/Short.TYPE":
return ValueType.SHORT;
case "java/lang/Character.TYPE":
return ValueType.CHARACTER;
case "java/lang/Integer.TYPE":
return ValueType.INTEGER;
case "java/lang/Long.TYPE":
return ValueType.LONG;
case "java/lang/Float.TYPE":
return ValueType.FLOAT;
case "java/lang/Double.TYPE":
return ValueType.DOUBLE;
case "java/lang/Void.TYPE":
return ValueType.VOID;
default:
return null;
}
}
}