Support @This parameters

This commit is contained in:
Andrey Breslav
2013-05-12 14:52:32 +02:00
parent d99ea2a783
commit 3cd1cf0807
5 changed files with 202 additions and 13 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.asm4.util.Textifier;
import org.jetbrains.asm4.util.TraceMethodVisitor;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -33,6 +34,7 @@ import static org.jetbrains.asm4.Opcodes.*;
public class InterceptionInstrumenter {
private static final Pattern ANYTHING = Pattern.compile(".*");
private static final Type OBJECT_TYPE = Type.getType(Object.class);
private final boolean dumpInstrumentedMethods = false;
@@ -141,13 +143,25 @@ public class InterceptionInstrumenter {
}
private static MethodData getMethodData(FieldData interceptorField, Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int thisParameterIndex = -1;
for (int i = 0; i < parameterAnnotations.length; i++) {
for (Annotation annotation : parameterAnnotations[i]) {
if (annotation instanceof This) {
if (thisParameterIndex > -1) {
throw new IllegalArgumentException("Multiple @This parameters in " + method);
}
thisParameterIndex = i;
}
}
}
return new MethodDataImpl(
interceptorField,
Type.getInternalName(method.getDeclaringClass()),
method.getName(),
Type.getMethodDescriptor(method),
method.getParameterTypes().length
);
method.getParameterTypes().length,
thisParameterIndex);
}
private void addDumpTask(final Object interceptor, final Method method, final MethodInstrumenter instrumenter) {
@@ -251,9 +265,10 @@ public class InterceptionInstrumenter {
mv = getDumpingVisitorWrapper(mv, name, desc);
}
return new MethodVisitor(ASM4, mv) {
return new MethodVisitorWithUniversalHandler(ASM4, mv) {
private InstructionAdapter ia = null;
private boolean enterDataWritten = false;
private InstructionAdapter getInstructionAdapter() {
if (ia == null) {
@@ -269,15 +284,22 @@ public class InterceptionInstrumenter {
}
@Override
public void visitCode() {
protected boolean visitAnyInsn(int opcode) {
writeEnterData();
return true;
}
private void writeEnterData() {
if (enterDataWritten) return;
enterDataWritten = true;
for (MethodData methodData : enterData) {
invokeMethod(access, name, desc, getInstructionAdapter(), methodData);
invokeMethod(access, desc, getInstructionAdapter(), methodData);
}
super.visitCode();
}
@Override
public void visitInsn(int opcode) {
writeEnterData();
switch (opcode) {
case RETURN:
case IRETURN:
@@ -287,7 +309,7 @@ public class InterceptionInstrumenter {
case ARETURN:
case ATHROW:
for (MethodData methodData : exitData) {
invokeMethod(access, name, desc, getInstructionAdapter(), methodData);
invokeMethod(access, desc, getInstructionAdapter(), methodData);
}
break;
}
@@ -326,18 +348,33 @@ public class InterceptionInstrumenter {
return cw.toByteArray();
}
private static void invokeMethod(int access, String name, String desc, InstructionAdapter ia, MethodData methodData) {
private static void invokeMethod(int access, String instrumentedMethodDesc, InstructionAdapter ia, MethodData methodData) {
FieldData field = methodData.getOwnerField();
ia.getstatic(field.getDeclaringClass(), field.getName(), field.getDesc());
ia.checkcast(field.getRuntimeType());
int parameterCount = methodData.getParameterCount();
if (parameterCount > 0) {
org.jetbrains.asm4.commons.Method method = new org.jetbrains.asm4.commons.Method(name, desc);
Type[] parameterTypes = method.getArgumentTypes();
int base = (access & ACC_STATIC) != 0 ? 0 : 1;
Type[] parameterTypes = Type.getArgumentTypes(instrumentedMethodDesc);
boolean isStatic = (access & ACC_STATIC) != 0;
int base = isStatic ? 0 : 1;
int parametersUsed = 0;
for (int i = 0; i < parameterCount; i++) {
ia.load(base + i, parameterTypes[i]);
if (methodData.getThisParameterIndex() == i) {
if (isStatic) {
// static method, 'this' is null
ia.aconst(null);
}
else {
// load 'this'
ia.load(0, OBJECT_TYPE);
}
}
else {
ia.load(base + parametersUsed, parameterTypes[parametersUsed]);
parametersUsed++;
}
}
}
ia.invokevirtual(methodData.getDeclaringClass(), methodData.getName(), methodData.getDesc());
@@ -19,4 +19,7 @@ package org.jetbrains.jet.preloading.instrumentation;
interface MethodData extends MemberData {
FieldData getOwnerField();
int getParameterCount();
// -1 for no @This parameter
int getThisParameterIndex();
}
@@ -19,17 +19,20 @@ package org.jetbrains.jet.preloading.instrumentation;
public class MethodDataImpl extends MemberDataImpl implements MethodData {
private final FieldData ownerField;
private final int parameterCount;
private final int thisParameterIndex;
MethodDataImpl(
FieldData ownerField,
String declaringClass,
String name,
String desc,
int parameterCount
int parameterCount,
int thisParameterIndex
) {
super(declaringClass, name, desc);
this.ownerField = ownerField;
this.parameterCount = parameterCount;
this.thisParameterIndex = thisParameterIndex;
}
@Override
@@ -41,4 +44,9 @@ public class MethodDataImpl extends MemberDataImpl implements MethodData {
public int getParameterCount() {
return parameterCount;
}
@Override
public int getThisParameterIndex() {
return thisParameterIndex;
}
}
@@ -0,0 +1,114 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* 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.jetbrains.jet.preloading.instrumentation;
import org.jetbrains.asm4.Handle;
import org.jetbrains.asm4.Label;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
public class MethodVisitorWithUniversalHandler extends MethodVisitor {
public MethodVisitorWithUniversalHandler(int api) {
super(api);
}
public MethodVisitorWithUniversalHandler(int api, MethodVisitor mv) {
super(api, mv);
}
protected boolean visitAnyInsn(int opcode) {
return true;
}
@Override
public void visitInsn(int opcode) {
if (!visitAnyInsn(opcode)) return;
super.visitInsn(opcode);
}
@Override
public void visitIntInsn(int opcode, int operand) {
if (!visitAnyInsn(opcode)) return;
super.visitIntInsn(opcode, operand);
}
@Override
public void visitVarInsn(int opcode, int var) {
if (!visitAnyInsn(opcode)) return;
super.visitVarInsn(opcode, var);
}
@Override
public void visitTypeInsn(int opcode, String type) {
if (!visitAnyInsn(opcode)) return;
super.visitTypeInsn(opcode, type);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (!visitAnyInsn(opcode)) return;
super.visitFieldInsn(opcode, owner, name, desc);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (!visitAnyInsn(opcode)) return;
super.visitMethodInsn(opcode, owner, name, desc);
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
if (!visitAnyInsn(Opcodes.INVOKEDYNAMIC)) return;
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
@Override
public void visitJumpInsn(int opcode, Label label) {
if (!visitAnyInsn(opcode)) return;
super.visitJumpInsn(opcode, label);
}
@Override
public void visitLdcInsn(Object cst) {
if (!visitAnyInsn(Opcodes.LDC)) return;
super.visitLdcInsn(cst);
}
@Override
public void visitIincInsn(int var, int increment) {
if (!visitAnyInsn(Opcodes.IINC)) return;
super.visitIincInsn(var, increment);
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
if (!visitAnyInsn(Opcodes.TABLESWITCH)) return;
super.visitTableSwitchInsn(min, max, dflt, labels);
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
if (!visitAnyInsn(Opcodes.LOOKUPSWITCH)) return;
super.visitLookupSwitchInsn(dflt, keys, labels);
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
if (!visitAnyInsn(Opcodes.MULTIANEWARRAY)) return;
super.visitMultiANewArrayInsn(desc, dims);
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* 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.jetbrains.jet.preloading.instrumentation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface This {
}