From 90661e8706229af5e4ddac7d492b8675bf6cfb9d Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sat, 11 May 2013 17:29:57 +0300 Subject: [PATCH] Instrumenting interceptors module --- .idea/artifacts/instrumentation_jar.xml | 10 + .idea/libraries/asm.xml | 11 + .idea/libraries/guava.xml | 11 + .idea/modules.xml | 1 + .../instrumentation/instrumentation.iml | 15 + .../preloading/instrumentation/FieldData.java | 23 ++ .../instrumentation/FieldDataImpl.java | 34 ++ .../InterceptionInstrumenter.java | 319 ++++++++++++++++++ .../InterceptionInstrumenterAdaptor.java | 39 +++ .../instrumentation/MemberData.java | 23 ++ .../instrumentation/MemberDataImpl.java | 45 +++ .../instrumentation/MethodData.java | 22 ++ .../instrumentation/MethodDataImpl.java | 44 +++ .../instrumentation/MethodInstrumenter.java | 29 ++ .../MethodInstrumenterImpl.java | 67 ++++ .../instrumentation/MethodInterceptor.java | 38 +++ 16 files changed, 731 insertions(+) create mode 100644 .idea/artifacts/instrumentation_jar.xml create mode 100644 .idea/libraries/asm.xml create mode 100644 .idea/libraries/guava.xml create mode 100644 compiler/preloader/instrumentation/instrumentation.iml create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldData.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/FieldDataImpl.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenter.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/InterceptionInstrumenterAdaptor.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberData.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MemberDataImpl.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodData.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodDataImpl.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenter.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInstrumenterImpl.java create mode 100644 compiler/preloader/instrumentation/src/org/jetbrains/jet/preloading/instrumentation/MethodInterceptor.java 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; +}