|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package jdk.internal.org.objectweb.asm.commons; |
|
|
|
import java.util.ArrayList; |
|
import java.util.HashMap; |
|
import java.util.List; |
|
import java.util.Map; |
|
|
|
import jdk.internal.org.objectweb.asm.Handle; |
|
import jdk.internal.org.objectweb.asm.Label; |
|
import jdk.internal.org.objectweb.asm.MethodVisitor; |
|
import jdk.internal.org.objectweb.asm.Opcodes; |
|
import jdk.internal.org.objectweb.asm.Type; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { |
|
|
|
private static final Object THIS = new Object(); |
|
|
|
private static final Object OTHER = new Object(); |
|
|
|
protected int methodAccess; |
|
|
|
protected String methodDesc; |
|
|
|
private boolean constructor; |
|
|
|
private boolean superInitialized; |
|
|
|
private List<Object> stackFrame; |
|
|
|
private Map<Label, List<Object>> branches; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected AdviceAdapter(final int api, final MethodVisitor mv, |
|
final int access, final String name, final String desc) { |
|
super(api, mv, access, name, desc); |
|
methodAccess = access; |
|
methodDesc = desc; |
|
constructor = "<init>".equals(name); |
|
} |
|
|
|
@Override |
|
public void visitCode() { |
|
mv.visitCode(); |
|
if (constructor) { |
|
stackFrame = new ArrayList<Object>(); |
|
branches = new HashMap<Label, List<Object>>(); |
|
} else { |
|
superInitialized = true; |
|
onMethodEnter(); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLabel(final Label label) { |
|
mv.visitLabel(label); |
|
if (constructor && branches != null) { |
|
List<Object> frame = branches.get(label); |
|
if (frame != null) { |
|
stackFrame = frame; |
|
branches.remove(label); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInsn(final int opcode) { |
|
if (constructor) { |
|
int s; |
|
switch (opcode) { |
|
case RETURN: |
|
onMethodExit(opcode); |
|
break; |
|
case IRETURN: |
|
case FRETURN: |
|
case ARETURN: |
|
case ATHROW: |
|
popValue(); |
|
onMethodExit(opcode); |
|
break; |
|
case LRETURN: |
|
case DRETURN: |
|
popValue(); |
|
popValue(); |
|
onMethodExit(opcode); |
|
break; |
|
case NOP: |
|
case LALOAD: |
|
case DALOAD: |
|
case LNEG: |
|
case DNEG: |
|
case FNEG: |
|
case INEG: |
|
case L2D: |
|
case D2L: |
|
case F2I: |
|
case I2B: |
|
case I2C: |
|
case I2S: |
|
case I2F: |
|
case ARRAYLENGTH: |
|
break; |
|
case ACONST_NULL: |
|
case ICONST_M1: |
|
case ICONST_0: |
|
case ICONST_1: |
|
case ICONST_2: |
|
case ICONST_3: |
|
case ICONST_4: |
|
case ICONST_5: |
|
case FCONST_0: |
|
case FCONST_1: |
|
case FCONST_2: |
|
case F2L: |
|
case F2D: |
|
case I2L: |
|
case I2D: |
|
pushValue(OTHER); |
|
break; |
|
case LCONST_0: |
|
case LCONST_1: |
|
case DCONST_0: |
|
case DCONST_1: |
|
pushValue(OTHER); |
|
pushValue(OTHER); |
|
break; |
|
case IALOAD: |
|
case FALOAD: |
|
case AALOAD: |
|
case BALOAD: |
|
case CALOAD: |
|
case SALOAD: |
|
case POP: |
|
case IADD: |
|
case FADD: |
|
case ISUB: |
|
case LSHL: |
|
case LSHR: |
|
case LUSHR: |
|
case L2I: |
|
case L2F: |
|
case D2I: |
|
case D2F: |
|
case FSUB: |
|
case FMUL: |
|
case FDIV: |
|
case FREM: |
|
case FCMPL: |
|
case FCMPG: |
|
case IMUL: |
|
case IDIV: |
|
case IREM: |
|
case ISHL: |
|
case ISHR: |
|
case IUSHR: |
|
case IAND: |
|
case IOR: |
|
case IXOR: |
|
case MONITORENTER: |
|
case MONITOREXIT: |
|
popValue(); |
|
break; |
|
case POP2: |
|
case LSUB: |
|
case LMUL: |
|
case LDIV: |
|
case LREM: |
|
case LADD: |
|
case LAND: |
|
case LOR: |
|
case LXOR: |
|
case DADD: |
|
case DMUL: |
|
case DSUB: |
|
case DDIV: |
|
case DREM: |
|
popValue(); |
|
popValue(); |
|
break; |
|
case IASTORE: |
|
case FASTORE: |
|
case AASTORE: |
|
case BASTORE: |
|
case CASTORE: |
|
case SASTORE: |
|
case LCMP: |
|
case DCMPL: |
|
case DCMPG: |
|
popValue(); |
|
popValue(); |
|
popValue(); |
|
break; |
|
case LASTORE: |
|
case DASTORE: |
|
popValue(); |
|
popValue(); |
|
popValue(); |
|
popValue(); |
|
break; |
|
case DUP: |
|
pushValue(peekValue()); |
|
break; |
|
case DUP_X1: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 2, stackFrame.get(s - 1)); |
|
break; |
|
case DUP_X2: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 3, stackFrame.get(s - 1)); |
|
break; |
|
case DUP2: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 2, stackFrame.get(s - 1)); |
|
stackFrame.add(s - 2, stackFrame.get(s - 1)); |
|
break; |
|
case DUP2_X1: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 3, stackFrame.get(s - 1)); |
|
stackFrame.add(s - 3, stackFrame.get(s - 1)); |
|
break; |
|
case DUP2_X2: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 4, stackFrame.get(s - 1)); |
|
stackFrame.add(s - 4, stackFrame.get(s - 1)); |
|
break; |
|
case SWAP: |
|
s = stackFrame.size(); |
|
stackFrame.add(s - 2, stackFrame.get(s - 1)); |
|
stackFrame.remove(s); |
|
break; |
|
} |
|
} else { |
|
switch (opcode) { |
|
case RETURN: |
|
case IRETURN: |
|
case FRETURN: |
|
case ARETURN: |
|
case LRETURN: |
|
case DRETURN: |
|
case ATHROW: |
|
onMethodExit(opcode); |
|
break; |
|
} |
|
} |
|
mv.visitInsn(opcode); |
|
} |
|
|
|
@Override |
|
public void visitVarInsn(final int opcode, final int var) { |
|
super.visitVarInsn(opcode, var); |
|
if (constructor) { |
|
switch (opcode) { |
|
case ILOAD: |
|
case FLOAD: |
|
pushValue(OTHER); |
|
break; |
|
case LLOAD: |
|
case DLOAD: |
|
pushValue(OTHER); |
|
pushValue(OTHER); |
|
break; |
|
case ALOAD: |
|
pushValue(var == 0 ? THIS : OTHER); |
|
break; |
|
case ASTORE: |
|
case ISTORE: |
|
case FSTORE: |
|
popValue(); |
|
break; |
|
case LSTORE: |
|
case DSTORE: |
|
popValue(); |
|
popValue(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitFieldInsn(final int opcode, final String owner, |
|
final String name, final String desc) { |
|
mv.visitFieldInsn(opcode, owner, name, desc); |
|
if (constructor) { |
|
char c = desc.charAt(0); |
|
boolean longOrDouble = c == 'J' || c == 'D'; |
|
switch (opcode) { |
|
case GETSTATIC: |
|
pushValue(OTHER); |
|
if (longOrDouble) { |
|
pushValue(OTHER); |
|
} |
|
break; |
|
case PUTSTATIC: |
|
popValue(); |
|
if (longOrDouble) { |
|
popValue(); |
|
} |
|
break; |
|
case PUTFIELD: |
|
popValue(); |
|
if (longOrDouble) { |
|
popValue(); |
|
popValue(); |
|
} |
|
break; |
|
|
|
default: |
|
if (longOrDouble) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIntInsn(final int opcode, final int operand) { |
|
mv.visitIntInsn(opcode, operand); |
|
if (constructor && opcode != NEWARRAY) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLdcInsn(final Object cst) { |
|
mv.visitLdcInsn(cst); |
|
if (constructor) { |
|
pushValue(OTHER); |
|
if (cst instanceof Double || cst instanceof Long) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMultiANewArrayInsn(final String desc, final int dims) { |
|
mv.visitMultiANewArrayInsn(desc, dims); |
|
if (constructor) { |
|
for (int i = 0; i < dims; i++) { |
|
popValue(); |
|
} |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTypeInsn(final int opcode, final String type) { |
|
mv.visitTypeInsn(opcode, type); |
|
|
|
if (constructor && opcode == NEW) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Deprecated |
|
@Override |
|
public void visitMethodInsn(final int opcode, final String owner, |
|
final String name, final String desc) { |
|
if (api >= Opcodes.ASM5) { |
|
super.visitMethodInsn(opcode, owner, name, desc); |
|
return; |
|
} |
|
doVisitMethodInsn(opcode, owner, name, desc, |
|
opcode == Opcodes.INVOKEINTERFACE); |
|
} |
|
|
|
@Override |
|
public void visitMethodInsn(final int opcode, final String owner, |
|
final String name, final String desc, final boolean itf) { |
|
if (api < Opcodes.ASM5) { |
|
super.visitMethodInsn(opcode, owner, name, desc, itf); |
|
return; |
|
} |
|
doVisitMethodInsn(opcode, owner, name, desc, itf); |
|
} |
|
|
|
private void doVisitMethodInsn(int opcode, final String owner, |
|
final String name, final String desc, final boolean itf) { |
|
mv.visitMethodInsn(opcode, owner, name, desc, itf); |
|
if (constructor) { |
|
Type[] types = Type.getArgumentTypes(desc); |
|
for (int i = 0; i < types.length; i++) { |
|
popValue(); |
|
if (types[i].getSize() == 2) { |
|
popValue(); |
|
} |
|
} |
|
switch (opcode) { |
|
// case INVOKESTATIC: |
|
|
|
case INVOKEINTERFACE: |
|
case INVOKEVIRTUAL: |
|
popValue(); |
|
break; |
|
case INVOKESPECIAL: |
|
Object type = popValue(); |
|
if (type == THIS && !superInitialized) { |
|
onMethodEnter(); |
|
superInitialized = true; |
|
// once super has been initialized it is no longer |
|
|
|
constructor = false; |
|
} |
|
break; |
|
} |
|
|
|
Type returnType = Type.getReturnType(desc); |
|
if (returnType != Type.VOID_TYPE) { |
|
pushValue(OTHER); |
|
if (returnType.getSize() == 2) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, |
|
Object... bsmArgs) { |
|
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); |
|
if (constructor) { |
|
Type[] types = Type.getArgumentTypes(desc); |
|
for (int i = 0; i < types.length; i++) { |
|
popValue(); |
|
if (types[i].getSize() == 2) { |
|
popValue(); |
|
} |
|
} |
|
|
|
Type returnType = Type.getReturnType(desc); |
|
if (returnType != Type.VOID_TYPE) { |
|
pushValue(OTHER); |
|
if (returnType.getSize() == 2) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitJumpInsn(final int opcode, final Label label) { |
|
mv.visitJumpInsn(opcode, label); |
|
if (constructor) { |
|
switch (opcode) { |
|
case IFEQ: |
|
case IFNE: |
|
case IFLT: |
|
case IFGE: |
|
case IFGT: |
|
case IFLE: |
|
case IFNULL: |
|
case IFNONNULL: |
|
popValue(); |
|
break; |
|
case IF_ICMPEQ: |
|
case IF_ICMPNE: |
|
case IF_ICMPLT: |
|
case IF_ICMPGE: |
|
case IF_ICMPGT: |
|
case IF_ICMPLE: |
|
case IF_ACMPEQ: |
|
case IF_ACMPNE: |
|
popValue(); |
|
popValue(); |
|
break; |
|
case JSR: |
|
pushValue(OTHER); |
|
break; |
|
} |
|
addBranch(label); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, |
|
final Label[] labels) { |
|
mv.visitLookupSwitchInsn(dflt, keys, labels); |
|
if (constructor) { |
|
popValue(); |
|
addBranches(dflt, labels); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTableSwitchInsn(final int min, final int max, |
|
final Label dflt, final Label... labels) { |
|
mv.visitTableSwitchInsn(min, max, dflt, labels); |
|
if (constructor) { |
|
popValue(); |
|
addBranches(dflt, labels); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTryCatchBlock(Label start, Label end, Label handler, |
|
String type) { |
|
super.visitTryCatchBlock(start, end, handler, type); |
|
if (constructor && !branches.containsKey(handler)) { |
|
List<Object> stackFrame = new ArrayList<Object>(); |
|
stackFrame.add(OTHER); |
|
branches.put(handler, stackFrame); |
|
} |
|
} |
|
|
|
private void addBranches(final Label dflt, final Label[] labels) { |
|
addBranch(dflt); |
|
for (int i = 0; i < labels.length; i++) { |
|
addBranch(labels[i]); |
|
} |
|
} |
|
|
|
private void addBranch(final Label label) { |
|
if (branches.containsKey(label)) { |
|
return; |
|
} |
|
branches.put(label, new ArrayList<Object>(stackFrame)); |
|
} |
|
|
|
private Object popValue() { |
|
return stackFrame.remove(stackFrame.size() - 1); |
|
} |
|
|
|
private Object peekValue() { |
|
return stackFrame.get(stackFrame.size() - 1); |
|
} |
|
|
|
private void pushValue(final Object o) { |
|
stackFrame.add(o); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void onMethodEnter() { |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void onMethodExit(int opcode) { |
|
} |
|
|
|
// TODO onException, onMethodCall |
|
} |