From 3cd1cf0807e1d03821bf1c6b95ab6d87779aabec Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sun, 12 May 2013 14:52:32 +0200 Subject: [PATCH] Support @This parameters --- .../InterceptionInstrumenter.java | 61 ++++++++-- .../instrumentation/MethodData.java | 3 + .../instrumentation/MethodDataImpl.java | 10 +- .../MethodVisitorWithUniversalHandler.java | 114 ++++++++++++++++++ .../jet/preloading/instrumentation/This.java | 27 +++++ 5 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodVisitorWithUniversalHandler.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/This.java diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java index 3224ab4c8ac..7d4a04a68ea 100644 --- a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java @@ -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()); diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java index fb35f57c9fd..8340bcb05ff 100644 --- a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java @@ -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(); } diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java index 2a3d172e425..5b59c0f2f91 100644 --- a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java @@ -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; + } } diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodVisitorWithUniversalHandler.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodVisitorWithUniversalHandler.java new file mode 100644 index 00000000000..78448ad7f39 --- /dev/null +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodVisitorWithUniversalHandler.java @@ -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); + } +} diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/This.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/This.java new file mode 100644 index 00000000000..67f67c5585d --- /dev/null +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/This.java @@ -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 { +}