diff --git a/.idea/artifacts/instrumentation_jar.xml b/.idea/artifacts/instrumentation_jar.xml
new file mode 100644
index 00000000000..5e0f98bc70e
--- /dev/null
+++ b/.idea/artifacts/instrumentation_jar.xml
@@ -0,0 +1,10 @@
+
+
+ $PROJECT_DIR$/out/artifacts/instrumentation_jar
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/asm.xml b/.idea/libraries/asm.xml
new file mode 100644
index 00000000000..8968222f967
--- /dev/null
+++ b/.idea/libraries/asm.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/guava.xml b/.idea/libraries/guava.xml
new file mode 100644
index 00000000000..c89b3a6c6b1
--- /dev/null
+++ b/.idea/libraries/guava.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 687c7133d79..19cb344b290 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -19,6 +19,7 @@
+
diff --git a/compiler/preloader/instrumentation/instrumentation.iml b/compiler/preloader/instrumentation/instrumentation.iml
new file mode 100644
index 00000000000..d7a80fb225f
--- /dev/null
+++ b/compiler/preloader/instrumentation/instrumentation.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldData.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldData.java
new file mode 100644
index 00000000000..082d97936f2
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldData.java
@@ -0,0 +1,23 @@
+/*
+ * 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.Type;
+
+interface FieldData extends MemberData {
+ Type getRuntimeType();
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldDataImpl.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldDataImpl.java
new file mode 100644
index 00000000000..0164699d8ab
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldDataImpl.java
@@ -0,0 +1,34 @@
+/*
+ * 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.Type;
+
+class FieldDataImpl extends MemberDataImpl implements FieldData {
+
+ private final Type runtimeType;
+
+ public FieldDataImpl(String declaringClass, String name, String desc, Type runtimeType) {
+ super(declaringClass, name, desc);
+ this.runtimeType = runtimeType;
+ }
+
+ @Override
+ public Type getRuntimeType() {
+ return runtimeType;
+ }
+}
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
new file mode 100644
index 00000000000..39311f59573
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java
@@ -0,0 +1,319 @@
+/*
+ * 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.*;
+import org.jetbrains.asm4.commons.InstructionAdapter;
+
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.regex.Pattern;
+
+import static org.jetbrains.asm4.Opcodes.*;
+
+public class InterceptionInstrumenter implements Instrumenter {
+ private final Map classPatterns = new LinkedHashMap();
+
+ private final Set neverMatchedClassPatterns = new LinkedHashSet();
+ private final Set neverMatchedInstrumenters = new LinkedHashSet();
+
+ interface DumpAction {
+ void dump(PrintStream out);
+ }
+ private final List dumpTasks = new ArrayList();
+
+ public InterceptionInstrumenter(List> handlerClasses) {
+ for (Class> handlerClass : handlerClasses) {
+ addHandlerClass(handlerClass);
+ }
+ }
+
+ private void addHandlerClass(Class> handlerClass) {
+ for (Field field : handlerClass.getFields()) {
+ MethodInterceptor annotation = field.getAnnotation(MethodInterceptor.class);
+ if (annotation == null) continue;
+
+ if ((field.getModifiers() & Modifier.STATIC) == 0) {
+ throw new IllegalArgumentException("Non-static field annotated @MethodInterceptor: " + field);
+ }
+
+ Pattern classPattern = Pattern.compile(annotation.className());
+ List instrumenters = addClassPattern(classPattern.pattern());
+
+ try {
+ Object interceptor = field.get(null);
+ if (interceptor == null) {
+ throw new IllegalArgumentException("Interceptor is null: " + field);
+ }
+
+ Class> interceptorClass = interceptor.getClass();
+
+ FieldData fieldData = getFieldData(field, interceptorClass);
+
+ List enterData = new ArrayList();
+ List exitData = new ArrayList();
+ Method[] methods = interceptorClass.getMethods();
+ for (Method method : methods) {
+ String name = method.getName();
+ if (name.startsWith("enter")) {
+ enterData.add(getMethodData(fieldData, method));
+ }
+ else if (name.startsWith("exit")) {
+ exitData.add(getMethodData(fieldData, method));
+ }
+ else if (name.startsWith("dump")) {
+ Class>[] parameterTypes = method.getParameterTypes();
+ // Dump must have no parameters or one PrintStream parameter
+ if (parameterTypes.length > 1) continue;
+ if (parameterTypes.length == 1 && parameterTypes[0] != PrintStream.class) {
+ continue;
+ }
+ addDumpTask(interceptor, method);
+ }
+ }
+
+ String nameFromAnnotation = annotation.methodName();
+ String methodName = nameFromAnnotation.isEmpty() ? field.getName() : nameFromAnnotation;
+ MethodInstrumenterImpl instrumenter = new MethodInstrumenterImpl(
+ Pattern.compile(methodName),
+ Pattern.compile(annotation.erasedSignature()),
+ annotation.allowMultipleMatches(),
+ enterData,
+ exitData);
+ instrumenters.add(instrumenter);
+ neverMatchedInstrumenters.add(instrumenter);
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ }
+ }
+
+ private List addClassPattern(String classPattern) {
+ ClassMatcher classMatcher = classPatterns.get(classPattern);
+ if (classMatcher == null) {
+ classMatcher = new ClassMatcher(Pattern.compile(classPattern));
+ classPatterns.put(classPattern, classMatcher);
+ neverMatchedClassPatterns.add(classPattern);
+ }
+ return classMatcher.instrumenters;
+ }
+
+ private static FieldData getFieldData(Field field, Class> runtimeType) {
+ return new FieldDataImpl(
+ Type.getInternalName(field.getDeclaringClass()),
+ field.getName(),
+ Type.getDescriptor(field.getType()),
+ Type.getType(runtimeType));
+ }
+
+ private static MethodData getMethodData(FieldData interceptorField, Method method) {
+ return new MethodDataImpl(
+ interceptorField,
+ Type.getInternalName(method.getDeclaringClass()),
+ method.getName(),
+ Type.getMethodDescriptor(method),
+ method.getParameterTypes().length
+ );
+ }
+
+ private void addDumpTask(final Object interceptor, final Method method) {
+ dumpTasks.add(new DumpAction() {
+ @Override
+ public void dump(PrintStream out) {
+ try {
+ if (method.getParameterTypes().length == 0) {
+ method.invoke(interceptor);
+ }
+ else {
+ method.invoke(interceptor, out);
+ }
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ });
+ }
+
+ public byte[] instrument(String resourceName, byte[] data) {
+ List applicableInstrumenters = new ArrayList();
+ String className = stripClassSuffix(resourceName);
+ for (Map.Entry classPatternEntry : classPatterns.entrySet()) {
+ String classPattern = classPatternEntry.getKey();
+ ClassMatcher classMatcher = classPatternEntry.getValue();
+ if (classMatcher.classPattern.matcher(className).matches()) {
+ neverMatchedClassPatterns.remove(classPattern);
+ applicableInstrumenters.addAll(classMatcher.instrumenters);
+ }
+ }
+
+ if (applicableInstrumenters.isEmpty()) return data;
+
+ return instrument(data, applicableInstrumenters);
+ }
+
+ private static String stripClassSuffix(String name) {
+ String suffix = ".class";
+ if (!name.endsWith(suffix)) return name;
+ return name.substring(0, name.length() - suffix.length());
+ }
+
+ private byte[] instrument(byte[] classData, final List instrumenters) {
+ final ClassReader cr = new ClassReader(classData);
+ ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
+ cr.accept(new ClassVisitor(ASM4, cw) {
+ private final Map matchedMethods = new HashMap();
+
+ @Override
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String desc,
+ String signature,
+ String[] exceptions
+ ) {
+ MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+ // Do not instrument synthetic methods
+ if ((access & (ACC_BRIDGE | ACC_SYNTHETIC)) != 0) return mv;
+
+ List applicableInstrumenters = new ArrayList();
+ for (MethodInstrumenter instrumenter : instrumenters) {
+ if (instrumenter.isApplicable(name, desc)) {
+ applicableInstrumenters.add(instrumenter);
+
+ checkMultipleMatches(instrumenter, name, desc);
+ neverMatchedInstrumenters.remove(instrumenter);
+ }
+ }
+
+ if (applicableInstrumenters.isEmpty()) return mv;
+
+ InstructionAdapter ia = new InstructionAdapter(mv);
+
+ final List exitData = new ArrayList();
+ for (MethodInstrumenter instrumenter : applicableInstrumenters) {
+ for (MethodData enterData : instrumenter.getEnterData()) {
+ invokeMethod(access, name, desc, ia, enterData);
+ }
+
+ exitData.addAll(instrumenter.getExitData());
+ }
+
+ if (exitData.isEmpty()) return mv;
+
+ return new MethodVisitor(ASM4, mv) {
+
+ private InstructionAdapter ia = null;
+
+ private InstructionAdapter getInstructionAdapter() {
+ if (ia == null) {
+ ia = new InstructionAdapter(this);
+ }
+ return ia;
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ switch (opcode) {
+ case RETURN:
+ case IRETURN:
+ case LRETURN:
+ case FRETURN:
+ case DRETURN:
+ case ARETURN:
+ case ATHROW:
+ for (MethodData methodData : exitData) {
+ invokeMethod(access, name, desc, getInstructionAdapter(), methodData);
+ }
+ break;
+ }
+ super.visitInsn(opcode);
+ }
+ };
+ }
+
+ private void checkMultipleMatches(MethodInstrumenter instrumenter, String name, String desc) {
+ if (!instrumenter.allowsMultipleMatches()) {
+ String erasedSignature = name + desc;
+ String alreadyMatched = matchedMethods.put(instrumenter, erasedSignature);
+ if (alreadyMatched != null) {
+ throw new IllegalStateException(instrumenter + " matched two methods in " + cr.getClassName() + ":\n"
+ + alreadyMatched + "\n"
+ + erasedSignature);
+ }
+ }
+ }
+ }, 0);
+ return cw.toByteArray();
+ }
+
+ private static void invokeMethod(int access, String name, String desc, 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;
+ for (int i = 0; i < parameterCount; i++) {
+ ia.load(base + i, parameterTypes[i]);
+ }
+ }
+ ia.invokevirtual(methodData.getDeclaringClass(), methodData.getName(), methodData.getDesc());
+ }
+
+ public void dump(PrintStream out) {
+ for (DumpAction task : dumpTasks) {
+ task.dump(out);
+ }
+
+ if (!neverMatchedClassPatterns.isEmpty()) {
+ out.println("Class patterns never matched:");
+ for (String classPattern : neverMatchedClassPatterns) {
+ out.println(" " + classPattern);
+ neverMatchedInstrumenters.removeAll(classPatterns.get(classPattern).instrumenters);
+ }
+ }
+
+ if (!neverMatchedInstrumenters.isEmpty()) {
+ out.println("Instrumenters never matched: ");
+ for (MethodInstrumenter instrumenter : neverMatchedInstrumenters) {
+ out.println(" " + instrumenter);
+ }
+ }
+ }
+
+ private static class ClassMatcher {
+ private final Pattern classPattern;
+ private final List instrumenters = new ArrayList();
+
+ private ClassMatcher(Pattern classPattern) {
+ this.classPattern = classPattern;
+ }
+ }
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenterAdaptor.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenterAdaptor.java
new file mode 100644
index 00000000000..87527e242ab
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenterAdaptor.java
@@ -0,0 +1,39 @@
+/*
+ * 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.io.PrintStream;
+import java.util.Collections;
+
+public abstract class InterceptionInstrumenterAdaptor implements Instrumenter {
+
+ private final InterceptionInstrumenter instrumenter;
+
+ public InterceptionInstrumenterAdaptor() {
+ this.instrumenter = new InterceptionInstrumenter(Collections.>singletonList(getClass()));
+ }
+
+ @Override
+ public byte[] instrument(String resourceName, byte[] data) {
+ return instrumenter.instrument(resourceName, data);
+ }
+
+ @Override
+ public void dump(PrintStream out) {
+ instrumenter.dump(out);
+ }
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberData.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberData.java
new file mode 100644
index 00000000000..31b4641776e
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberData.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+interface MemberData {
+ String getDeclaringClass();
+ String getName();
+ String getDesc();
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberDataImpl.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberDataImpl.java
new file mode 100644
index 00000000000..f2cc1f00c58
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberDataImpl.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+class MemberDataImpl implements MemberData {
+
+ private final String declaringClass;
+ private final String name;
+ private final String desc;
+
+ public MemberDataImpl(String declaringClass, String name, String desc) {
+ this.declaringClass = declaringClass;
+ this.name = name;
+ this.desc = desc;
+ }
+
+ @Override
+ public String getDeclaringClass() {
+ return declaringClass;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getDesc() {
+ return desc;
+ }
+}
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
new file mode 100644
index 00000000000..fb35f57c9fd
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+interface MethodData extends MemberData {
+ FieldData getOwnerField();
+ int getParameterCount();
+}
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
new file mode 100644
index 00000000000..2a3d172e425
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+public class MethodDataImpl extends MemberDataImpl implements MethodData {
+ private final FieldData ownerField;
+ private final int parameterCount;
+
+ MethodDataImpl(
+ FieldData ownerField,
+ String declaringClass,
+ String name,
+ String desc,
+ int parameterCount
+ ) {
+ super(declaringClass, name, desc);
+ this.ownerField = ownerField;
+ this.parameterCount = parameterCount;
+ }
+
+ @Override
+ public FieldData getOwnerField() {
+ return ownerField;
+ }
+
+ @Override
+ public int getParameterCount() {
+ return parameterCount;
+ }
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenter.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenter.java
new file mode 100644
index 00000000000..6310f311100
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenter.java
@@ -0,0 +1,29 @@
+/*
+ * 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.util.List;
+
+interface MethodInstrumenter {
+ boolean isApplicable(String name, String desc);
+
+ List getExitData();
+
+ List getEnterData();
+
+ boolean allowsMultipleMatches();
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenterImpl.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenterImpl.java
new file mode 100644
index 00000000000..9d711d2472a
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenterImpl.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.List;
+import java.util.regex.Pattern;
+
+class MethodInstrumenterImpl implements MethodInstrumenter {
+ private final Pattern namePattern;
+ private final Pattern descPattern;
+ private final boolean allowMultipleMatches;
+ private final List enterData;
+ private final List exitData;
+
+ public MethodInstrumenterImpl(
+ Pattern namePattern,
+ Pattern descPattern,
+ boolean allowMultipleMatches,
+ List enterData,
+ List exitData
+ ) {
+ this.namePattern = namePattern;
+ this.descPattern = descPattern;
+ this.allowMultipleMatches = allowMultipleMatches;
+ this.enterData = enterData;
+ this.exitData = exitData;
+ }
+
+ @Override
+ public boolean allowsMultipleMatches() {
+ return allowMultipleMatches;
+ }
+
+ @Override
+ public boolean isApplicable(String name, String desc) {
+ return namePattern.matcher(name).matches() && descPattern.matcher(desc).matches();
+ }
+
+ @Override
+ public List getEnterData() {
+ return enterData;
+ }
+
+ @Override
+ public List getExitData() {
+ return exitData;
+ }
+
+ @Override
+ public String toString() {
+ return namePattern + " " + descPattern + (allowMultipleMatches ? "[multiple]" : "");
+ }
+}
diff --git a/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInterceptor.java b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInterceptor.java
new file mode 100644
index 00000000000..9f0790081d3
--- /dev/null
+++ b/compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInterceptor.java
@@ -0,0 +1,38 @@
+/*
+ * 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.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MethodInterceptor {
+ // JVM internal name like java/util/Map$Entry or short name like FooBar
+ String className();
+
+ // regexp, if omitted, field name is used
+ String methodName() default "";
+
+ // regexp
+ String erasedSignature() default "";
+
+ // if this is false, an exception is thrown when more than one method in the same class matches
+ boolean allowMultipleMatches() default false;
+}