|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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.ConstantDynamic; |
|
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 UNINITIALIZED_THIS = new Object(); |
|
|
|
|
|
private static final Object OTHER = new Object(); |
|
|
|
|
|
private static final String INVALID_OPCODE = "Invalid opcode "; |
|
|
|
|
|
protected int methodAccess; |
|
|
|
|
|
protected String methodDesc; |
|
|
|
|
|
private final boolean isConstructor; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean superClassConstructorCalled; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private List<Object> stackFrame; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Map<Label, List<Object>> forwardJumpStackFrames; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected AdviceAdapter( |
|
final int api, |
|
final MethodVisitor methodVisitor, |
|
final int access, |
|
final String name, |
|
final String descriptor) { |
|
super(api, methodVisitor, access, name, descriptor); |
|
methodAccess = access; |
|
methodDesc = descriptor; |
|
isConstructor = "<init>".equals(name); |
|
} |
|
|
|
@Override |
|
public void visitCode() { |
|
super.visitCode(); |
|
if (isConstructor) { |
|
stackFrame = new ArrayList<>(); |
|
forwardJumpStackFrames = new HashMap<>(); |
|
} else { |
|
onMethodEnter(); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLabel(final Label label) { |
|
super.visitLabel(label); |
|
if (isConstructor && forwardJumpStackFrames != null) { |
|
List<Object> labelStackFrame = forwardJumpStackFrames.get(label); |
|
if (labelStackFrame != null) { |
|
stackFrame = labelStackFrame; |
|
superClassConstructorCalled = false; |
|
forwardJumpStackFrames.remove(label); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInsn(final int opcode) { |
|
if (isConstructor && !superClassConstructorCalled) { |
|
int stackSize; |
|
switch (opcode) { |
|
case IRETURN: |
|
case FRETURN: |
|
case ARETURN: |
|
case LRETURN: |
|
case DRETURN: |
|
throw new IllegalArgumentException("Invalid return in constructor"); |
|
case RETURN: |
|
onMethodExit(opcode); |
|
break; |
|
case ATHROW: |
|
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: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); |
|
break; |
|
case DUP_X2: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); |
|
break; |
|
case DUP2: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); |
|
stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); |
|
break; |
|
case DUP2_X1: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); |
|
stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); |
|
break; |
|
case DUP2_X2: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 4, stackFrame.get(stackSize - 1)); |
|
stackFrame.add(stackSize - 4, stackFrame.get(stackSize - 1)); |
|
break; |
|
case SWAP: |
|
stackSize = stackFrame.size(); |
|
stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); |
|
stackFrame.remove(stackSize); |
|
break; |
|
default: |
|
throw new IllegalArgumentException(INVALID_OPCODE + opcode); |
|
} |
|
} else { |
|
switch (opcode) { |
|
case RETURN: |
|
case IRETURN: |
|
case FRETURN: |
|
case ARETURN: |
|
case LRETURN: |
|
case DRETURN: |
|
case ATHROW: |
|
onMethodExit(opcode); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
super.visitInsn(opcode); |
|
} |
|
|
|
@Override |
|
public void visitVarInsn(final int opcode, final int var) { |
|
super.visitVarInsn(opcode, var); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
switch (opcode) { |
|
case ILOAD: |
|
case FLOAD: |
|
pushValue(OTHER); |
|
break; |
|
case LLOAD: |
|
case DLOAD: |
|
pushValue(OTHER); |
|
pushValue(OTHER); |
|
break; |
|
case ALOAD: |
|
pushValue(var == 0 ? UNINITIALIZED_THIS : OTHER); |
|
break; |
|
case ASTORE: |
|
case ISTORE: |
|
case FSTORE: |
|
popValue(); |
|
break; |
|
case LSTORE: |
|
case DSTORE: |
|
popValue(); |
|
popValue(); |
|
break; |
|
case RET: |
|
break; |
|
default: |
|
throw new IllegalArgumentException(INVALID_OPCODE + opcode); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitFieldInsn( |
|
final int opcode, final String owner, final String name, final String descriptor) { |
|
super.visitFieldInsn(opcode, owner, name, descriptor); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
char firstDescriptorChar = descriptor.charAt(0); |
|
boolean longOrDouble = firstDescriptorChar == 'J' || firstDescriptorChar == 'D'; |
|
switch (opcode) { |
|
case GETSTATIC: |
|
pushValue(OTHER); |
|
if (longOrDouble) { |
|
pushValue(OTHER); |
|
} |
|
break; |
|
case PUTSTATIC: |
|
popValue(); |
|
if (longOrDouble) { |
|
popValue(); |
|
} |
|
break; |
|
case PUTFIELD: |
|
popValue(); |
|
popValue(); |
|
if (longOrDouble) { |
|
popValue(); |
|
} |
|
break; |
|
case GETFIELD: |
|
if (longOrDouble) { |
|
pushValue(OTHER); |
|
} |
|
break; |
|
default: |
|
throw new IllegalArgumentException(INVALID_OPCODE + opcode); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIntInsn(final int opcode, final int operand) { |
|
super.visitIntInsn(opcode, operand); |
|
if (isConstructor && !superClassConstructorCalled && opcode != NEWARRAY) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLdcInsn(final Object value) { |
|
super.visitLdcInsn(value); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
pushValue(OTHER); |
|
if (value instanceof Double |
|
|| value instanceof Long |
|
|| (value instanceof ConstantDynamic && ((ConstantDynamic) value).getSize() == 2)) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { |
|
super.visitMultiANewArrayInsn(descriptor, numDimensions); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
for (int i = 0; i < numDimensions; i++) { |
|
popValue(); |
|
} |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTypeInsn(final int opcode, final String type) { |
|
super.visitTypeInsn(opcode, type); |
|
|
|
if (isConstructor && !superClassConstructorCalled && opcode == NEW) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMethodInsn( |
|
final int opcodeAndSource, |
|
final String owner, |
|
final String name, |
|
final String descriptor, |
|
final boolean isInterface) { |
|
if (api < Opcodes.ASM5 && (opcodeAndSource & Opcodes.SOURCE_DEPRECATED) == 0) { |
|
|
|
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); |
|
return; |
|
} |
|
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface); |
|
int opcode = opcodeAndSource & ~Opcodes.SOURCE_MASK; |
|
|
|
doVisitMethodInsn(opcode, descriptor); |
|
} |
|
|
|
private void doVisitMethodInsn(final int opcode, final String descriptor) { |
|
if (isConstructor && !superClassConstructorCalled) { |
|
for (Type argumentType : Type.getArgumentTypes(descriptor)) { |
|
popValue(); |
|
if (argumentType.getSize() == 2) { |
|
popValue(); |
|
} |
|
} |
|
switch (opcode) { |
|
case INVOKEINTERFACE: |
|
case INVOKEVIRTUAL: |
|
popValue(); |
|
break; |
|
case INVOKESPECIAL: |
|
Object value = popValue(); |
|
if (value == UNINITIALIZED_THIS && !superClassConstructorCalled) { |
|
superClassConstructorCalled = true; |
|
onMethodEnter(); |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
Type returnType = Type.getReturnType(descriptor); |
|
if (returnType != Type.VOID_TYPE) { |
|
pushValue(OTHER); |
|
if (returnType.getSize() == 2) { |
|
pushValue(OTHER); |
|
} |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInvokeDynamicInsn( |
|
final String name, |
|
final String descriptor, |
|
final Handle bootstrapMethodHandle, |
|
final Object... bootstrapMethodArguments) { |
|
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); |
|
doVisitMethodInsn(Opcodes.INVOKEDYNAMIC, descriptor); |
|
} |
|
|
|
@Override |
|
public void visitJumpInsn(final int opcode, final Label label) { |
|
super.visitJumpInsn(opcode, label); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
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; |
|
default: |
|
break; |
|
} |
|
addForwardJump(label); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { |
|
super.visitLookupSwitchInsn(dflt, keys, labels); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
popValue(); |
|
addForwardJumps(dflt, labels); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTableSwitchInsn( |
|
final int min, final int max, final Label dflt, final Label... labels) { |
|
super.visitTableSwitchInsn(min, max, dflt, labels); |
|
if (isConstructor && !superClassConstructorCalled) { |
|
popValue(); |
|
addForwardJumps(dflt, labels); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTryCatchBlock( |
|
final Label start, final Label end, final Label handler, final String type) { |
|
super.visitTryCatchBlock(start, end, handler, type); |
|
// By definition of 'forwardJumpStackFrames', 'handler' should be pushed only if there is an |
|
// instruction between 'start' and 'end' at which the super class constructor is not yet |
|
// called. Unfortunately, try catch blocks must be visited before their labels, so we have no |
|
// way to know this at this point. Instead, we suppose that the super class constructor has not |
|
// been called at the start of *any* exception handler. If this is wrong, normally there should |
|
// not be a second super class constructor call in the exception handler (an object can't be |
|
// initialized twice), so this is not issue (in the sense that there is no risk to emit a wrong |
|
|
|
if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) { |
|
List<Object> handlerStackFrame = new ArrayList<>(); |
|
handlerStackFrame.add(OTHER); |
|
forwardJumpStackFrames.put(handler, handlerStackFrame); |
|
} |
|
} |
|
|
|
private void addForwardJumps(final Label dflt, final Label[] labels) { |
|
addForwardJump(dflt); |
|
for (Label label : labels) { |
|
addForwardJump(label); |
|
} |
|
} |
|
|
|
private void addForwardJump(final Label label) { |
|
if (forwardJumpStackFrames.containsKey(label)) { |
|
return; |
|
} |
|
forwardJumpStackFrames.put(label, new ArrayList<>(stackFrame)); |
|
} |
|
|
|
private Object popValue() { |
|
return stackFrame.remove(stackFrame.size() - 1); |
|
} |
|
|
|
private Object peekValue() { |
|
return stackFrame.get(stackFrame.size() - 1); |
|
} |
|
|
|
private void pushValue(final Object value) { |
|
stackFrame.add(value); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void onMethodEnter() {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void onMethodExit(final int opcode) {} |
|
} |