in TransformCore/src/main/java/com/facebook/ads/injkit/benchmark/BenchmarkInjector.java [294:461]
private MethodNode createForwardMethod(
String name, ClassNode owner, MethodNode destination, BenchmarkMetrics metrics) {
MethodNode newMethod =
new MethodNode(
destination.access,
name,
destination.desc,
destination.signature,
toArray(destination.exceptions));
InsnList instructions = new InsnList();
// Get current nano time and save because we may have an exception.
int tempVar =
Type.getArgumentTypes(newMethod.desc).length
+ ((destination.access & Opcodes.ACC_STATIC) == 0 ? 1 : 0);
instructions.add(
new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false));
instructions.add(new VarInsnNode(Opcodes.LSTORE, tempVar));
// Mark method call start.
LabelNode methodStartLabel = new LabelNode(new Label());
instructions.add(methodStartLabel);
// Call the forwarded method.
Type[] arguments = Type.getArgumentTypes(destination.desc);
int varPos = 0;
if ((destination.access & Opcodes.ACC_STATIC) == 0) {
varPos++;
// Load "this".
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
for (Type argument : arguments) {
instructions.add(new VarInsnNode(argument.getOpcode(Opcodes.ILOAD), varPos));
varPos += argument.getSize();
}
if ((destination.access & Opcodes.ACC_STATIC) != 0) {
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESTATIC, owner.name, destination.name, destination.desc, false));
} else {
instructions.add(
new MethodInsnNode(
Opcodes.INVOKEVIRTUAL, owner.name, destination.name, destination.desc, false));
}
LabelNode methodCallEndLabel = new LabelNode(new Label());
instructions.add(methodCallEndLabel);
// At this point stack contains the return value. Add the parameters to notify. These will
// be the same for all method callbacks.
instructions.add(new LdcInsnNode(owner.name.replace('/', '.')));
instructions.add(new LdcInsnNode(name));
instructions.add(new LdcInsnNode(newMethod.desc));
// Compute time difference in millis.
instructions.add(new VarInsnNode(Opcodes.LLOAD, tempVar));
instructions.add(
new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false));
instructions.add(new InsnNode(Opcodes.LSUB));
instructions.add(new InsnNode(Opcodes.LNEG));
// Handle failure.
LabelNode failureHandler = new LabelNode();
long failNanos = ((long) metrics.failAtMillis) * 1_000_000L;
instructions.add(new InsnNode(Opcodes.DUP2));
instructions.add(new LdcInsnNode(failNanos));
instructions.add(new InsnNode(Opcodes.LCMP));
instructions.add(new JumpInsnNode(Opcodes.IFGE, failureHandler));
// Handle warning.
LabelNode warningHandler = new LabelNode();
long warnNanos = ((long) metrics.warnAtMillis) * 1_000_000L;
instructions.add(new InsnNode(Opcodes.DUP2));
instructions.add(new LdcInsnNode(warnNanos));
instructions.add(new InsnNode(Opcodes.LCMP));
instructions.add(new JumpInsnNode(Opcodes.IFGE, warningHandler));
// Otherwise compute text.
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESTATIC,
benchmarkReceiverClassIName,
EXECUTED_SUCCESS_METHOD_NAME,
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V",
false));
// Return the return value, if any.
LabelNode endNode = new LabelNode();
instructions.add(endNode);
instructions.add(new InsnNode(Type.getReturnType(destination.desc).getOpcode(Opcodes.IRETURN)));
// Failure handler.
instructions.add(failureHandler);
instructions.add(new LdcInsnNode(failNanos));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESTATIC,
benchmarkReceiverClassIName,
EXECUTED_FAILED_METHOD_NAME,
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJ)V",
false));
instructions.add(new JumpInsnNode(Opcodes.GOTO, endNode));
// Warning handler.
instructions.add(warningHandler);
instructions.add(new LdcInsnNode(warnNanos));
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESTATIC,
benchmarkReceiverClassIName,
EXECUTED_WARNING_METHOD_NAME,
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJ)V",
false));
instructions.add(new JumpInsnNode(Opcodes.GOTO, endNode));
// Add catch handler.
LabelNode catchLabel = new LabelNode(new Label());
instructions.add(catchLabel);
// Stack contains [exception]. Add parameters before exception, but keep a copy of the
// exception on the bottom of the stack for later rethrowing.
instructions.add(new InsnNode(Opcodes.DUP));
instructions.add(new LdcInsnNode(owner.name.replace('/', '.')));
instructions.add(new InsnNode(Opcodes.SWAP));
instructions.add(new LdcInsnNode(name));
instructions.add(new InsnNode(Opcodes.SWAP));
instructions.add(new LdcInsnNode(newMethod.desc));
instructions.add(new InsnNode(Opcodes.SWAP));
// Compute time difference in millis.
instructions.add(new VarInsnNode(Opcodes.LLOAD, tempVar));
instructions.add(
new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false));
instructions.add(new InsnNode(Opcodes.LSUB));
instructions.add(new InsnNode(Opcodes.LNEG));
// Call the method to report the failure.
instructions.add(
new MethodInsnNode(
Opcodes.INVOKESTATIC,
benchmarkReceiverClassIName,
EXECUTED_THROWN_METHOD_NAME,
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;J)V",
false));
// Rethrow the exception.
instructions.add(new InsnNode(Opcodes.ATHROW));
// Mark the end of the catch block.
LabelNode endCatchLabel = new LabelNode(new Label());
instructions.add(endCatchLabel);
if (newMethod.tryCatchBlocks == null) {
newMethod.tryCatchBlocks = new ArrayList<>();
}
newMethod.tryCatchBlocks.add(
new TryCatchBlockNode(
methodStartLabel, methodCallEndLabel, catchLabel, "java/lang/Throwable"));
newMethod.instructions = instructions;
return newMethod;
}