All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.javaagent.tooling.instrumentation.indy.IndyProxyFactory Maven / Gradle / Ivy

There is a newer version: 2.12.0-alpha
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.javaagent.tooling.instrumentation.indy;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.InvokeDynamic;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.utility.JavaConstant;

/**
 * Factory for generating proxies which invoke their target via {@code INVOKEDYNAMIC}. Generated
 * proxy classes have the following properties: The generated proxies have the following basic
 * structure:
 *
 * 
    *
  • it has same superclass as the proxied class *
  • it implements all interfaces implemented by the proxied class *
  • for every public constructor of the proxied class, it defined a matching public constructor * which: *
      *
    • invokes the default constructor of the superclass *
    • invoked the corresponding constructor of the proxied class to generate the object to * which the proxy delegates *
    *
  • it "copies" every declared static and non-static public method, the implementation will * delegate to the corresponding method in the proxied class *
  • all annotations on the proxied class and on its methods are copied to the proxy *
* *

Note that only the public methods declared by the proxied class are actually proxied. * Inherited methods are not automatically proxied. If you want those to be proxied, you'll need to * explicitly override them in the proxied class. */ public class IndyProxyFactory { @FunctionalInterface public interface BootstrapArgsProvider { /** * Defines the additional arguments to pass to the invokedynamic bootstrap method for a given * proxied method. The arguments have to be storable in the constant pool. * * @param classBeingProxied the type for which {@link * IndyProxyFactory#generateProxy(TypeDescription, String)} was invoked * @param proxiedMethodOrCtor the method or constructor from the proxied class for which the * arguments are requested * @return the arguments to pass to the bootstrap method */ List getBootstrapArgsForMethod( TypeDescription classBeingProxied, MethodDescription.InDefinedShape proxiedMethodOrCtor); } private static final String DELEGATE_FIELD_NAME = "delegate"; private final MethodDescription.InDefinedShape indyBootstrapMethod; private final BootstrapArgsProvider bootstrapArgsProvider; public IndyProxyFactory(Method bootstrapMethod, BootstrapArgsProvider bootstrapArgsProvider) { this.indyBootstrapMethod = new MethodDescription.ForLoadedMethod(bootstrapMethod); this.bootstrapArgsProvider = bootstrapArgsProvider; } /** * Generates a proxy. * * @param classToProxy the class for which a proxy will be generated * @param proxyClassName the desired fully qualified name for the proxy class * @return the generated proxy class */ public DynamicType.Unloaded generateProxy( TypeDescription classToProxy, String proxyClassName) { TypeDescription.Generic superClass = classToProxy.getSuperClass(); DynamicType.Builder builder = new ByteBuddy() .subclass(superClass, ConstructorStrategy.Default.NO_CONSTRUCTORS) .implement(classToProxy.getInterfaces()) .name(proxyClassName) .annotateType(classToProxy.getDeclaredAnnotations()) .defineField(DELEGATE_FIELD_NAME, Object.class, Modifier.PRIVATE | Modifier.FINAL); for (MethodDescription.InDefinedShape method : classToProxy.getDeclaredMethods()) { if (method.isPublic()) { if (method.isConstructor()) { List bootstrapArgs = bootstrapArgsProvider.getBootstrapArgsForMethod(classToProxy, method); builder = createProxyConstructor(superClass, method, bootstrapArgs, builder); } else if (method.isMethod()) { List bootstrapArgs = bootstrapArgsProvider.getBootstrapArgsForMethod(classToProxy, method); builder = createProxyMethod(method, bootstrapArgs, builder); } } } return builder.make(); } private DynamicType.Builder createProxyMethod( MethodDescription.InDefinedShape proxiedMethod, List bootstrapArgs, DynamicType.Builder builder) { InvokeDynamic body = InvokeDynamic.bootstrap(indyBootstrapMethod, bootstrapArgs); if (!proxiedMethod.isStatic()) { body = body.withField(DELEGATE_FIELD_NAME); } body = body.withMethodArguments(); int modifiers = Modifier.PUBLIC | (proxiedMethod.isStatic() ? Modifier.STATIC : 0); return createProxyMethodOrConstructor( proxiedMethod, builder.defineMethod(proxiedMethod.getName(), proxiedMethod.getReturnType(), modifiers), body); } private DynamicType.Builder createProxyConstructor( TypeDescription.Generic superClass, MethodDescription.InDefinedShape proxiedConstructor, List bootstrapArgs, DynamicType.Builder builder) { MethodDescription defaultSuperCtor = findDefaultConstructor(superClass); Implementation.Composable fieldAssignment = FieldAccessor.ofField(DELEGATE_FIELD_NAME) .setsValue( new StackManipulation.Compound( MethodVariableAccess.allArgumentsOf(proxiedConstructor), MethodInvocation.invoke(indyBootstrapMethod) .dynamic( "ctor", // the actual method name is not allowed by the verifier TypeDescription.ForLoadedType.of(Object.class), proxiedConstructor.getParameters().asTypeList().asErasures(), bootstrapArgs)), Object.class); Implementation.Composable ctorBody = MethodCall.invoke(defaultSuperCtor).andThen(fieldAssignment); return createProxyMethodOrConstructor( proxiedConstructor, builder.defineConstructor(Modifier.PUBLIC), ctorBody); } private static MethodDescription findDefaultConstructor(TypeDescription.Generic superClass) { return superClass.getDeclaredMethods().stream() .filter(MethodDescription::isConstructor) .filter(constructor -> constructor.getParameters().isEmpty()) .findFirst() .orElseThrow( () -> new IllegalArgumentException( "Superclass of provided type does not define a default constructor")); } private static DynamicType.Builder createProxyMethodOrConstructor( MethodDescription.InDefinedShape method, DynamicType.Builder.MethodDefinition.ParameterDefinition methodDef, Implementation methodBody) { for (ParameterDescription param : method.getParameters()) { methodDef = methodDef .withParameter(param.getType(), param.getName(), param.getModifiers()) .annotateParameter(param.getDeclaredAnnotations()); } return methodDef .throwing(method.getExceptionTypes()) .intercept(methodBody) .annotateMethod(method.getDeclaredAnnotations()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy