Search in sources :

Example 1 with ExecutableMethodsDefinitionWriter

use of io.micronaut.inject.writer.ExecutableMethodsDefinitionWriter in project micronaut-core by micronaut-projects.

the class AopProxyWriter method visitBeanDefinitionEnd.

/**
 * Finalizes the proxy. This method should be called before writing the proxy to disk with {@link #writeTo(File)}
 */
@Override
public void visitBeanDefinitionEnd() {
    if (declaredConstructor == null) {
        throw new IllegalStateException("The method visitBeanDefinitionConstructor(..) should be called at least once");
    } else {
        initConstructor(declaredConstructor);
    }
    if (parentWriter != null && !isProxyTarget) {
        processAlreadyVisitedMethods(parentWriter);
    }
    interceptorParameter.annotate(AnnotationUtil.ANN_INTERCEPTOR_BINDING_QUALIFIER, builder -> {
        final AnnotationValue<?>[] interceptorBinding = this.interceptorBinding.toArray(new AnnotationValue[0]);
        builder.values(interceptorBinding);
    });
    qualifierParameter.annotate(AnnotationUtil.NULLABLE);
    String constructorDescriptor = getConstructorDescriptor(Arrays.asList(newConstructor.getParameters()));
    ClassWriter proxyClassWriter = this.classWriter;
    this.constructorWriter = proxyClassWriter.visitMethod(ACC_PUBLIC, CONSTRUCTOR_NAME, constructorDescriptor, null, null);
    this.constructorGenerator = new GeneratorAdapter(constructorWriter, Opcodes.ACC_PUBLIC, CONSTRUCTOR_NAME, constructorDescriptor);
    GeneratorAdapter proxyConstructorGenerator = this.constructorGenerator;
    proxyConstructorGenerator.loadThis();
    if (isInterface) {
        proxyConstructorGenerator.invokeConstructor(TYPE_OBJECT, METHOD_DEFAULT_CONSTRUCTOR);
    } else {
        ParameterElement[] existingArguments = declaredConstructor.getParameters();
        for (int i = 0; i < existingArguments.length; i++) {
            proxyConstructorGenerator.loadArg(i);
        }
        String superConstructorDescriptor = getConstructorDescriptor(Arrays.asList(existingArguments));
        proxyConstructorGenerator.invokeConstructor(getTypeReferenceForName(targetClassFullName), new Method(CONSTRUCTOR_NAME, superConstructorDescriptor));
    }
    proxyBeanDefinitionWriter.visitBeanDefinitionConstructor(newConstructor, constructorRequiresReflection, visitorContext);
    GeneratorAdapter targetDefinitionGenerator = null;
    GeneratorAdapter targetTypeGenerator = null;
    if (parentWriter != null) {
        proxyBeanDefinitionWriter.visitBeanDefinitionInterface(ProxyBeanDefinition.class);
        ClassVisitor pcw = proxyBeanDefinitionWriter.getClassWriter();
        targetDefinitionGenerator = new GeneratorAdapter(pcw.visitMethod(ACC_PUBLIC, METHOD_PROXY_TARGET_TYPE.getName(), METHOD_PROXY_TARGET_TYPE.getDescriptor(), null, null), ACC_PUBLIC, METHOD_PROXY_TARGET_TYPE.getName(), METHOD_PROXY_TARGET_TYPE.getDescriptor());
        targetDefinitionGenerator.loadThis();
        targetDefinitionGenerator.push(getTypeReferenceForName(parentWriter.getBeanDefinitionName()));
        targetDefinitionGenerator.returnValue();
        targetTypeGenerator = new GeneratorAdapter(pcw.visitMethod(ACC_PUBLIC, METHOD_PROXY_TARGET_CLASS.getName(), METHOD_PROXY_TARGET_CLASS.getDescriptor(), null, null), ACC_PUBLIC, METHOD_PROXY_TARGET_CLASS.getName(), METHOD_PROXY_TARGET_CLASS.getDescriptor());
        targetTypeGenerator.loadThis();
        targetTypeGenerator.push(getTypeReferenceForName(parentWriter.getBeanTypeName()));
        targetTypeGenerator.returnValue();
    }
    Class<?> interceptedInterface = isIntroduction ? Introduced.class : Intercepted.class;
    Type targetType = getTypeReferenceForName(targetClassFullName);
    // add the $beanLocator field
    if (isProxyTarget) {
        proxyClassWriter.visitField(ACC_PRIVATE | ACC_FINAL, FIELD_BEAN_LOCATOR, TYPE_BEAN_LOCATOR.getDescriptor(), null, null);
        // add the $beanQualifier field
        proxyClassWriter.visitField(ACC_PRIVATE, FIELD_BEAN_QUALIFIER, Type.getType(Qualifier.class).getDescriptor(), null, null);
        writeWithQualifierMethod(proxyClassWriter);
        if (!lazy || cacheLazyTarget) {
            // add the $target field for the target bean
            int modifiers = hotswap ? ACC_PRIVATE : ACC_PRIVATE | ACC_FINAL;
            proxyClassWriter.visitField(modifiers, FIELD_TARGET, targetType.getDescriptor(), null, null);
        }
        if (lazy) {
            interceptedInterface = InterceptedProxy.class;
            proxyClassWriter.visitField(ACC_PRIVATE, FIELD_BEAN_RESOLUTION_CONTEXT, Type.getType(BeanResolutionContext.class).getDescriptor(), null, null);
        } else {
            interceptedInterface = hotswap ? HotSwappableInterceptedProxy.class : InterceptedProxy.class;
            if (hotswap) {
                // Add ReadWriteLock field
                // private final ReentrantReadWriteLock $target_rwl = new ReentrantReadWriteLock();
                proxyClassWriter.visitField(ACC_PRIVATE | ACC_FINAL, FIELD_READ_WRITE_LOCK, TYPE_READ_WRITE_LOCK.getDescriptor(), null, null);
                proxyConstructorGenerator.loadThis();
                pushNewInstance(proxyConstructorGenerator, TYPE_READ_WRITE_LOCK);
                proxyConstructorGenerator.putField(proxyType, FIELD_READ_WRITE_LOCK, TYPE_READ_WRITE_LOCK);
                // Add Read Lock field
                // private final Lock $target_rl = $target_rwl.readLock();
                proxyClassWriter.visitField(ACC_PRIVATE | ACC_FINAL, FIELD_READ_LOCK, TYPE_LOCK.getDescriptor(), null, null);
                proxyConstructorGenerator.loadThis();
                proxyConstructorGenerator.loadThis();
                proxyConstructorGenerator.getField(proxyType, FIELD_READ_WRITE_LOCK, TYPE_READ_WRITE_LOCK);
                proxyConstructorGenerator.invokeInterface(Type.getType(ReadWriteLock.class), Method.getMethod(Lock.class.getName() + " readLock()"));
                proxyConstructorGenerator.putField(proxyType, FIELD_READ_LOCK, TYPE_LOCK);
                // Add Write Lock field
                // private final Lock $target_wl = $target_rwl.writeLock();
                proxyClassWriter.visitField(ACC_PRIVATE | ACC_FINAL, FIELD_WRITE_LOCK, Type.getDescriptor(Lock.class), null, null);
                proxyConstructorGenerator.loadThis();
                proxyConstructorGenerator.loadThis();
                proxyConstructorGenerator.getField(proxyType, FIELD_READ_WRITE_LOCK, TYPE_READ_WRITE_LOCK);
                proxyConstructorGenerator.invokeInterface(Type.getType(ReadWriteLock.class), Method.getMethod(Lock.class.getName() + " writeLock()"));
                proxyConstructorGenerator.putField(proxyType, FIELD_WRITE_LOCK, TYPE_LOCK);
            }
        }
        // assign the bean locator
        proxyConstructorGenerator.loadThis();
        proxyConstructorGenerator.loadArg(beanContextArgumentIndex);
        proxyConstructorGenerator.putField(proxyType, FIELD_BEAN_LOCATOR, TYPE_BEAN_LOCATOR);
        proxyConstructorGenerator.loadThis();
        proxyConstructorGenerator.loadArg(qualifierIndex);
        proxyConstructorGenerator.putField(proxyType, FIELD_BEAN_QUALIFIER, Type.getType(Qualifier.class));
        if (!lazy) {
            proxyConstructorGenerator.loadThis();
            pushResolveProxyTargetBean(proxyConstructorGenerator, targetType);
            proxyConstructorGenerator.putField(proxyType, FIELD_TARGET, targetType);
        } else {
            proxyConstructorGenerator.loadThis();
            proxyConstructorGenerator.loadArg(beanResolutionContextArgumentIndex);
            proxyConstructorGenerator.invokeInterface(Type.getType(BeanResolutionContext.class), Method.getMethod(ReflectionUtils.getRequiredMethod(BeanResolutionContext.class, "copy")));
            proxyConstructorGenerator.putField(proxyType, FIELD_BEAN_RESOLUTION_CONTEXT, Type.getType(BeanResolutionContext.class));
        }
        // Write the Object interceptedTarget() method
        writeInterceptedTargetMethod(proxyClassWriter, targetType);
        // e. T swap(T newInstance);
        if (hotswap && !lazy) {
            writeSwapMethod(proxyClassWriter, targetType);
        }
    }
    String[] interfaces = getImplementedInterfaceInternalNames();
    if (isInterface && implementInterface) {
        String[] adviceInterfaces = { getInternalName(targetClassFullName), Type.getInternalName(interceptedInterface) };
        interfaces = ArrayUtils.concat(interfaces, adviceInterfaces);
    } else {
        String[] adviceInterfaces = { Type.getInternalName(interceptedInterface) };
        interfaces = ArrayUtils.concat(interfaces, adviceInterfaces);
    }
    proxyClassWriter.visit(V1_8, ACC_SYNTHETIC, proxyInternalName, null, isInterface ? TYPE_OBJECT.getInternalName() : getTypeReferenceForName(targetClassFullName).getInternalName(), interfaces);
    // set $proxyMethods field
    proxyConstructorGenerator.loadThis();
    proxyConstructorGenerator.push(proxyMethodCount);
    proxyConstructorGenerator.newArray(EXECUTABLE_METHOD_TYPE);
    proxyConstructorGenerator.putField(proxyType, FIELD_PROXY_METHODS, FIELD_TYPE_PROXY_METHODS);
    // set $interceptors field
    proxyConstructorGenerator.loadThis();
    proxyConstructorGenerator.push(proxyMethodCount);
    proxyConstructorGenerator.newArray(INTERCEPTOR_ARRAY_TYPE);
    proxyConstructorGenerator.putField(proxyType, FIELD_INTERCEPTORS, FIELD_TYPE_INTERCEPTORS);
    // now initialize the held values
    if (isProxyTarget) {
        if (proxiedMethods.size() == proxyMethodCount) {
            Iterator<MethodRef> iterator = proxyTargetMethods.iterator();
            for (int i = 0; i < proxyMethodCount; i++) {
                MethodRef methodRef = iterator.next();
                // The following will initialize the array of $proxyMethod instances
                // Eg. this.$proxyMethods[0] = $PARENT_BEAN.getRequiredMethod("test", new Class[]{String.class});
                proxyConstructorGenerator.loadThis();
                // Step 1: dereference the array - this.$proxyMethods[0]
                proxyConstructorGenerator.getField(proxyType, FIELD_PROXY_METHODS, FIELD_TYPE_PROXY_METHODS);
                proxyConstructorGenerator.push(i);
                // Step 2: lookup the Method instance from the declaring type
                // context.getProxyTargetMethod("test", new Class[]{String.class});
                proxyConstructorGenerator.loadArg(beanContextArgumentIndex);
                buildProxyLookupArgument(proxyConstructorGenerator, targetType);
                proxyConstructorGenerator.loadArg(qualifierIndex);
                pushMethodNameAndTypesArguments(proxyConstructorGenerator, methodRef.name, methodRef.argumentTypes);
                proxyConstructorGenerator.invokeInterface(Type.getType(ExecutionHandleLocator.class), METHOD_GET_PROXY_TARGET);
                // Step 3: store the result in the array
                proxyConstructorGenerator.visitInsn(AASTORE);
                // Step 4: Resolve the interceptors
                // this.$interceptors[0] = InterceptorChain.resolveAroundInterceptors(this.$proxyMethods[0], var2);
                pushResolveInterceptorsCall(proxyConstructorGenerator, i, isIntroduction);
            }
        }
    } else if (!proxiedMethods.isEmpty()) {
        BeanDefinitionWriter beanDefinitionWriter = parentWriter == null ? proxyBeanDefinitionWriter : parentWriter;
        ExecutableMethodsDefinitionWriter executableMethodsDefinitionWriter = beanDefinitionWriter.getExecutableMethodsWriter();
        Type executableMethodsDefinitionType = executableMethodsDefinitionWriter.getClassType();
        proxyConstructorGenerator.newInstance(executableMethodsDefinitionType);
        proxyConstructorGenerator.dup();
        if (executableMethodsDefinitionWriter.isSupportsInterceptedProxy()) {
            proxyConstructorGenerator.push(true);
            proxyConstructorGenerator.invokeConstructor(executableMethodsDefinitionType, new Method(CONSTRUCTOR_NAME, getConstructorDescriptor(boolean.class)));
        } else {
            proxyConstructorGenerator.invokeConstructor(executableMethodsDefinitionType, new Method(CONSTRUCTOR_NAME, DESCRIPTOR_DEFAULT_CONSTRUCTOR));
        }
        int executableMethodsDefinitionIndex = proxyConstructorGenerator.newLocal(executableMethodsDefinitionType);
        proxyConstructorGenerator.storeLocal(executableMethodsDefinitionIndex, executableMethodsDefinitionType);
        for (int i = 0; i < proxyMethodCount; i++) {
            MethodRef methodRef = proxiedMethods.get(i);
            int methodIndex = methodRef.methodIndex;
            boolean introduction = isIntroduction && (executableMethodsDefinitionWriter.isAbstract(methodIndex) || (executableMethodsDefinitionWriter.isInterface(methodIndex) && !executableMethodsDefinitionWriter.isDefault(methodIndex)));
            // The following will initialize the array of $proxyMethod instances
            // Eg. this.proxyMethods[0] = new $blah0();
            proxyConstructorGenerator.loadThis();
            proxyConstructorGenerator.getField(proxyType, FIELD_PROXY_METHODS, FIELD_TYPE_PROXY_METHODS);
            proxyConstructorGenerator.push(i);
            // getExecutableMethodByIndex
            proxyConstructorGenerator.loadLocal(executableMethodsDefinitionIndex);
            proxyConstructorGenerator.push(methodIndex);
            proxyConstructorGenerator.invokeVirtual(executableMethodsDefinitionType, ExecutableMethodsDefinitionWriter.GET_EXECUTABLE_AT_INDEX_METHOD);
            proxyConstructorGenerator.visitInsn(AASTORE);
            pushResolveInterceptorsCall(proxyConstructorGenerator, i, introduction);
        }
    }
    for (Runnable fieldInjectionPoint : deferredInjectionPoints) {
        fieldInjectionPoint.run();
    }
    constructorWriter.visitInsn(RETURN);
    constructorWriter.visitMaxs(DEFAULT_MAX_STACK, 1);
    this.constructorWriter.visitEnd();
    proxyBeanDefinitionWriter.visitBeanDefinitionEnd();
    if (targetDefinitionGenerator != null) {
        targetDefinitionGenerator.visitMaxs(1, 1);
        targetDefinitionGenerator.visitEnd();
    }
    if (targetTypeGenerator != null) {
        targetTypeGenerator.visitMaxs(1, 1);
        targetTypeGenerator.visitEnd();
    }
    proxyClassWriter.visitEnd();
}
Also used : ClassVisitor(org.objectweb.asm.ClassVisitor) InterceptedProxy(io.micronaut.aop.InterceptedProxy) HotSwappableInterceptedProxy(io.micronaut.aop.HotSwappableInterceptedProxy) BeanDefinitionWriter(io.micronaut.inject.writer.BeanDefinitionWriter) HotSwappableInterceptedProxy(io.micronaut.aop.HotSwappableInterceptedProxy) ReadWriteLock(java.util.concurrent.locks.ReadWriteLock) ReentrantReadWriteLock(java.util.concurrent.locks.ReentrantReadWriteLock) Qualifier(io.micronaut.context.Qualifier) ParameterElement(io.micronaut.inject.ast.ParameterElement) ExecutableMethodsDefinitionWriter(io.micronaut.inject.writer.ExecutableMethodsDefinitionWriter) ExecutionHandleLocator(io.micronaut.context.ExecutionHandleLocator) ExecutableMethod(io.micronaut.inject.ExecutableMethod) Method(org.objectweb.asm.commons.Method) ClassWriter(org.objectweb.asm.ClassWriter) ReadWriteLock(java.util.concurrent.locks.ReadWriteLock) ReentrantReadWriteLock(java.util.concurrent.locks.ReentrantReadWriteLock) Lock(java.util.concurrent.locks.Lock) Type(org.objectweb.asm.Type) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) BeanResolutionContext(io.micronaut.context.BeanResolutionContext)

Aggregations

HotSwappableInterceptedProxy (io.micronaut.aop.HotSwappableInterceptedProxy)1 InterceptedProxy (io.micronaut.aop.InterceptedProxy)1 BeanResolutionContext (io.micronaut.context.BeanResolutionContext)1 ExecutionHandleLocator (io.micronaut.context.ExecutionHandleLocator)1 Qualifier (io.micronaut.context.Qualifier)1 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)1 ExecutableMethod (io.micronaut.inject.ExecutableMethod)1 ParameterElement (io.micronaut.inject.ast.ParameterElement)1 BeanDefinitionWriter (io.micronaut.inject.writer.BeanDefinitionWriter)1 ExecutableMethodsDefinitionWriter (io.micronaut.inject.writer.ExecutableMethodsDefinitionWriter)1 Lock (java.util.concurrent.locks.Lock)1 ReadWriteLock (java.util.concurrent.locks.ReadWriteLock)1 ReentrantReadWriteLock (java.util.concurrent.locks.ReentrantReadWriteLock)1 ClassVisitor (org.objectweb.asm.ClassVisitor)1 ClassWriter (org.objectweb.asm.ClassWriter)1 Type (org.objectweb.asm.Type)1 GeneratorAdapter (org.objectweb.asm.commons.GeneratorAdapter)1 Method (org.objectweb.asm.commons.Method)1