下面列出了org.objectweb.asm.Opcodes# DUP 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (checkTargetInsn(opcode, owner, name, desc)) {
markPatchedSuccessfully();
super.visitFieldInsn(Opcodes.PUTSTATIC, TARGET_CLASS_NAME, SpecialArmorClassVisitor.CACHED_TOUGHNESS_FIELD_NAME, "F"); //store armorToughness
super.visitFieldInsn(Opcodes.PUTSTATIC, TARGET_CLASS_NAME, SpecialArmorClassVisitor.CACHED_TOTAL_ARMOR_FIELD_NAME, "F"); //store totalArmor
super.visitInsn(Opcodes.DUP); //duplicate damage
super.visitVarInsn(Opcodes.ALOAD, 0); //load entity
super.visitVarInsn(Opcodes.ALOAD, 1); //load inventory
super.visitVarInsn(Opcodes.ALOAD, 2); //load damageSource
super.visitMethodInsn(Opcodes.INVOKESTATIC, ARMOR_HOOKS_OWNER, ARMOR_HOOKS_METHOD_NAME, ARMOR_HOOKS_SIGNATURE, false); //call ArmorHooks
super.visitFieldInsn(Opcodes.GETSTATIC, TARGET_CLASS_NAME, SpecialArmorClassVisitor.CACHED_TOTAL_ARMOR_FIELD_NAME, "F"); //load totalArmor back
super.visitFieldInsn(Opcodes.GETSTATIC, TARGET_CLASS_NAME, SpecialArmorClassVisitor.CACHED_TOUGHNESS_FIELD_NAME, "F"); //load armorToughness back
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
private void mutatePrimitiveIntegerReturn() {
if (shouldMutate("primitive boolean/byte/short/integer",
"(x == 1) ? 0 : x + 1")) {
final Label label = new Label();
super.visitInsn(Opcodes.DUP);
super.visitInsn(Opcodes.ICONST_1);
super.visitJumpInsn(Opcodes.IF_ICMPEQ, label);
super.visitInsn(Opcodes.ICONST_1);
super.visitInsn(Opcodes.IADD);
super.visitInsn(Opcodes.IRETURN);
super.visitLabel(label);
super.visitInsn(Opcodes.ICONST_0);
super.visitInsn(Opcodes.IRETURN);
}
}
private void addWhereAmI() {
// 0: new #2; //class java/lang/Exception
// 3: dup
// 4: invokespecial #3; //Method java/lang/Exception."<init>":()V
// 7: invokevirtual #4; //Method
// java/lang/Exception.printStackTrace:()V
// 10: return
// super.visitTypeInsn(Opcodes.NEW, type);
String exClass = Type.getInternalName(Exception.class);
super.visitTypeInsn(Opcodes.NEW, exClass);
super.visitInsn(Opcodes.DUP);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, exClass, "<init>",
"()V");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, exClass,
"printStackTrace", "()V");
}
@SuppressWarnings("unused")
protected void onMethodEnter() {
if (done) return;
overridden = true;
Label start = new Label();
Label normal = new Label();
super.visitLabel(start);
super.visitFieldInsn(Opcodes.GETSTATIC, CONFIGURATION, CONFIGURATION_FIELD_NAME, Type.INT_TYPE.getDescriptor());
super.visitInsn(Opcodes.DUP);
super.visitJumpInsn(Opcodes.IFEQ, normal);
super.visitInsn(Opcodes.IRETURN);
super.visitLabel(normal);
super.visitInsn(Opcodes.POP);
Label end = new Label();
super.visitJumpInsn(Opcodes.GOTO, end);
super.visitLabel(end);
super.visitTryCatchBlock(start, normal, end, Type.getType(Throwable.class).getDescriptor());
}
private List<AbstractInsnNode> getPossibleDupPop(AbstractInsnNode ain)
{
AbstractInsnNode next = ain;
List<AbstractInsnNode> instrs = new ArrayList<>();
while(next != null)
{
if(Utils.isInstruction(next) && next.getOpcode() != Opcodes.IINC)
instrs.add(next);
if(instrs.size() >= 3)
break;
next = next.getNext();
}
if(instrs.size() >= 3 && (willPush(instrs.get(0)) || ain.getOpcode() == Opcodes.DUP)
&& (willPush(instrs.get(1)) || instrs.get(1).getOpcode() == Opcodes.DUP)
&& instrs.get(2).getOpcode() == Opcodes.POP2)
return instrs;
else
return null;
}
private List<AbstractInsnNode> getPossibleSwap(AbstractInsnNode ain, int mode)
{
AbstractInsnNode next = ain;
List<AbstractInsnNode> instrs = new ArrayList<>();
while(next != null)
{
if(Utils.isInstruction(next) && next.getOpcode() != Opcodes.IINC)
instrs.add(next);
if(instrs.size() >= (mode == 0 ? 3 : 4))
break;
next = next.getNext();
}
if(mode == 0 && instrs.size() >= 3 && willPush(instrs.get(0)) && willPush(instrs.get(1))
&& instrs.get(2).getOpcode() == Opcodes.SWAP)
return instrs;
else if(mode == 1 && instrs.size() >= 4 && willPush(instrs.get(0))
&& (willPush(instrs.get(1)) || instrs.get(1).getOpcode() == Opcodes.DUP)
&& instrs.get(2).getOpcode() == Opcodes.GETFIELD
&& Type.getType(((FieldInsnNode)instrs.get(2)).desc).getSort() != Type.LONG
&& Type.getType(((FieldInsnNode)instrs.get(2)).desc).getSort() != Type.DOUBLE
&& instrs.get(3).getOpcode() == Opcodes.SWAP)
return instrs;
else
return null;
}
public static int tryReduce(List<Instruction> instructions, int index) {
Instruction instr = instructions.get(index);
if (index < 1 || instr.getOpcode() != Opcodes.DUP) {
return -1;
}
Instruction prev = instructions.get(index-1);
StringBuilder devNull = new StringBuilder();
if (prev.getOpcode() == Opcodes.ALOAD && prev instanceof AssignableExpression && ((AssignableExpression)prev).assignTo(null, devNull)) {
DupExpression dup = new DupExpression();
dup.sourceInstr = prev;
dup.dupInstr = instr;
instructions.remove(index);
instructions.add(index, dup);
return index;
}
return -1;
}
private void buildRecorderFromObject(
int opcode, String owner, String name, String signature, boolean itf) {
super.visitMethodInsn(opcode, owner, name, signature, itf);
// -> stack: ... newobj
super.visitInsn(Opcodes.DUP);
// -> stack: ... newobj newobj
super.visitInsn(Opcodes.DUP);
// -> stack: ... newobj newobj newobj
// We could be instantiating this class or a subclass, so we
// have to get the class the hard way.
super.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
// -> stack: ... newobj newobj Class
super.visitInsn(Opcodes.SWAP);
// -> stack: ... newobj Class newobj
super.visitMethodInsn(
Opcodes.INVOKESTATIC, recorderClass, recorderMethod, CLASS_RECORDER_SIG, false);
// -> stack: ... newobj
}
/**
* Mutates a primitive float return (<code>Opcode.FRETURN</code>). The
* strategy used was translated from jumble BCEL code. The following is
* complicated by the problem of <tt>NaN</tt>s. By default the new value is
* <code>-(x + 1)</code>, but this doesn't work for <tt>NaN</tt>s. But for a
* <tt>NaN</tt> <code>x != x</code> is true, and we use this to detect them.
*
* @see #mutatePrimitiveDoubleReturn()
*/
private void mutatePrimitiveFloatReturn() {
if (shouldMutate("primitive float", "(x != NaN)? -(x + 1) : -1 ")) {
final Label label = new Label();
super.visitInsn(Opcodes.DUP);
super.visitInsn(Opcodes.DUP);
super.visitInsn(Opcodes.FCMPG);
super.visitJumpInsn(Opcodes.IFEQ, label);
super.visitInsn(Opcodes.POP);
super.visitInsn(Opcodes.FCONST_0);
// the following code is executed in NaN case, too
super.visitLabel(label);
super.visitInsn(Opcodes.FCONST_1);
super.visitInsn(Opcodes.FADD);
super.visitInsn(Opcodes.FNEG);
super.visitInsn(Opcodes.FRETURN);
}
}
@Override
public void visitInsn(int opcode)
{
if(methodData.hasHookAt(HookPosition.METHOD_END) && opcode >= 172
&& opcode <= 177)
{
HookData hookData = methodData.getHook(HookPosition.METHOD_END);
super.visitLdcInsn(className + "." + methodName + "|end");
if(hookData.collectsParams())
{
super.visitIntInsn(Opcodes.BIPUSH, paramCount);
super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for(byte i = 0; i < paramCount; i++)
{
super.visitInsn(Opcodes.DUP);
super.visitIntInsn(Opcodes.BIPUSH, i);
super.visitVarInsn(Opcodes.ALOAD, i);
super.visitInsn(Opcodes.AASTORE);
}
}
// TODO: Custom class path
super.visitMethodInsn(Opcodes.INVOKESTATIC,
"tk/wurst_client/hooks/HookManager", "hook",
"(Ljava/lang/String;"
+ (hookData.collectsParams() ? "[Ljava/lang/Object;" : "")
+ ")V", false);
}
super.visitInsn(opcode);
}
private void instrumentToTrackReturn(int opcode) {
if (!VariableType.isReturnOperation(opcode)) {
return;
}
if (Opcodes.RETURN == opcode) {
super.visitLdcInsn(lineNumber);
super.visitLdcInsn(methodName);
super.visitLdcInsn(Type.getType("L" + className + ";"));
super.visitMethodInsn(Opcodes.INVOKESTATIC, instrumentationActions.trackerClass, "trackReturn", "(ILjava/lang/String;Ljava/lang/Class;)V", false);
} else {
if (opcode == Opcodes.DRETURN || opcode == Opcodes.LRETURN) {
super.visitInsn(Opcodes.DUP2);
} else {
super.visitInsn(Opcodes.DUP);
}
final VariableType variableType;
if (opcode == Opcodes.IRETURN) {
variableType = VariableType.getReturnTypeFromMethodDesc(desc);
} else {
variableType = VariableType.getByReturnOpCode(opcode);
}
super.visitLdcInsn(lineNumber);
super.visitLdcInsn(methodName);
super.visitLdcInsn(Type.getType("L" + className + ";"));
super.visitMethodInsn(Opcodes.INVOKESTATIC, instrumentationActions.trackerClass, "trackReturn", "(" + variableType.desc + "ILjava/lang/String;Ljava/lang/Class;)V", false);
}
}
private void addLog(boolean dup, int site) {
if (dup)
super.visitInsn(Opcodes.DUP);
super.visitLdcInsn(new Integer(site));
super.visitMethodInsn(Opcodes.INVOKESTATIC, name,
LOG_INTERNAL_ALLOC_REPORT, OBJECT_SITE_SIGNATURE);
if (!constructor)
super.visitMethodInsn(Opcodes.INVOKESTATIC, name,
LOG_INTERNAL_ALLOC_DONE, VOID_SIGNATURE);
}
@Override
public void visitInsn(int opcode) {
// check the last object allocation we encountered, if this is in the same package
// we need to go through reflection and should therefore remove the DUP, otherwise
// we leave it.
if (opcode == Opcodes.DUP && state == MachineState.AFTER_NEW) {
state = MachineState.NORMAL;
return;
}
super.visitInsn(opcode);
}
@Override
public void visitEnd() {
if (!hasCode) {
super.visitTypeInsn(Opcodes.NEW, EXCEPTION_INTERNAL_NAME);
super.visitInsn(Opcodes.DUP);
super.visitMethodInsn(
Opcodes.INVOKESPECIAL, EXCEPTION_INTERNAL_NAME, "<init>", "()V", /*itf*/ false);
super.visitInsn(Opcodes.ATHROW);
super.visitMaxs(0, 0); // triggers computation of the actual max's
}
super.visitEnd();
}
@Override
public void visitCode()
{
super.visitCode();
if(methodData.hasHookAt(HookPosition.METHOD_START))
{
HookData hookData = methodData.getHook(HookPosition.METHOD_START);
super.visitLdcInsn(className + "." + methodName + "|start");
if(hookData.collectsParams())
{
super.visitIntInsn(Opcodes.BIPUSH, paramCount);
super.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
for(byte i = 0; i < paramCount; i++)
{
super.visitInsn(Opcodes.DUP);
super.visitIntInsn(Opcodes.BIPUSH, i);
super.visitVarInsn(Opcodes.ALOAD, i);
super.visitInsn(Opcodes.AASTORE);
}
}
// TODO: Custom class path
super.visitMethodInsn(Opcodes.INVOKESTATIC,
"tk/wurst_client/hooks/HookManager", "hook",
"(Ljava/lang/String;"
+ (hookData.collectsParams() ? "[Ljava/lang/Object;" : "")
+ ")V", false);
}
}
private void pushClassNameOnStack() {
super.visitInsn(Opcodes.DUP);
// -> stack: ... class class
super.visitMethodInsn(
Opcodes.INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
// -> stack: ... class classNameDotted
}
/**
* NOTE: All calls to instruction visitation routines are made against super, directly, since we do frame offset accounting within our overrides
* and that offset only applies to incoming bytecodes, not outgoing ones.
*
* @param opcode The opcode.
* @param descriptor The type descriptor of the field to which the opcode is applied.
*/
private void checkInjectLazyLoad(int opcode, String descriptor) {
// If this is a PUTFIELD or GETFIELD, we want to call "lazyLoad()":
// -PUTIFELD: DUP2, POP, INVOKEVIRTUAL
// -GETIFELD: DUP, INVOKEVIRTUAL
if ((Opcodes.PUTFIELD == opcode) && ((null == this.tracker) || !this.tracker.isThisTargetOfPut(this.frameOffset))) {
// We need to see how big this type is since double and long need a far more complex dance.
if ((1 == descriptor.length()) && ((DescriptorParser.LONG == descriptor.charAt(0)) || (DescriptorParser.DOUBLE == descriptor.charAt(0)))) {
// Here, the stack looks like: ... OBJECT, VAR1, VAR2 (top)
// Where we need: ... OBJECT, VAR1, VAR2, OBJECT (top)
// This is multiple stages:
// DUP2_X1: ... VAR1, VAR2, OBJECT, VAR1, VAR2 (top)
super.visitInsn(Opcodes.DUP2_X1);
// POP2: ... VAR1, VAR2, OBJECT (top)
super.visitInsn(Opcodes.POP2);
// DUP: ... VAR1, VAR2, OBJECT, OBJECT (top)
super.visitInsn(Opcodes.DUP);
// INOKE: ... VAR1, VAR2, OBJECT (top)
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SHADOW_OBJECT_NAME, LAZY_LOAD_NAME, LAZY_LOAD_DESCRIPTOR, false);
// DUP_X2: ... OBJECT, VAR1, VAR2, OBJECT (top)
super.visitInsn(Opcodes.DUP_X2);
// POP: ... OBJECT, VAR1, VAR2 (top)
super.visitInsn(Opcodes.POP);
} else {
// Here, the stack looks like: ... OBJECT, VAR, (top)
// Where we need: ... OBJECT, VAR, OBJECT (top)
// Stages:
// DUP2: ... OBJECT, VAR, OBJECT, VAR (top)
super.visitInsn(Opcodes.DUP2);
// POP: ... OBJECT, VAR, OBJECT (top)
super.visitInsn(Opcodes.POP);
// INOKE: ... OBJECT, VAR (top)
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SHADOW_OBJECT_NAME, LAZY_LOAD_NAME, LAZY_LOAD_DESCRIPTOR, false);
}
} else if ((Opcodes.GETFIELD == opcode) && ((null == this.tracker) || !this.tracker.isThisTargetOfGet(this.frameOffset))) {
// Here, the stack looks like: ... OBJECT, (top)
// Where we need: ... OBJECT, OBJECT (top)
super.visitInsn(Opcodes.DUP);
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SHADOW_OBJECT_NAME, LAZY_LOAD_NAME, LAZY_LOAD_DESCRIPTOR, false);
}
}
void calculateArrayLengthAndDispatch(String typeName, int dimCount) {
// Since the dimensions of the array are not known at instrumentation
// time, we take the created multi-dimensional array and peel off nesting
// levels from the left. For each nesting layer we probe the array length
// and accumulate a partial product which we can then feed the recording
// function.
// below we note the partial product of dimensions 1 to X-1 as productToX
// (so productTo1 == 1 == no dimensions yet). We denote by aref0 the
// array reference at the current nesting level (the containing aref's [0]
// element). If we hit a level whose arraylength is 0 or whose
// reference is null, there's no point continuing, so we shortcut
// out.
// This approach works pretty well when you create a new array with the
// newarray bytecodes. You can also create a new array by cloning an
// existing array; an existing multidimensional array might have had some
// of its [0] elements nulled out. We currently deal with this by bailing
// out, but arguably we should do something more principled (like calculate
// the size of the multidimensional array from scratch if you are using
// clone()).
// TODO(java-platform-team): Do something about modified multidimensional
// arrays and clone().
Label zeroDimension = new Label();
super.visitInsn(Opcodes.DUP); // -> stack: ... origaref aref0
super.visitLdcInsn(1); // -> stack: ... origaref aref0 productTo1
for (int i = 0; i < dimCount; ++i) {
// pre: stack: ... origaref aref0 productToI
super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref productToI aref
super.visitInsn(Opcodes.DUP);
Label nonNullDimension = new Label();
// -> stack: ... origaref productToI aref aref
super.visitJumpInsn(Opcodes.IFNONNULL, nonNullDimension);
// -> stack: ... origaref productToI aref
super.visitInsn(Opcodes.SWAP);
// -> stack: ... origaref aref productToI
super.visitJumpInsn(Opcodes.GOTO, zeroDimension);
super.visitLabel(nonNullDimension);
// -> stack: ... origaref productToI aref
super.visitInsn(Opcodes.DUP_X1);
// -> stack: ... origaref aref0 productToI aref
super.visitInsn(Opcodes.ARRAYLENGTH);
// -> stack: ... origaref aref0 productToI dimI
Label nonZeroDimension = new Label();
super.visitInsn(Opcodes.DUP);
// -> stack: ... origaref aref0 productToI dimI dimI
super.visitJumpInsn(Opcodes.IFNE, nonZeroDimension);
// -> stack: ... origaref aref0 productToI dimI
super.visitInsn(Opcodes.POP);
// -> stack: ... origaref aref0 productToI
super.visitJumpInsn(Opcodes.GOTO, zeroDimension);
super.visitLabel(nonZeroDimension);
// -> stack: ... origaref aref0 productToI max(dimI,1)
super.visitInsn(Opcodes.IMUL);
// -> stack: ... origaref aref0 productTo{I+1}
if (i < dimCount - 1) {
super.visitInsn(Opcodes.SWAP);
// -> stack: ... origaref productTo{I+1} aref0
super.visitInsn(Opcodes.ICONST_0);
// -> stack: ... origaref productTo{I+1} aref0 0
super.visitInsn(Opcodes.AALOAD);
// -> stack: ... origaref productTo{I+1} aref0'
super.visitInsn(Opcodes.SWAP);
}
// post: stack: ... origaref aref0 productTo{I+1}
}
super.visitLabel(zeroDimension);
super.visitInsn(Opcodes.SWAP); // -> stack: ... origaref product aref0
super.visitInsn(Opcodes.POP); // -> stack: ... origaref product
super.visitInsn(Opcodes.SWAP); // -> stack: ... product origaref
invokeRecordAllocation(typeName);
}
public void visitFieldInsn(int opcode, String owner, String fieldName,
String desc) {
if (firstInstruction)
addInc();
if (logPointerChange && opcode == Opcodes.PUTFIELD
&& desc.charAt(0) == 'L') {
if (constructor && !doneSuperConstructor && name.equals(owner)
&& finalFields.contains(fieldName))
delayedFieldPointer.put(fieldName, desc);
else {
// instrument reference changes from
// putfield ...,obj,v' => ...
// to
// dup2 ...,obj,v' => ...,obj,v',obj,v'
// swap ...,obj,v',obj,v' => ...,obj,v',v',obj
// dup ...,obj,v',v',obj => ...,obj,v',v',obj,obj
// getfield ...,obj,v',v',obj,obj => ...,obj,v',v',obj,v
// invokespecial
// pointerchangelog(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
// ...,obj,v',v',obj,v => ...,obj,v'
// putfield ...,obj,v' =>
super.visitInsn(Opcodes.DUP2);
super.visitInsn(Opcodes.SWAP);
super.visitInsn(Opcodes.DUP);
super.visitFieldInsn(Opcodes.GETFIELD, owner, fieldName,
desc);
super.visitMethodInsn(Opcodes.INVOKESTATIC, name,
LOG_INTERNAL_POINTER_CHANGE,
POINTER_CHANGE_SIGNATURE);
}
} else if (logPointerChange && opcode == Opcodes.PUTSTATIC
&& desc.charAt(0) == 'L') {
// if (finalFields.contains(fieldName)) {
// // assume field is initially null
// super.visitInsn(Opcodes.DUP);
// } else {
// instrument reference changes from
// putstatic ...,v' => ...
// to
// dup ...,v' => ...,v',v'
// ldc owner.class ...,v',v' => ...,v',v',k
// getstatic ...,v',v',k => ...,v',v',k,v
// invokespecial
// staticpointerchangelog(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Object;)V
// ...,v',v',k,v => ...,v'
super.visitInsn(Opcodes.DUP);
super.visitLdcInsn(Type.getObjectType(owner));
super.visitFieldInsn(Opcodes.GETSTATIC, owner, fieldName, desc);
super.visitMethodInsn(Opcodes.INVOKESTATIC, name,
LOG_INTERNAL_STATIC_POINTER_CHANGE,
STATIC_POINTER_CHANGE_SIGNATURE);
// }
}
super.visitFieldInsn(opcode, owner, fieldName, desc);
}
public InsnNode() {
super(Opcodes.DUP);
}