@Test @ToolsJarRule.Enforce public void testAgentSelfInitializationAuxiliaryTypes() throws Exception { assertThat(ByteBuddyAgent.installOnOpenJDK(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .rebase(isAnnotatedWith(ShouldRebase.class), ElementMatchers.is(classLoader)) .transform(new QuxTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Qux.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.newInstance()), is((Object) (FOO + BAR))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } }
@Test @ToolsJarRule.Enforce public void testAgentWithoutSelfInitializationWithNativeMethodPrefix() throws Exception { assertThat(ByteBuddyAgent.installOnOpenJDK(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .disableSelfInitialization() .withNativeMethodPrefix(QUX) .rebase(isAnnotatedWith(ShouldRebase.class), ElementMatchers.is(classLoader)) .transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Baz.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.newInstance()), is((Object) BAR)); assertThat(type.getDeclaredMethod(QUX + FOO), notNullValue(Method.class)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } }
/** * Generates a subclass of {@link CompiledFunction} with a static method "call" and static methods * for getting information from a {@link DebugInfo} instance. * * <p>The "call" method contains the compiled version of this function's AST. */ private Optional<Method> buildCompiledFunction() throws EvalException { // replace the / character in the path so we have file system compatible class names // the java specification mentions that $ should be used in generated code // see http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.8 String path = location.getPath() != null ? location.getPath().getPathString().replace('/', '$') : ""; String compiledFunctionClassName = CompiledFunction.class.getCanonicalName() + path + "$" + getName(); compilerDebug("Compiling " + getLocationPathAndLine() + " " + getName()); try { int publicStatic = Visibility.PUBLIC.getMask() | Ownership.STATIC.getMask(); TypeDescription.Latent latentCompiledFunctionClass = new TypeDescription.Latent( compiledFunctionClassName, publicStatic | TypeManifestation.FINAL.getMask(), new TypeDescription.ForLoadedType(CompiledFunction.class), Collections.<TypeDescription>emptyList()); MethodDescription getAstNode = new MethodDescription.Latent( latentCompiledFunctionClass, new MethodDescription.Token( "getAstNode", publicStatic | MethodManifestation.FINAL.getMask(), new TypeDescription.ForLoadedType(ASTNode.class), Arrays.asList(new TypeDescription.ForLoadedType(int.class)))); MethodDescription getLocation = new MethodDescription.Latent( latentCompiledFunctionClass, new MethodDescription.Token( "getLocation", publicStatic | MethodManifestation.FINAL.getMask(), new TypeDescription.ForLoadedType(Location.class), Arrays.asList(new TypeDescription.ForLoadedType(int.class)))); DebugInfo debugInfo = new DebugInfo(getAstNode, getLocation); FunctionSignature sig = signature.getSignature(); VariableScope scope = VariableScope.function(sig.getNames()); Implementation compiledImplementation = compileBody(scope, debugInfo); List<Class<?>> parameterTypes = sig.getShape().toClasses(); parameterTypes.add(Environment.class); Unloaded<CompiledFunction> unloadedImplementation = new ByteBuddy() .withClassVisitor(new StackMapFrameClassVisitor(debugCompilerPrintByteCode)) .subclass(CompiledFunction.class) .name(compiledFunctionClassName) .defineMethod( "call", Object.class, parameterTypes, Visibility.PUBLIC, Ownership.STATIC, MethodManifestation.FINAL) .intercept(compiledImplementation) .defineMethod(getAstNode) // TODO(bazel-team) unify the two delegate fields into one, probably needs a custom // ImplementationDelegate that adds it only once? or just create the static field // itself with the correct value and create getAstNode & getLocation with a custom // implementation using it .intercept( MethodDelegation.to(debugInfo, DebugInfo.class, "getAstNodeDelegate") .filter(ElementMatchers.named("getAstNode"))) .defineMethod(getLocation) .intercept( MethodDelegation.to(debugInfo, DebugInfo.class, "getLocationDelegate") .filter(ElementMatchers.named("getLocation"))) .make(); saveByteCode(unloadedImplementation); Class<? extends CompiledFunction> functionClass = unloadedImplementation .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); return Optional.of( ReflectionUtils.getMethod( functionClass, "call", parameterTypes.toArray(new Class<?>[parameterTypes.size()])) .getLoadedMethod()); } catch (EvalException e) { // don't capture EvalExceptions throw e; } catch (Throwable e) { compilerDebug("Error while compiling", e); // TODO(bazel-team) don't capture all throwables? couldn't compile this, log somewhere? } return Optional.absent(); }
private <E> DynamicType.Builder<E> getNode( final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { return builder .method(ElementMatchers.is(method)) .intercept(MethodDelegation.to(getVertexInterceptor.class)); }