diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/AllArgs.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/AllArgs.java new file mode 100644 index 00000000000..134da385b23 --- /dev/null +++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/AllArgs.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 AllArgs { +} 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 3edf226f608..780613d264a 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 @@ -147,6 +147,7 @@ public class InterceptionInstrumenter { int thisParameterIndex = -1; int methodNameParameterIndex = -1; int methodDescParameterIndex = -1; + int allArgsParameterIndex = -1; for (int i = 0; i < parameterAnnotations.length; i++) { for (Annotation annotation : parameterAnnotations[i]) { if (annotation instanceof This) { @@ -167,6 +168,12 @@ public class InterceptionInstrumenter { } methodDescParameterIndex = i; } + else if (annotation instanceof AllArgs) { + if (allArgsParameterIndex > -1) { + throw new IllegalArgumentException("Multiple @AllArgs parameters in " + method); + } + allArgsParameterIndex = i; + } } } return new MethodDataImpl( @@ -177,8 +184,8 @@ public class InterceptionInstrumenter { method.getParameterTypes().length, thisParameterIndex, methodNameParameterIndex, - methodDescParameterIndex - ); + methodDescParameterIndex, + allArgsParameterIndex); } private void addDumpTask(final Object interceptor, final Method method, final MethodInstrumenter instrumenter) { @@ -275,15 +282,15 @@ public class InterceptionInstrumenter { int maxParamCount = 0; for (MethodInstrumenter instrumenter : applicableInstrumenters) { for (MethodData methodData : instrumenter.getEnterData()) { - if (maxParamCount < methodData.getParameterCount()) { - maxParamCount = methodData.getParameterCount(); + if (maxParamCount < stackDepth(methodData)) { + maxParamCount = stackDepth(methodData); } enterData.add(methodData); } for (MethodData methodData : instrumenter.getExitData()) { - if (maxParamCount < methodData.getParameterCount()) { - maxParamCount = methodData.getParameterCount(); + if (maxParamCount < stackDepth(methodData)) { + maxParamCount = stackDepth(methodData); } exitData.add(methodData); } @@ -310,7 +317,7 @@ public class InterceptionInstrumenter { @Override public void visitMaxs(int maxStack, int maxLocals) { - int maxInstrumentedStack = finalMaxParamCount + 2; // +1 for receiver +1 for returned value on the stack + int maxInstrumentedStack = finalMaxParamCount + 1; // +1 for returned value on the stack super.visitMaxs(Math.max(maxStack, maxInstrumentedStack), maxLocals); } @@ -349,6 +356,12 @@ public class InterceptionInstrumenter { }; } + private int stackDepth(MethodData methodData) { + // array + index + value + int allArgsStackDepth = methodData.getAllArgsParameterIndex() >= 0 ? 3 : 0; + return methodData.getParameterCount() + 1 /*receiver*/ + allArgsStackDepth; + } + private void checkMultipleMatches(MethodInstrumenter instrumenter, String name, String desc) { if (!instrumenter.allowsMultipleMatches()) { String erasedSignature = name + desc; @@ -416,6 +429,20 @@ public class InterceptionInstrumenter { else if (i == methodData.getMethodDescParameterIndex()) { ia.aconst(instrumentedMethodDesc); } + else if (i == methodData.getAllArgsParameterIndex()) { + ia.aconst(parameterTypes.length); + ia.newarray(OBJECT_TYPE); + int offset = 0; + for (int parameterIndex = 0; parameterIndex < parameterTypes.length; parameterIndex++) { + ia.dup(); + Type type = parameterTypes[parameterIndex]; + ia.iconst(parameterIndex); + ia.load(base + offset, type); + offset += type.getSize(); + applyBoxingIfNeeded(ia, type); + ia.astore(OBJECT_TYPE); + } + } else { Type type = parameterTypes[parameterOffset]; ia.load(base + parameterOffset, type); @@ -427,6 +454,42 @@ public class InterceptionInstrumenter { ia.invokevirtual(methodData.getDeclaringClass(), methodData.getName(), methodData.getDesc()); } + private static void applyBoxingIfNeeded(InstructionAdapter ia, Type type) { + switch (type.getSort()) { + case Type.BOOLEAN: + box(ia, type, Boolean.class); + break; + case Type.CHAR: + box(ia, type, Character.class); + break; + case Type.BYTE: + box(ia, type, Byte.class); + break; + case Type.SHORT: + box(ia, type, Short.class); + break; + case Type.INT: + box(ia, type, Integer.class); + break; + case Type.FLOAT: + box(ia, type, Float.class); + break; + case Type.LONG: + box(ia, type, Long.class); + break; + case Type.DOUBLE: + box(ia, type, Double.class); + break; + default: + // Nothing to do, it's an object already + } + } + + private static void box(InstructionAdapter ia, Type from, Class boxedClass) { + Type boxedType = Type.getType(boxedClass); + ia.invokestatic(boxedType.getInternalName(), "valueOf", "(" + from.getDescriptor() + ")" + boxedType.getDescriptor()); + } + public void dump(PrintStream out) { for (DumpAction task : dumpTasks) { task.dump(out); 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 1d7f266b0ad..5cb50fc5d01 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 @@ -28,4 +28,7 @@ interface MethodData extends MemberData { // -1 for no @MethodDesc int getMethodDescParameterIndex(); + + // -1 for no @AllArgs + int getAllArgsParameterIndex(); } 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 fa2b385441e..ee4cb491df2 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 @@ -22,6 +22,7 @@ public class MethodDataImpl extends MemberDataImpl implements MethodData { private final int thisParameterIndex; private final int methodNameParameterIndex; private final int methodDescParameterIndex; + private final int allArgsParameterIndex; MethodDataImpl( FieldData ownerField, @@ -31,7 +32,8 @@ public class MethodDataImpl extends MemberDataImpl implements MethodData { int parameterCount, int thisParameterIndex, int methodNameParameterIndex, - int methodDescParameterIndex + int methodDescParameterIndex, + int allArgsParameterIndex ) { super(declaringClass, name, desc); this.ownerField = ownerField; @@ -39,6 +41,7 @@ public class MethodDataImpl extends MemberDataImpl implements MethodData { this.thisParameterIndex = thisParameterIndex; this.methodNameParameterIndex = methodNameParameterIndex; this.methodDescParameterIndex = methodDescParameterIndex; + this.allArgsParameterIndex = allArgsParameterIndex; } @Override @@ -65,4 +68,9 @@ public class MethodDataImpl extends MemberDataImpl implements MethodData { public int getMethodDescParameterIndex() { return methodDescParameterIndex; } + + @Override + public int getAllArgsParameterIndex() { + return allArgsParameterIndex; + } }