package io.micronaut.inject.writer;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.micronaut.context.AbstractBeanDefinition;
import io.micronaut.context.AbstractParametrizedBeanDefinition;
import io.micronaut.context.BeanContext;
import io.micronaut.context.BeanResolutionContext;
import io.micronaut.context.DefaultBeanContext;
import io.micronaut.context.annotation.ConfigurationBuilder;
import io.micronaut.context.annotation.ConfigurationProperties;
import io.micronaut.context.annotation.DefaultScope;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.EachProperty;
import io.micronaut.context.annotation.Parameter;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.annotation.Property;
import io.micronaut.context.annotation.Provided;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.*;
import io.micronaut.inject.annotation.AnnotationMetadataHierarchy;
import io.micronaut.inject.annotation.AnnotationMetadataReference;
import io.micronaut.inject.annotation.AnnotationMetadataWriter;
import io.micronaut.inject.annotation.DefaultAnnotationMetadata;
import io.micronaut.inject.ast.Element;
import io.micronaut.inject.configuration.ConfigurationMetadataBuilder;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.signature.SignatureVisitor;
import org.objectweb.asm.signature.SignatureWriter;
import javax.inject.Scope;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Internal
public class BeanDefinitionWriter extends AbstractClassFileWriter implements BeanDefinitionVisitor {
private static final Constructor<AbstractBeanDefinition> CONSTRUCTOR_ABSTRACT_BEAN_DEFINITION = ReflectionUtils.findConstructor(
AbstractBeanDefinition.class,
Class.class,
AnnotationMetadata.class,
boolean.class,
Argument[].class)
.orElseThrow(() -> new ClassGenerationException("Invalid version of Micronaut present on the class path"));
private static final org.objectweb.asm.commons.Method METHOD_MAP_OF = org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredInternalMethod(
CollectionUtils.class,
"mapOf",
Object[].class
)
);
private static final Method POST_CONSTRUCT_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "postConstruct", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method INJECT_BEAN_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "injectBean", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method PRE_DESTROY_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "preDestroy", BeanResolutionContext.class, BeanContext.class, Object.class);
private static final Method ADD_FIELD_INJECTION_POINT_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "addInjectionPoint", Class.class, Class.class, String.class, AnnotationMetadata.class, Argument[].class, boolean.class);
private static final Method ADD_METHOD_INJECTION_POINT_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "addInjectionPoint", Class.class, String.class, Argument[].class, AnnotationMetadata.class, boolean.class);
private static final Method ADD_POST_CONSTRUCT_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "addPostConstruct", Class.class, String.class, Argument[].class, AnnotationMetadata.class, boolean.class);
private static final Method ADD_PRE_DESTROY_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "addPreDestroy", Class.class, String.class, Argument[].class, AnnotationMetadata.class, boolean.class);
private static final Method ADD_EXECUTABLE_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "addExecutableMethod", ExecutableMethod.class);
private static final Method GET_BEAN_FOR_CONSTRUCTOR_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getBeanForConstructorArgument", BeanResolutionContext.class, BeanContext.class, int.class);
private static final Method GET_VALUE_FOR_CONSTRUCTOR_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getValueForConstructorArgument", BeanResolutionContext.class, BeanContext.class, int.class);
private static final Method GET_BEAN_FOR_FIELD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getBeanForField", BeanResolutionContext.class, BeanContext.class, int.class);
private static final Method GET_VALUE_FOR_FIELD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getValueForField", BeanResolutionContext.class, BeanContext.class, int.class);
private static final Method GET_VALUE_FOR_PATH = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getValueForPath", BeanResolutionContext.class, BeanContext.class, Argument.class, String.class);
private static final Method CONTAINS_VALUE_FOR_FIELD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "containsValueForField", BeanResolutionContext.class, BeanContext.class, int.class);
private static final Method CONTAINS_PROPERTIES_METHOD = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "containsProperties", BeanResolutionContext.class, BeanContext.class);
private static final Method GET_BEAN_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getBeanForMethodArgument", BeanResolutionContext.class, BeanContext.class, int.class, int.class);
private static final Method GET_VALUE_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "getValueForMethodArgument", BeanResolutionContext.class, BeanContext.class, int.class, int.class);
private static final Method CONTAINS_VALUE_FOR_METHOD_ARGUMENT = ReflectionUtils.getRequiredInternalMethod(AbstractBeanDefinition.class, "containsValueForMethodArgument", BeanResolutionContext.class, BeanContext.class, int.class, int.class);
private static final org.objectweb.asm.commons.Method BEAN_DEFINITION_CLASS_CONSTRUCTOR = new org.objectweb.asm.commons.Method(CONSTRUCTOR_NAME, getConstructorDescriptor(
Class.class, AnnotationMetadata.class, boolean.class, Argument[].class
));
private static final org.objectweb.asm.commons.Method BEAN_DEFINITION_METHOD_CONSTRUCTOR = new org.objectweb.asm.commons.Method(CONSTRUCTOR_NAME, getConstructorDescriptor(
Class.class, Class.class, String.class, AnnotationMetadata.class, boolean.class, Argument[].class
));
private static final Type TYPE_ABSTRACT_BEAN_DEFINITION = Type.getType(AbstractBeanDefinition.class);
private static final Type TYPE_ABSTRACT_PARAMETRIZED_BEAN_DEFINITION = Type.getType(AbstractParametrizedBeanDefinition.class);
private static final org.objectweb.asm.commons.Method METHOD_OPTIONAL_EMPTY = org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredMethod(Optional.class, "empty")
);
private static final Type TYPE_OPTIONAL = Type.getType(Optional.class);
private static final org.objectweb.asm.commons.Method METHOD_OPTIONAL_OF = org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredMethod(Optional.class, "of", Object.class)
);
private final ClassWriter classWriter;
private final String beanFullClassName;
private final String beanDefinitionName;
private final String beanDefinitionInternalName;
private final Type beanType;
private final Type providedType;
private final Set<Class> interfaceTypes;
private final Map<String, GeneratorAdapter> loadTypeMethods = new LinkedHashMap<>();
private final Map<String, ExecutableMethodWriter> methodExecutors = new LinkedHashMap<>();
private final String providedBeanClassName;
private final String packageName;
private final String beanSimpleClassName;
private final Type beanDefinitionType;
private final boolean isInterface;
private final boolean isConfigurationProperties;
private GeneratorAdapter constructorVisitor;
private GeneratorAdapter buildMethodVisitor;
private GeneratorAdapter injectMethodVisitor;
private Label injectEnd = null;
private GeneratorAdapter preDestroyMethodVisitor;
private GeneratorAdapter postConstructMethodVisitor;
private int methodExecutorIndex = 0;
private int currentFieldIndex = 0;
private int currentMethodIndex = 0;
private int buildMethodLocalCount = 4;
private int injectMethodLocalCount = 4;
private int postConstructMethodLocalCount = 4;
private int preDestroyMethodLocalCount = 4;
private int buildInstanceIndex;
private int argsIndex = -1;
private int injectInstanceIndex;
private int postConstructInstanceIndex;
private int preDestroyInstanceIndex;
private boolean beanFinalized = false;
private Type superType = TYPE_ABSTRACT_BEAN_DEFINITION;
private boolean isSuperFactory = false;
private final AnnotationMetadata annotationMetadata;
private ConfigBuilderState currentConfigBuilderState;
private int optionalInstanceIndex;
private boolean preprocessMethods = false;
private Map<String, Map<String, Object>> typeArguments;
private List<MethodVisitData> postConstructMethodVisits = new ArrayList<>(2);
private List<MethodVisitData> preDestroyMethodVisits = new ArrayList<>(2);
private String interceptedType;
private List<Runnable> deferredInjectionPoints = new ArrayList<>();
public BeanDefinitionWriter(String packageName,
String className,
Element originatingElement,
AnnotationMetadata annotationMetadata) {
this(packageName, className, packageName + '.' + className, false, originatingElement, annotationMetadata);
}
public BeanDefinitionWriter(String packageName,
String className,
boolean isInterface,
Element originatingElement,
AnnotationMetadata annotationMetadata) {
this(packageName, className, packageName + '.' + className, isInterface, originatingElement, annotationMetadata);
}
public BeanDefinitionWriter(String packageName,
String className,
boolean isInterface,
OriginatingElements originatingElements,
AnnotationMetadata annotationMetadata) {
this(packageName,
className,
packageName + '.' + className,
isInterface,
originatingElements,
annotationMetadata
);
}
public BeanDefinitionWriter(String packageName,
String className,
String providedClassName,
boolean isInterface,
OriginatingElements originatingElements,
AnnotationMetadata annotationMetadata) {
this(
packageName,
className,
packageName + ".$" + className + "Definition",
providedClassName,
isInterface,
originatingElements,
annotationMetadata
);
}
public BeanDefinitionWriter(String packageName,
String className,
String providedClassName,
boolean isInterface,
Element originatingElement,
AnnotationMetadata annotationMetadata) {
this(
packageName,
className,
packageName + ".$" + className + "Definition",
providedClassName,
isInterface,
OriginatingElements.of(originatingElement),
annotationMetadata
);
}
public BeanDefinitionWriter(String packageName,
String className,
String beanDefinitionName,
String providedClassName,
boolean isInterface,
Element originatingElement,
AnnotationMetadata annotationMetadata) {
this(packageName, className, beanDefinitionName, providedClassName, isInterface, OriginatingElements.of(originatingElement), annotationMetadata);
}
public BeanDefinitionWriter(String packageName,
String className,
String beanDefinitionName,
String providedClassName,
boolean isInterface,
OriginatingElements originatingElements,
AnnotationMetadata annotationMetadata) {
super(originatingElements);
this.classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
this.packageName = packageName;
this.isInterface = isInterface;
this.beanFullClassName = packageName + '.' + className;
this.annotationMetadata = annotationMetadata;
this.beanSimpleClassName = className;
this.providedBeanClassName = providedClassName;
this.beanDefinitionName = beanDefinitionName;
this.beanDefinitionType = getTypeReference(this.beanDefinitionName);
this.beanType = getTypeReference(beanFullClassName);
this.providedType = getTypeReference(providedBeanClassName);
this.beanDefinitionInternalName = getInternalName(this.beanDefinitionName);
this.interfaceTypes = new TreeSet<>(Comparator.comparing(Class::getName));
this.interfaceTypes.add(BeanFactory.class);
this.isConfigurationProperties = annotationMetadata.hasDeclaredStereotype(ConfigurationProperties.class);
}
@Override
@NonNull
public String getBeanDefinitionReferenceClassName() {
return beanDefinitionName + BeanDefinitionReferenceWriter.REF_SUFFIX;
}
public final List<MethodVisitData> getPostConstructMethodVisits() {
return Collections.unmodifiableList(postConstructMethodVisits);
}
public List<MethodVisitData> getPreDestroyMethodVisits() {
return Collections.unmodifiableList(preDestroyMethodVisits);
}
public ClassVisitor getClassWriter() {
return classWriter;
}
@Override
public boolean isInterface() {
return isInterface;
}
@Override
public boolean isSingleton() {
return annotationMetadata.hasDeclaredStereotype(Singleton.class);
}
@Override
public void visitBeanDefinitionInterface(Class<? extends BeanDefinition> interfaceType) {
this.interfaceTypes.add(interfaceType);
}
@Override
public void visitSuperBeanDefinition(String name) {
this.superType = getTypeReference(name);
}
@Override
public void visitSuperBeanDefinitionFactory(String beanName) {
visitSuperBeanDefinition(beanName);
this.isSuperFactory = true;
}
@Override
public String getBeanTypeName() {
return beanFullClassName;
}
@Override
public Type getProvidedType() {
return providedType;
}
@Override
public void setValidated(boolean validated) {
if (validated) {
this.interfaceTypes.add(ValidatedBeanDefinition.class);
} else {
this.interfaceTypes.remove(ValidatedBeanDefinition.class);
}
}
@Override
public void setInterceptedType(String typeName) {
if (typeName != null) {
this.interfaceTypes.add(AdvisedBeanType.class);
}
this.interceptedType = typeName;
}
@Override
public Optional<Type> getInterceptedType() {
return Optional.ofNullable(interceptedType)
.map(BeanDefinitionWriter::getTypeReferenceForName);
}
@Override
public boolean isValidated() {
return this.interfaceTypes.contains(ValidatedBeanDefinition.class);
}
@Override
public String getBeanDefinitionName() {
return beanDefinitionName;
}
public String getBeanDefinitionClassFile() {
String className = getBeanDefinitionName();
return getClassFileName(className);
}
public void visitBeanFactoryMethod(Object factoryClass,
Object producedType,
String methodName,
AnnotationMetadata methodAnnotationMetadata,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes) {
if (constructorVisitor != null) {
throw new IllegalStateException("Only a single call to visitBeanFactoryMethod(..) is permitted");
} else {
visitBuildFactoryMethodDefinition(factoryClass, methodName, argumentTypes, argumentAnnotationMetadata);
buildFactoryMethodClassConstructor(
factoryClass,
producedType,
methodName,
methodAnnotationMetadata,
argumentTypes,
argumentAnnotationMetadata,
genericTypes);
visitInjectMethodDefinition();
}
}
@Override
public void visitBeanDefinitionConstructor(AnnotationMetadata annotationMetadata,
boolean requiresReflection,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes) {
if (constructorVisitor == null) {
visitBeanDefinitionConstructorInternal(
annotationMetadata,
requiresReflection,
argumentTypes,
argumentAnnotationMetadata,
genericTypes);
visitBuildMethodDefinition(argumentTypes, argumentAnnotationMetadata);
visitInjectMethodDefinition();
}
}
@Override
public void visitBeanDefinitionConstructor(AnnotationMetadata annotationMetadata,
boolean requiresReflection) {
if (constructorVisitor == null) {
visitBeanDefinitionConstructorInternal(annotationMetadata, requiresReflection, Collections.emptyMap(), null, null);
visitBuildMethodDefinition(Collections.emptyMap(), Collections.emptyMap());
visitInjectMethodDefinition();
}
}
@SuppressWarnings("Duplicates")
@Override
public void visitBeanDefinitionEnd() {
if (classWriter == null) {
throw new IllegalStateException("At least one called to visitBeanDefinitionConstructor(..) is required");
}
String[] interfaceInternalNames = new String[interfaceTypes.size()];
Iterator<Class> j = interfaceTypes.iterator();
for (int i = 0; i < interfaceInternalNames.length; i++) {
interfaceInternalNames[i] = Type.getInternalName(j.next());
}
classWriter.visit(V1_8, ACC_SYNTHETIC,
beanDefinitionInternalName,
generateBeanDefSig(providedType.getInternalName()),
isSuperFactory ? TYPE_ABSTRACT_BEAN_DEFINITION.getInternalName() : superType.getInternalName(),
interfaceInternalNames);
classWriter.visitAnnotation(TYPE_GENERATED.getDescriptor(), false);
if (buildMethodVisitor == null) {
throw new IllegalStateException("At least one call to visitBeanDefinitionConstructor() is required");
}
finalizeInjectMethod();
finalizeBuildMethod();
finalizeAnnotationMetadata();
finalizeTypeArguments();
if (preprocessMethods) {
GeneratorAdapter requiresMethodProcessing = startPublicMethod(classWriter, "requiresMethodProcessing", boolean.class.getName());
requiresMethodProcessing.push(true);
requiresMethodProcessing.visitInsn(IRETURN);
requiresMethodProcessing.visitMaxs(1, 1);
requiresMethodProcessing.visitEnd();
}
for (Runnable fieldInjectionPoint : deferredInjectionPoints) {
fieldInjectionPoint.run();
}
constructorVisitor.visitInsn(RETURN);
constructorVisitor.visitMaxs(DEFAULT_MAX_STACK, 1);
if (buildMethodVisitor != null) {
buildMethodVisitor.visitInsn(ARETURN);
buildMethodVisitor.visitMaxs(DEFAULT_MAX_STACK, buildMethodLocalCount);
}
if (injectMethodVisitor != null) {
injectMethodVisitor.visitMaxs(DEFAULT_MAX_STACK, injectMethodLocalCount);
}
if (postConstructMethodVisitor != null) {
postConstructMethodVisitor.visitVarInsn(ALOAD, postConstructInstanceIndex);
postConstructMethodVisitor.visitInsn(ARETURN);
postConstructMethodVisitor.visitMaxs(DEFAULT_MAX_STACK, postConstructMethodLocalCount);
}
if (preDestroyMethodVisitor != null) {
preDestroyMethodVisitor.visitVarInsn(ALOAD, preDestroyInstanceIndex);
preDestroyMethodVisitor.visitInsn(ARETURN);
preDestroyMethodVisitor.visitMaxs(DEFAULT_MAX_STACK, preDestroyMethodLocalCount);
}
getInterceptedType().ifPresent(t -> implementInterceptedTypeMethod(t, this.classWriter));
for (GeneratorAdapter method : loadTypeMethods.values()) {
method.visitMaxs(3, 1);
method.visitEnd();
}
classWriter.visitEnd();
this.beanFinalized = true;
}
private void finalizeTypeArguments() {
if (CollectionUtils.isNotEmpty(typeArguments)) {
GeneratorAdapter visitor = startPublicMethodZeroArgs(classWriter, Map.class, "getTypeArgumentsMap");
int totalSize = typeArguments.size() * 2;
pushNewArray(visitor, Object.class, totalSize);
int i = 0;
for (Map.Entry<String, Map<String, Object>> entry : typeArguments.entrySet()) {
String typeName = entry.getKey();
pushStoreStringInArray(visitor, i++, totalSize, typeName);
pushStoreInArray(visitor, i++, totalSize, () ->
pushTypeArguments(visitor, entry.getValue())
);
}
visitor.invokeStatic(Type.getType(CollectionUtils.class), METHOD_MAP_OF);
visitor.returnValue();
visitor.visitMaxs(1, 1);
visitor.visitEnd();
}
}
private void finalizeAnnotationMetadata() {
if (annotationMetadata != null) {
GeneratorAdapter annotationMetadataMethod = startProtectedMethod(
classWriter,
"resolveAnnotationMetadata",
AnnotationMetadata.class.getName()
);
annotationMetadataMethod.loadThis();
annotationMetadataMethod.getStatic(getTypeReference(getBeanDefinitionReferenceClassName()), AbstractAnnotationMetadataWriter.FIELD_ANNOTATION_METADATA, Type.getType(AnnotationMetadata.class));
annotationMetadataMethod.returnValue();
annotationMetadataMethod.visitMaxs(1, 1);
annotationMetadataMethod.visitEnd();
}
AnnotationMetadata annotationMetadata = this.annotationMetadata != null ? this.annotationMetadata : AnnotationMetadata.EMPTY_METADATA;
writeBooleanMethod(classWriter, "isSingleton", () ->
annotationMetadata.hasDeclaredStereotype(Singleton.class) ||
annotationMetadata.classValue(DefaultScope.class).map(t -> t == Singleton.class).orElse(false));
writeBooleanMethod(classWriter, "isIterable", () ->
annotationMetadata.hasDeclaredStereotype(EachProperty.class) ||
annotationMetadata.hasDeclaredStereotype(EachBean.class));
writeBooleanMethod(classWriter, "isPrimary", () ->
annotationMetadata.hasDeclaredStereotype(Primary.class));
writeBooleanMethod(classWriter, "isProvided", () ->
annotationMetadata.hasDeclaredStereotype(Provided.class));
GeneratorAdapter getScopeMethod = startPublicMethodZeroArgs(
classWriter,
Optional.class,
"getScope"
);
getScopeMethod.loadThis();
Optional<String> scope = annotationMetadata.getDeclaredAnnotationNameByStereotype(Scope.class.getName());
if (scope.isPresent()) {
getScopeMethod.push(getTypeReferenceForName(scope.get()));
getScopeMethod.invokeStatic(
TYPE_OPTIONAL,
METHOD_OPTIONAL_OF
);
} else {
getScopeMethod.invokeStatic(TYPE_OPTIONAL, METHOD_OPTIONAL_EMPTY);
}
getScopeMethod.returnValue();
getScopeMethod.visitMaxs(1, 1);
getScopeMethod.visitEnd();
}
public byte[] toByteArray() {
if (!beanFinalized) {
throw new IllegalStateException("Bean definition not finalized. Call visitBeanDefinitionEnd() first.");
}
return classWriter.toByteArray();
}
@Override
public void accept(ClassWriterOutputVisitor visitor) throws IOException {
try (OutputStream out = visitor.visitClass(getBeanDefinitionName(), getOriginatingElements())) {
try {
for (ExecutableMethodWriter methodWriter : methodExecutors.values()) {
methodWriter.accept(visitor);
}
} catch (RuntimeException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
} else {
throw e;
}
}
out.write(toByteArray());
}
}
@Override
public void visitSetterValue(Object declaringType,
Object returnType,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
Object fieldType,
String fieldName,
String setterName,
Map<String, Object> genericTypes,
boolean isOptional) {
Type declaringTypeRef = getTypeReference(declaringType);
addInjectionPointForSetterInternal(annotationMetadata, requiresReflection, fieldType, fieldName, setterName, genericTypes, declaringTypeRef);
if (!requiresReflection) {
resolveBeanOrValueForSetter(declaringTypeRef, returnType, setterName, fieldType, GET_VALUE_FOR_METHOD_ARGUMENT, isOptional);
}
currentMethodIndex++;
}
@Override
public void visitSetterValue(
Object declaringType,
Object returnType,
AnnotationMetadata annotationMetadata,
boolean requiresReflection,
Object valueType,
String setterName,
Map<String, Object> genericTypes,
AnnotationMetadata setterArgumentMetadata,
boolean isOptional) {
Type declaringTypeRef = getTypeReference(declaringType);
constructorVisitor.visitVarInsn(ALOAD, 0);
constructorVisitor.push(declaringTypeRef);
constructorVisitor.push(setterName);
String propertyName = NameUtils.getPropertyNameForSetter(setterName);
pushBuildArgumentsForMethod(
beanDefinitionType,
classWriter,
constructorVisitor,
Collections.singletonMap(propertyName, valueType),
Collections.singletonMap(propertyName, setterArgumentMetadata),
Collections.singletonMap(propertyName, genericTypes),
loadTypeMethods
);
if (annotationMetadata == AnnotationMetadata.EMPTY_METADATA) {
constructorVisitor.visitInsn(ACONST_NULL);
} else {
AnnotationMetadataWriter.instantiateNewMetadata(
beanDefinitionType,
classWriter,
constructorVisitor,
(DefaultAnnotationMetadata) annotationMetadata,
loadTypeMethods
);
}
constructorVisitor.visitInsn(requiresReflection ? ICONST_1 : ICONST_0);
pushInvokeMethodOnSuperClass(constructorVisitor, ADD_METHOD_INJECTION_POINT_METHOD);
if (!requiresReflection) {
resolveBeanOrValueForSetter(declaringTypeRef, returnType, setterName, valueType, GET_VALUE_FOR_METHOD_ARGUMENT, isOptional);
}
currentMethodIndex++;
}
@Override
public void visitPostConstructMethod(Object declaringType,
boolean requiresReflection,
Object returnType,
String methodName,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata) {
visitPostConstructMethodDefinition();
final MethodVisitData methodVisitData = new MethodVisitData(
declaringType,
requiresReflection,
returnType,
methodName,
argumentTypes,
argumentAnnotationMetadata,
genericTypes,
annotationMetadata
);
postConstructMethodVisits.add(methodVisitData);
visitMethodInjectionPointInternal(methodVisitData,
constructorVisitor,
postConstructMethodVisitor,
postConstructInstanceIndex,
ADD_POST_CONSTRUCT_METHOD);
}
public void visitPreDestroyMethod(Object declaringType,
String methodName) {
visitPreDestroyMethod(declaringType, Void.TYPE, methodName);
}
public void visitPreDestroyMethod(Object declaringType,
Object returnType,
String methodName) {
visitPreDestroyMethodDefinition();
final MethodVisitData methodVisitData = new MethodVisitData(
declaringType,
false,
returnType,
methodName,
Collections.emptyMap(),
Collections.emptyMap(),
Collections.emptyMap(),
AnnotationMetadata.EMPTY_METADATA);
preDestroyMethodVisits.add(methodVisitData);
visitMethodInjectionPointInternal(methodVisitData,
constructorVisitor,
preDestroyMethodVisitor,
preDestroyInstanceIndex,
ADD_PRE_DESTROY_METHOD);
}
@Override
public void visitPreDestroyMethod(Object declaringType,
boolean requiresReflection,
Object returnType,
String methodName,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata) {
visitPreDestroyMethodDefinition();
final MethodVisitData methodVisitData = new MethodVisitData(declaringType,
requiresReflection,
returnType,
methodName,
argumentTypes,
argumentAnnotationMetadata,
genericTypes,
annotationMetadata);
preDestroyMethodVisits.add(methodVisitData);
visitMethodInjectionPointInternal(
methodVisitData,
constructorVisitor,
preDestroyMethodVisitor,
preDestroyInstanceIndex,
ADD_PRE_DESTROY_METHOD);
}
@Override
public void visitMethodInjectionPoint(Object declaringType,
boolean requiresReflection,
Object returnType,
String methodName,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata) {
GeneratorAdapter constructorVisitor = this.constructorVisitor;
GeneratorAdapter injectMethodVisitor = this.injectMethodVisitor;
int injectInstanceIndex = this.injectInstanceIndex;
visitMethodInjectionPointInternal(
new MethodVisitData(
declaringType,
requiresReflection,
returnType,
methodName,
argumentTypes,
argumentAnnotationMetadata,
genericTypes,
annotationMetadata),
constructorVisitor,
injectMethodVisitor,
injectInstanceIndex,
ADD_METHOD_INJECTION_POINT_METHOD);
}
@Override
public ExecutableMethodWriter visitExecutableMethod(Object declaringType,
Object returnType,
Object genericReturnType,
Map<String, Object> returnTypeGenericTypes,
String methodName,
Map<String, Object> argumentTypes,
Map<String, Object> genericArgumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata,
boolean isInterface,
boolean isDefault) {
return visitExecutableMethod(
declaringType,
returnType,
genericReturnType,
returnTypeGenericTypes,
methodName,
argumentTypes,
genericArgumentTypes,
argumentAnnotationMetadata,
genericTypes,
annotationMetadata,
isInterface && !isDefault,
isInterface,
isDefault,
null, null);
}
public ExecutableMethodWriter visitExecutableMethod(Object declaringType,
Object returnType,
Object genericReturnType,
Map<String, Object> returnTypeGenericTypes,
String methodName,
Map<String, Object> argumentTypes,
Map<String, Object> genericArgumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata,
boolean isAbstract,
boolean isInterface,
boolean isDefault,
String interceptedProxyClassName,
String interceptedProxyBridgeMethodName) {
String methodKey = new StringBuilder(methodName)
.append("(")
.append(argumentTypes.values().stream()
.map(Object::toString)
.collect(Collectors.joining(",")))
.append(")")
.toString();
if (methodExecutors.containsKey(methodKey)) {
return methodExecutors.get(methodKey);
}
DefaultAnnotationMetadata.contributeDefaults(
this.annotationMetadata,
annotationMetadata
);
if (argumentAnnotationMetadata != null) {
for (AnnotationMetadata metadata : argumentAnnotationMetadata.values()) {
DefaultAnnotationMetadata.contributeDefaults(
this.annotationMetadata,
metadata
);
}
}
String methodProxyShortName = "$exec" + ++methodExecutorIndex;
String methodExecutorClassName = beanDefinitionName + "$" + methodProxyShortName;
boolean isSuspend = "kotlin.coroutines.Continuation".equals(CollectionUtils.last(argumentTypes.values()));
if (annotationMetadata instanceof AnnotationMetadataHierarchy) {
annotationMetadata = new AnnotationMetadataHierarchy(
new AnnotationMetadataReference(getBeanDefinitionReferenceClassName(), this.annotationMetadata),
((AnnotationMetadataHierarchy) annotationMetadata).getDeclaredMetadata()
);
}
ExecutableMethodWriter executableMethodWriter = new ExecutableMethodWriter(
beanFullClassName,
methodExecutorClassName,
methodProxyShortName,
this.isInterface || isInterface,
isAbstract,
isDefault,
isSuspend,
this,
annotationMetadata,
interceptedProxyClassName,
interceptedProxyBridgeMethodName
);
executableMethodWriter.visitMethod(
declaringType,
returnType,
genericReturnType,
returnTypeGenericTypes,
methodName,
argumentTypes,
genericArgumentTypes,
argumentAnnotationMetadata,
genericTypes
);
methodExecutors.put(methodKey, executableMethodWriter);
deferredInjectionPoints.add(() -> {
if (constructorVisitor == null) {
throw new IllegalStateException("Method visitBeanDefinitionConstructor(..) should be called first!");
}
constructorVisitor.visitVarInsn(ALOAD, 0);
String methodExecutorInternalName = executableMethodWriter.getInternalName();
constructorVisitor.visitTypeInsn(NEW, methodExecutorInternalName);
constructorVisitor.visitInsn(DUP);
constructorVisitor.visitMethodInsn(INVOKESPECIAL,
methodExecutorInternalName,
CONSTRUCTOR_NAME,
DESCRIPTOR_DEFAULT_CONSTRUCTOR,
false);
pushInvokeMethodOnSuperClass(constructorVisitor, ADD_EXECUTABLE_METHOD);
});
return executableMethodWriter;
}
@Override
public String toString() {
return "BeanDefinitionWriter{" +
"beanFullClassName='" + beanFullClassName + '\'' +
'}';
}
@Override
public String getPackageName() {
return packageName;
}
@Override
public String getBeanSimpleName() {
return beanSimpleClassName;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return this.annotationMetadata;
}
@Override
public void visitConfigBuilderField(
Object type,
String field,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface) {
String factoryMethod = annotationMetadata
.getValue(
ConfigurationBuilder.class,
"factoryMethod",
String.class)
.orElse(null);
if (StringUtils.isNotEmpty(factoryMethod)) {
Type builderType = getTypeReference(type);
injectMethodVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
injectMethodVisitor.invokeStatic(
builderType,
org.objectweb.asm.commons.Method.getMethod(
builderType.getClassName() + " " + factoryMethod + "()"
)
);
injectMethodVisitor.putField(beanType, field, builderType);
}
this.currentConfigBuilderState = new ConfigBuilderState(
type,
field,
false,
annotationMetadata,
metadataBuilder,
isInterface);
}
@Override
public void visitConfigBuilderMethod(
Object type,
String methodName,
AnnotationMetadata annotationMetadata,
ConfigurationMetadataBuilder metadataBuilder,
boolean isInterface) {
String factoryMethod = annotationMetadata
.getValue(
ConfigurationBuilder.class,
"factoryMethod",
String.class)
.orElse(null);
if (StringUtils.isNotEmpty(factoryMethod)) {
Type builderType = getTypeReference(type);
injectMethodVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
injectMethodVisitor.invokeStatic(
builderType,
org.objectweb.asm.commons.Method.getMethod(
builderType.getClassName() + " " + factoryMethod + "()"
)
);
String propertyName = NameUtils.getPropertyNameForGetter(methodName);
String setterName = NameUtils.setterNameFor(propertyName);
injectMethodVisitor.invokeVirtual(beanType, org.objectweb.asm.commons.Method.getMethod(
"void " + setterName + "(" + builderType.getClassName() + ")"
));
}
this.currentConfigBuilderState = new ConfigBuilderState(type, methodName, true, annotationMetadata, metadataBuilder, isInterface);
}
@Override
public void visitConfigBuilderDurationMethod(
String prefix,
Object returnType,
String methodName,
String path) {
visitConfigBuilderMethodInternal(
prefix,
returnType,
methodName,
Duration.class,
Collections.emptyMap(),
true,
path
);
}
@Override
public void visitConfigBuilderMethod(
String prefix,
Object returnType,
String methodName,
Object paramType,
Map<String, Object> generics,
String path) {
visitConfigBuilderMethodInternal(
prefix,
returnType,
methodName,
paramType,
generics,
false,
path
);
}
@Override
public void visitConfigBuilderEnd() {
currentConfigBuilderState = null;
}
@Override
public void setRequiresMethodProcessing(boolean shouldPreProcess) {
this.preprocessMethods = shouldPreProcess;
}
@Override
public void visitTypeArguments(Map<String, Map<String, Object>> typeArguments) {
this.typeArguments = typeArguments;
}
@Override
public boolean requiresMethodProcessing() {
return this.preprocessMethods;
}
@Override
public void visitFieldInjectionPoint(
Object declaringType,
Object fieldType,
String fieldName,
boolean requiresReflection,
AnnotationMetadata annotationMetadata,
@Nullable Map<String, Object> typeArguments) {
visitFieldInjectionPointInternal(
declaringType,
annotationMetadata,
typeArguments,
requiresReflection,
fieldType,
fieldName,
GET_BEAN_FOR_FIELD,
false);
}
@Override
public void visitFieldValue(
Object declaringType,
Object fieldType,
String fieldName,
boolean requiresReflection,
AnnotationMetadata annotationMetadata,
@Nullable Map<String, Object> typeArguments,
boolean isOptional) {
visitFieldInjectionPointInternal(
declaringType,
annotationMetadata,
typeArguments,
requiresReflection,
fieldType,
fieldName,
GET_VALUE_FOR_FIELD,
isOptional);
}
private void visitConfigBuilderMethodInternal(
String prefix,
Object returnType,
String methodName,
Object paramType,
Map<String, Object> generics,
boolean isDurationWithTimeUnit,
String propertyPath) {
if (currentConfigBuilderState != null) {
Type builderType = currentConfigBuilderState.getType();
String builderName = currentConfigBuilderState.getName();
boolean isResolveBuilderViaMethodCall = currentConfigBuilderState.isMethod();
GeneratorAdapter injectMethodVisitor = this.injectMethodVisitor;
String propertyName = NameUtils.hyphenate(NameUtils.decapitalize(methodName.substring(prefix.length())), true);
boolean zeroArgs = paramType == null;
pushGetValueForPathCall(injectMethodVisitor, paramType, propertyName, propertyPath, zeroArgs, generics);
Label ifEnd = new Label();
injectMethodVisitor.invokeVirtual(Type.getType(Optional.class), org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredMethod(Optional.class, "isPresent")
));
injectMethodVisitor.push(false);
injectMethodVisitor.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, ifEnd);
if (zeroArgs) {
pushOptionalGet(injectMethodVisitor);
pushCastToType(injectMethodVisitor, boolean.class);
injectMethodVisitor.push(false);
injectMethodVisitor.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, ifEnd);
}
injectMethodVisitor.visitLabel(new Label());
String methodDescriptor;
if (zeroArgs) {
methodDescriptor = getMethodDescriptor(returnType, Collections.emptyList());
} else if (isDurationWithTimeUnit) {
methodDescriptor = getMethodDescriptor(returnType, Arrays.asList(long.class, TimeUnit.class));
} else {
methodDescriptor = getMethodDescriptor(returnType, Collections.singleton(paramType));
}
Label tryStart = new Label();
Label tryEnd = new Label();
Label exceptionHandler = new Label();
injectMethodVisitor.visitTryCatchBlock(tryStart, tryEnd, exceptionHandler, Type.getInternalName(NoSuchMethodError.class));
injectMethodVisitor.visitLabel(tryStart);
injectMethodVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
if (isResolveBuilderViaMethodCall) {
String desc = builderType.getClassName() + " " + builderName + "()";
injectMethodVisitor.invokeVirtual(beanType, org.objectweb.asm.commons.Method.getMethod(desc));
} else {
injectMethodVisitor.getField(beanType, builderName, builderType);
}
if (!zeroArgs) {
pushOptionalGet(injectMethodVisitor);
pushCastToType(injectMethodVisitor, paramType);
}
boolean isInterface = currentConfigBuilderState.isInterface();
if (isDurationWithTimeUnit) {
injectMethodVisitor.invokeVirtual(Type.getType(Duration.class), org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredMethod(Duration.class, "toMillis")
));
Type tu = Type.getType(TimeUnit.class);
injectMethodVisitor.getStatic(tu, "MILLISECONDS", tu);
}
if (isInterface) {
injectMethodVisitor.invokeInterface(builderType,
new org.objectweb.asm.commons.Method(methodName, methodDescriptor));
} else {
injectMethodVisitor.invokeVirtual(builderType,
new org.objectweb.asm.commons.Method(methodName, methodDescriptor));
}
if (returnType != void.class) {
injectMethodVisitor.pop();
}
injectMethodVisitor.visitJumpInsn(GOTO, tryEnd);
injectMethodVisitor.visitLabel(exceptionHandler);
injectMethodVisitor.pop();
injectMethodVisitor.visitLabel(tryEnd);
injectMethodVisitor.visitLabel(ifEnd);
}
}
private void pushOptionalGet(GeneratorAdapter injectMethodVisitor) {
injectMethodVisitor.visitVarInsn(ALOAD, optionalInstanceIndex);
injectMethodVisitor.invokeVirtual(Type.getType(Optional.class), org.objectweb.asm.commons.Method.getMethod(
ReflectionUtils.getRequiredMethod(Optional.class, "get")
));
}
private void pushGetValueForPathCall(GeneratorAdapter injectMethodVisitor, Object propertyType, String propertyName, String propertyPath, boolean zeroArgs, Map<String, Object> generics) {
injectMethodVisitor.loadThis();
injectMethodVisitor.loadArg(0);
injectMethodVisitor.loadArg(1);
if (zeroArgs) {
buildArgument(
injectMethodVisitor,
propertyName,
Boolean.class
);
} else {
buildArgumentWithGenerics(
injectMethodVisitor,
propertyName,
Collections.singletonMap(propertyType, generics)
);
}
injectMethodVisitor.push(propertyPath);
injectMethodVisitor.invokeVirtual(beanDefinitionType, org.objectweb.asm.commons.Method.getMethod(GET_VALUE_FOR_PATH));
injectMethodVisitor.visitVarInsn(ASTORE, optionalInstanceIndex);
injectMethodVisitor.visitVarInsn(ALOAD, optionalInstanceIndex);
}
private void buildFactoryMethodClassConstructor(
Object factoryClass,
Object producedType,
String methodName,
AnnotationMetadata methodAnnotationMetadata,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes) {
Type factoryTypeRef = getTypeReference(factoryClass);
Type producedTypeRef = getTypeReference(producedType);
this.constructorVisitor = buildProtectedConstructor(BEAN_DEFINITION_METHOD_CONSTRUCTOR);
GeneratorAdapter defaultConstructor = new GeneratorAdapter(
startConstructor(classWriter),
ACC_PUBLIC,
CONSTRUCTOR_NAME,
DESCRIPTOR_DEFAULT_CONSTRUCTOR
);
defaultConstructor.loadThis();
defaultConstructor.push(producedTypeRef);
defaultConstructor.push(factoryTypeRef);
defaultConstructor.push(methodName);
pushAnnotationMetadata(methodAnnotationMetadata, defaultConstructor);
defaultConstructor.push(false);
if (CollectionUtils.isNotEmpty(argumentTypes)) {
pushBuildArgumentsForMethod(
beanDefinitionType, classWriter, defaultConstructor,
argumentTypes,
argumentAnnotationMetadata,
genericTypes,
loadTypeMethods);
} else {
defaultConstructor.visitInsn(ACONST_NULL);
}
defaultConstructor.invokeConstructor(
beanDefinitionType,
BEAN_DEFINITION_METHOD_CONSTRUCTOR
);
defaultConstructor.visitInsn(RETURN);
defaultConstructor.visitMaxs(DEFAULT_MAX_STACK, 1);
defaultConstructor.visitEnd();
}
private void visitFieldInjectionPointInternal(
Object declaringType,
AnnotationMetadata annotationMetadata,
Map<String, Object> typeArguments,
boolean requiresReflection,
Object fieldType,
String fieldName,
Method methodToInvoke,
boolean isValueOptional) {
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, annotationMetadata);
GeneratorAdapter constructorVisitor = this.constructorVisitor;
constructorVisitor.loadThis();
Type declaringTypeRef = getTypeReference(declaringType);
constructorVisitor.push(declaringTypeRef);
constructorVisitor.push(getTypeReference(fieldType));
constructorVisitor.push(fieldName);
pushAnnotationMetadata(annotationMetadata, constructorVisitor);
if (CollectionUtils.isNotEmpty(typeArguments)) {
pushTypeArguments(constructorVisitor, typeArguments);
} else {
constructorVisitor.visitInsn(ACONST_NULL);
}
constructorVisitor.visitInsn(requiresReflection ? ICONST_1 : ICONST_0);
pushInvokeMethodOnSuperClass(constructorVisitor, ADD_FIELD_INJECTION_POINT_METHOD);
GeneratorAdapter injectMethodVisitor = this.injectMethodVisitor;
Label falseCondition = null;
if (isValueOptional) {
Label trueCondition = new Label();
falseCondition = new Label();
injectMethodVisitor.loadThis();
injectMethodVisitor.loadArg(0);
injectMethodVisitor.loadArg(1);
injectMethodVisitor.push(currentFieldIndex);
injectMethodVisitor.invokeVirtual(beanDefinitionType, org.objectweb.asm.commons.Method.getMethod(CONTAINS_VALUE_FOR_FIELD));
injectMethodVisitor.push(false);
injectMethodVisitor.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, falseCondition);
injectMethodVisitor.visitLabel(trueCondition);
}
if (!requiresReflection) {
injectMethodVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
injectMethodVisitor.loadThis();
injectMethodVisitor.visitVarInsn(ALOAD, 1);
injectMethodVisitor.visitVarInsn(ALOAD, 2);
injectMethodVisitor.push(currentFieldIndex);
pushInvokeMethodOnSuperClass(injectMethodVisitor, methodToInvoke);
pushCastToType(injectMethodVisitor, fieldType);
injectMethodVisitor.visitFieldInsn(PUTFIELD, declaringTypeRef.getInternalName(), fieldName, getTypeDescriptor(fieldType));
} else {
pushInjectMethodForIndex(injectMethodVisitor, injectInstanceIndex, currentFieldIndex, "injectBeanField");
}
if (falseCondition != null) {
injectMethodVisitor.visitLabel(falseCondition);
}
currentFieldIndex++;
}
private void pushAnnotationMetadata(AnnotationMetadata annotationMetadata, GeneratorAdapter constructorVisitor) {
if (!(annotationMetadata instanceof DefaultAnnotationMetadata)) {
constructorVisitor.visitInsn(ACONST_NULL);
} else {
AnnotationMetadataWriter.instantiateNewMetadata(
beanDefinitionType,
classWriter,
constructorVisitor,
(DefaultAnnotationMetadata) annotationMetadata,
loadTypeMethods
);
}
}
private void addInjectionPointForSetterInternal(
AnnotationMetadata fieldAnnotationMetadata,
boolean requiresReflection,
Object fieldType,
String fieldName,
String setterName,
Map<String, Object> genericTypes,
Type declaringTypeRef) {
GeneratorAdapter constructorVisitor = this.constructorVisitor;
constructorVisitor.visitVarInsn(ALOAD, 0);
constructorVisitor.push(declaringTypeRef);
constructorVisitor.push(setterName);
pushBuildArgumentsForMethod(
beanDefinitionType, classWriter, constructorVisitor,
Collections.singletonMap(
fieldName, fieldType
),
Collections.singletonMap(
fieldName,
fieldAnnotationMetadata
),
Collections.singletonMap(
fieldName,
genericTypes
),
loadTypeMethods);
if (fieldAnnotationMetadata == null || fieldAnnotationMetadata == AnnotationMetadata.EMPTY_METADATA) {
constructorVisitor.visitInsn(ACONST_NULL);
} else {
AnnotationMetadataWriter.instantiateNewMetadata(
beanDefinitionType,
classWriter,
constructorVisitor,
(DefaultAnnotationMetadata) fieldAnnotationMetadata,
loadTypeMethods
);
}
constructorVisitor.visitInsn(requiresReflection ? ICONST_1 : ICONST_0);
pushInvokeMethodOnSuperClass(constructorVisitor, ADD_METHOD_INJECTION_POINT_METHOD);
}
private void visitMethodInjectionPointInternal(MethodVisitData methodVisitData,
GeneratorAdapter constructorVisitor,
GeneratorAdapter injectMethodVisitor,
int injectInstanceIndex,
Method addMethodInjectionPointMethod) {
final AnnotationMetadata annotationMetadata = methodVisitData.annotationMetadata;
final Map<String, Object> argumentTypes = methodVisitData.argumentTypes;
final Object declaringType = methodVisitData.declaringType;
final String methodName = methodVisitData.methodName;
final Map<String, AnnotationMetadata> argumentMetadata = methodVisitData.argumentAnnotationMetadata;
final Map<String, Map<String, Object>> genericTypes = methodVisitData.genericTypes;
final boolean requiresReflection = methodVisitData.requiresReflection;
final Object returnType = methodVisitData.returnType;
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, annotationMetadata);
boolean hasArguments = argumentTypes != null && !argumentTypes.isEmpty();
int argCount = hasArguments ? argumentTypes.size() : 0;
Type declaringTypeRef = getTypeReference(declaringType);
constructorVisitor.visitVarInsn(ALOAD, 0);
constructorVisitor.push(declaringTypeRef);
constructorVisitor.push(methodName);
if (hasArguments) {
pushBuildArgumentsForMethod(
beanDefinitionType, classWriter, constructorVisitor,
argumentTypes,
argumentMetadata,
genericTypes,
loadTypeMethods);
for (AnnotationMetadata value : argumentMetadata.values()) {
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, value);
}
} else {
constructorVisitor.visitInsn(ACONST_NULL);
}
pushAnnotationMetadata(this.annotationMetadata, constructorVisitor);
constructorVisitor.visitInsn(requiresReflection ? ICONST_1 : ICONST_0);
Collection<Object> argumentTypeClasses = hasArguments ? argumentTypes.values() : Collections.emptyList();
pushInvokeMethodOnSuperClass(constructorVisitor, addMethodInjectionPointMethod);
if (!requiresReflection) {
injectMethodVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
String methodDescriptor;
if (hasArguments) {
methodDescriptor = getMethodDescriptor(returnType, argumentTypeClasses);
Iterator<Map.Entry<String, Object>> argIterator = argumentTypes.entrySet().iterator();
for (int i = 0; i < argCount; i++) {
Map.Entry<String, Object> entry = argIterator.next();
AnnotationMetadata argMetadata = argumentMetadata.get(entry.getKey());
injectMethodVisitor.visitVarInsn(ALOAD, 0);
injectMethodVisitor.visitVarInsn(ALOAD, 1);
injectMethodVisitor.visitVarInsn(ALOAD, 2);
injectMethodVisitor.push(currentMethodIndex);
injectMethodVisitor.push(i);
Method methodToInvoke = argMetadata.hasDeclaredStereotype(Value.class) || argMetadata.hasDeclaredStereotype(Property.class) ? GET_VALUE_FOR_METHOD_ARGUMENT : GET_BEAN_FOR_METHOD_ARGUMENT;
pushInvokeMethodOnSuperClass(injectMethodVisitor, methodToInvoke);
pushCastToType(injectMethodVisitor, entry.getValue());
}
} else {
methodDescriptor = getMethodDescriptor(returnType, Collections.emptyList());
}
injectMethodVisitor.visitMethodInsn(isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL,
declaringTypeRef.getInternalName(), methodName,
methodDescriptor, isInterface);
if (isConfigurationProperties && returnType != void.class) {
injectMethodVisitor.pop();
}
} else {
pushInjectMethodForIndex(injectMethodVisitor, injectInstanceIndex, currentMethodIndex, "injectBeanMethod");
}
currentMethodIndex++;
}
private void pushInvokeMethodOnSuperClass(MethodVisitor constructorVisitor, Method methodToInvoke) {
constructorVisitor.visitMethodInsn(INVOKESPECIAL,
isSuperFactory ? TYPE_ABSTRACT_BEAN_DEFINITION.getInternalName() : superType.getInternalName(),
methodToInvoke.getName(),
Type.getMethodDescriptor(methodToInvoke),
false);
}
private void resolveBeanOrValueForSetter(Type declaringTypeRef, Object returnType, String setterName, Object fieldType, Method resolveMethod, boolean isValueOptional) {
GeneratorAdapter injectVisitor = this.injectMethodVisitor;
Label falseCondition = null;
if (isValueOptional) {
Label trueCondition = new Label();
falseCondition = new Label();
injectVisitor.loadThis();
injectVisitor.loadArg(0);
injectVisitor.loadArg(1);
injectVisitor.push(currentMethodIndex);
injectVisitor.push(0);
injectVisitor.invokeVirtual(beanDefinitionType, org.objectweb.asm.commons.Method.getMethod(CONTAINS_VALUE_FOR_METHOD_ARGUMENT));
injectVisitor.push(false);
injectVisitor.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, falseCondition);
injectVisitor.visitLabel(trueCondition);
}
injectVisitor.visitVarInsn(ALOAD, injectInstanceIndex);
String methodDescriptor = getMethodDescriptor(returnType, Collections.singletonList(fieldType));
injectMethodVisitor.visitVarInsn(ALOAD, 0);
injectMethodVisitor.visitVarInsn(ALOAD, 1);
injectMethodVisitor.visitVarInsn(ALOAD, 2);
injectMethodVisitor.push(currentMethodIndex);
injectVisitor.push(0);
pushInvokeMethodOnSuperClass(injectVisitor, resolveMethod);
pushCastToType(injectVisitor, fieldType);
injectVisitor.visitMethodInsn(INVOKEVIRTUAL,
declaringTypeRef.getInternalName(), setterName,
methodDescriptor, false);
if (returnType != void.class) {
injectVisitor.pop();
}
if (falseCondition != null) {
injectVisitor.visitLabel(falseCondition);
}
}
@SuppressWarnings("MagicNumber")
private void visitInjectMethodDefinition() {
if (injectMethodVisitor == null) {
String desc = getMethodDescriptor(Object.class.getName(), BeanResolutionContext.class.getName(), BeanContext.class.getName(), Object.class.getName());
injectMethodVisitor = new GeneratorAdapter(classWriter.visitMethod(
ACC_PROTECTED,
"injectBean",
desc,
null,
null), ACC_PROTECTED, "injectBean", desc);
GeneratorAdapter injectMethodVisitor = this.injectMethodVisitor;
if (isConfigurationProperties) {
injectMethodVisitor.loadThis();
injectMethodVisitor.loadArg(0);
injectMethodVisitor.loadArg(1);
injectMethodVisitor.invokeVirtual(beanDefinitionType, org.objectweb.asm.commons.Method.getMethod(CONTAINS_PROPERTIES_METHOD));
injectMethodVisitor.push(false);
injectEnd = new Label();
injectMethodVisitor.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, injectEnd);
injectMethodVisitor.visitLabel(new Label());
}
injectMethodVisitor.visitVarInsn(ALOAD, 3);
injectMethodVisitor.visitTypeInsn(CHECKCAST, beanType.getInternalName());
injectInstanceIndex = pushNewInjectLocalVariable();
injectMethodVisitor.visitInsn(ACONST_NULL);
optionalInstanceIndex = pushNewInjectLocalVariable();
}
}
@SuppressWarnings("MagicNumber")
private void visitPostConstructMethodDefinition() {
if (postConstructMethodVisitor == null) {
interfaceTypes.add(InitializingBeanDefinition.class);
GeneratorAdapter postConstructMethodVisitor = newLifeCycleMethod("initialize");
this.postConstructMethodVisitor = postConstructMethodVisitor;
postConstructMethodVisitor.visitVarInsn(ALOAD, 3);
postConstructMethodVisitor.visitTypeInsn(CHECKCAST, beanType.getInternalName());
postConstructInstanceIndex = pushNewPostConstructLocalVariable();
invokeSuperInjectMethod(postConstructMethodVisitor, POST_CONSTRUCT_METHOD);
pushBeanDefinitionMethodInvocation(buildMethodVisitor, "initialize");
pushCastToType(buildMethodVisitor, beanFullClassName);
buildMethodVisitor.visitVarInsn(ASTORE, buildInstanceIndex);
}
}
private void pushInjectMethodForIndex(GeneratorAdapter methodVisitor, int instanceIndex, int injectIndex, String injectMethodName) {
Method injectBeanMethod = ReflectionUtils.getRequiredMethod(AbstractBeanDefinition.class, injectMethodName, BeanResolutionContext.class, DefaultBeanContext.class, int.class, Object.class);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitVarInsn(ALOAD, 2);
pushCastToType(methodVisitor, DefaultBeanContext.class);
methodVisitor.push(injectIndex);
methodVisitor.visitVarInsn(ALOAD, instanceIndex);
pushInvokeMethodOnSuperClass(methodVisitor, injectBeanMethod);
}
@SuppressWarnings("MagicNumber")
private void visitPreDestroyMethodDefinition() {
if (preDestroyMethodVisitor == null) {
interfaceTypes.add(DisposableBeanDefinition.class);
GeneratorAdapter preDestroyMethodVisitor = newLifeCycleMethod("dispose");
this.preDestroyMethodVisitor = preDestroyMethodVisitor;
preDestroyMethodVisitor.visitVarInsn(ALOAD, 3);
preDestroyMethodVisitor.visitTypeInsn(CHECKCAST, beanType.getInternalName());
preDestroyInstanceIndex = pushNewPreDestroyLocalVariable();
invokeSuperInjectMethod(preDestroyMethodVisitor, PRE_DESTROY_METHOD);
}
}
private GeneratorAdapter newLifeCycleMethod(String methodName) {
String desc = getMethodDescriptor(Object.class.getName(), BeanResolutionContext.class.getName(), BeanContext.class.getName(), Object.class.getName());
return new GeneratorAdapter(classWriter.visitMethod(
ACC_PUBLIC,
methodName,
desc,
getMethodSignature(getTypeDescriptor(providedBeanClassName), getTypeDescriptor(BeanResolutionContext.class.getName()), getTypeDescriptor(BeanContext.class.getName()), getTypeDescriptor(providedBeanClassName)),
null),
ACC_PUBLIC,
methodName,
desc
);
}
private void finalizeBuildMethod() {
if (!providedBeanClassName.equals(beanFullClassName)) {
buildMethodVisitor.visitVarInsn(ASTORE, buildInstanceIndex);
buildMethodVisitor.visitVarInsn(ALOAD, buildInstanceIndex);
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL,
beanType.getInternalName(),
"get",
Type.getMethodDescriptor(Type.getType(Object.class)),
false);
pushCastToType(buildMethodVisitor, providedBeanClassName);
buildMethodVisitor.visitVarInsn(ASTORE, buildInstanceIndex);
pushBeanDefinitionMethodInvocation(buildMethodVisitor, "injectAnother");
pushCastToType(buildMethodVisitor, providedBeanClassName);
}
}
private void finalizeInjectMethod() {
if (injectEnd != null) {
injectMethodVisitor.visitLabel(injectEnd);
}
invokeSuperInjectMethod(injectMethodVisitor, INJECT_BEAN_METHOD);
injectMethodVisitor.visitInsn(ARETURN);
}
@SuppressWarnings("MagicNumber")
private void invokeSuperInjectMethod(MethodVisitor methodVisitor, Method methodToInvoke) {
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitVarInsn(ALOAD, 2);
pushCastToType(methodVisitor, DefaultBeanContext.class);
methodVisitor.visitVarInsn(ALOAD, 3);
pushInvokeMethodOnSuperClass(methodVisitor, methodToInvoke);
}
private void visitBuildFactoryMethodDefinition(
Object factoryClass,
String methodName,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata) {
if (buildMethodVisitor == null) {
boolean isParametrized = isParametrized(argumentAnnotationMetadata);
defineBuilderMethod(isParametrized);
GeneratorAdapter buildMethodVisitor = this.buildMethodVisitor;
buildMethodVisitor.visitVarInsn(ALOAD, 2);
pushCastToType(buildMethodVisitor, DefaultBeanContext.class);
buildMethodVisitor.visitVarInsn(ALOAD, 1);
Type factoryType = getTypeReference(factoryClass);
buildMethodVisitor.visitLdcInsn(factoryType);
Method getBeanMethod = ReflectionUtils.getRequiredInternalMethod(DefaultBeanContext.class, "getBean", BeanResolutionContext.class, Class.class);
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL,
Type.getInternalName(DefaultBeanContext.class),
"getBean",
Type.getMethodDescriptor(getBeanMethod), false);
int factoryVar = pushNewBuildLocalVariable();
buildMethodVisitor.visitVarInsn(ALOAD, factoryVar);
pushCastToType(buildMethodVisitor, factoryClass);
if (argumentTypes.isEmpty()) {
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL,
factoryType.getInternalName(),
methodName,
Type.getMethodDescriptor(beanType), false);
} else {
pushConstructorArguments(buildMethodVisitor, argumentTypes, argumentAnnotationMetadata);
String methodDescriptor = getMethodDescriptor(beanFullClassName, argumentTypes.values());
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL,
factoryType.getInternalName(),
methodName,
methodDescriptor, false);
}
this.buildInstanceIndex = pushNewBuildLocalVariable();
pushBeanDefinitionMethodInvocation(buildMethodVisitor, "injectBean");
pushCastToType(buildMethodVisitor, beanFullClassName);
buildMethodVisitor.visitVarInsn(ASTORE, buildInstanceIndex);
buildMethodVisitor.visitVarInsn(ALOAD, buildInstanceIndex);
}
}
private void visitBuildMethodDefinition(Map<String, Object> argumentTypes, Map<String, AnnotationMetadata> argumentAnnotationMetadata) {
if (buildMethodVisitor == null) {
boolean isParametrized = isParametrized(argumentAnnotationMetadata);
defineBuilderMethod(isParametrized);
GeneratorAdapter buildMethodVisitor = this.buildMethodVisitor;
buildMethodVisitor.visitTypeInsn(NEW, beanType.getInternalName());
buildMethodVisitor.visitInsn(DUP);
pushConstructorArguments(buildMethodVisitor, argumentTypes, argumentAnnotationMetadata);
String constructorDescriptor = getConstructorDescriptor(argumentTypes.values());
buildMethodVisitor.visitMethodInsn(INVOKESPECIAL, beanType.getInternalName(), "<init>", constructorDescriptor, false);
this.buildInstanceIndex = pushNewBuildLocalVariable();
pushBeanDefinitionMethodInvocation(buildMethodVisitor, "injectBean");
pushCastToType(buildMethodVisitor, beanFullClassName);
buildMethodVisitor.visitVarInsn(ASTORE, buildInstanceIndex);
buildMethodVisitor.visitVarInsn(ALOAD, buildInstanceIndex);
}
}
private void pushConstructorArguments(GeneratorAdapter buildMethodVisitor,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata) {
int size = argumentTypes.size();
if (size > 0) {
Iterator<Map.Entry<String, Object>> iterator = argumentTypes.entrySet().iterator();
for (int i = 0; i < size; i++) {
Map.Entry<String, Object> entry = iterator.next();
AnnotationMetadata argMetadata = argumentAnnotationMetadata.get(entry.getKey());
pushConstructorArgument(buildMethodVisitor, entry.getKey(), entry.getValue(), argMetadata, i);
}
}
}
private void pushConstructorArgument(GeneratorAdapter buildMethodVisitor,
String argumentName,
Object argumentType,
AnnotationMetadata annotationMetadata,
int index) {
if (isArgumentType(annotationMetadata) && argsIndex > -1) {
buildMethodVisitor.visitVarInsn(ALOAD, argsIndex);
buildMethodVisitor.push(argumentName);
buildMethodVisitor.invokeInterface(Type.getType(Map.class), org.objectweb.asm.commons.Method.getMethod(ReflectionUtils.getRequiredMethod(Map.class, "get", Object.class)));
pushCastToType(buildMethodVisitor, argumentType);
} else {
buildMethodVisitor.visitVarInsn(ALOAD, 0);
buildMethodVisitor.visitVarInsn(ALOAD, 1);
buildMethodVisitor.visitVarInsn(ALOAD, 2);
buildMethodVisitor.push(index);
Method methodToInvoke = isValueType(annotationMetadata) ? GET_VALUE_FOR_CONSTRUCTOR_ARGUMENT : GET_BEAN_FOR_CONSTRUCTOR_ARGUMENT;
pushInvokeMethodOnSuperClass(buildMethodVisitor, methodToInvoke);
pushCastToType(buildMethodVisitor, argumentType);
}
}
private boolean isValueType(AnnotationMetadata annotationMetadata) {
if (annotationMetadata != null) {
return annotationMetadata.hasDeclaredStereotype(Value.class) || annotationMetadata.hasDeclaredStereotype(Property.class);
}
return false;
}
private boolean isArgumentType(AnnotationMetadata annotationMetadata) {
if (annotationMetadata != null) {
return annotationMetadata.hasDeclaredAnnotation(Parameter.class);
}
return false;
}
private boolean isParametrized(Map<String, AnnotationMetadata> argumentAnnotationMetadata) {
Optional<AnnotationMetadata> argumentQualifier = argumentAnnotationMetadata != null ? argumentAnnotationMetadata.values().stream().filter(this::isArgumentType).findFirst() : Optional.empty();
return argumentQualifier.isPresent();
}
private void defineBuilderMethod(boolean isParametrized) {
if (isParametrized) {
superType = TYPE_ABSTRACT_PARAMETRIZED_BEAN_DEFINITION;
argsIndex = buildMethodLocalCount++;
}
String methodDescriptor;
String methodSignature;
if (isParametrized) {
methodDescriptor = getMethodDescriptor(
Object.class.getName(),
BeanResolutionContext.class.getName(),
BeanContext.class.getName(),
BeanDefinition.class.getName(),
Map.class.getName()
);
methodSignature = getMethodSignature(
getTypeDescriptor(providedBeanClassName),
getTypeDescriptor(BeanResolutionContext.class.getName()),
getTypeDescriptor(BeanContext.class.getName()),
getTypeDescriptor(BeanDefinition.class.getName(),
providedBeanClassName),
getTypeDescriptor(Map.class.getName())
);
} else {
methodDescriptor = getMethodDescriptor(
Object.class.getName(),
BeanResolutionContext.class.getName(),
BeanContext.class.getName(),
BeanDefinition.class.getName()
);
methodSignature = getMethodSignature(
getTypeDescriptor(providedBeanClassName),
getTypeDescriptor(BeanResolutionContext.class.getName()),
getTypeDescriptor(BeanContext.class.getName()),
getTypeDescriptor(BeanDefinition.class.getName(),
providedBeanClassName)
);
}
String methodName = isParametrized ? "doBuild" : "build";
this.buildMethodVisitor = new GeneratorAdapter(classWriter.visitMethod(
ACC_PUBLIC,
methodName,
methodDescriptor,
methodSignature,
null), ACC_PUBLIC, methodName, methodDescriptor);
}
private void pushBeanDefinitionMethodInvocation(MethodVisitor buildMethodVisitor, String methodName) {
buildMethodVisitor.visitVarInsn(ALOAD, 0);
buildMethodVisitor.visitVarInsn(ALOAD, 1);
buildMethodVisitor.visitVarInsn(ALOAD, 2);
buildMethodVisitor.visitVarInsn(ALOAD, buildInstanceIndex);
buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL,
beanDefinitionInternalName,
methodName,
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(BeanResolutionContext.class), Type.getType(BeanContext.class), Type.getType(Object.class)),
false);
}
private int pushNewBuildLocalVariable() {
buildMethodVisitor.visitVarInsn(ASTORE, buildMethodLocalCount);
return buildMethodLocalCount++;
}
private int pushNewInjectLocalVariable() {
injectMethodVisitor.visitVarInsn(ASTORE, injectMethodLocalCount);
return injectMethodLocalCount++;
}
private int pushNewPostConstructLocalVariable() {
postConstructMethodVisitor.visitVarInsn(ASTORE, postConstructMethodLocalCount);
return postConstructMethodLocalCount++;
}
private int pushNewPreDestroyLocalVariable() {
preDestroyMethodVisitor.visitVarInsn(ASTORE, preDestroyMethodLocalCount);
return preDestroyMethodLocalCount++;
}
private void visitBeanDefinitionConstructorInternal(
AnnotationMetadata constructorMetadata,
boolean requiresReflection,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes) {
if (constructorVisitor == null) {
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, constructorMetadata);
Optional<AnnotationMetadata> argumentQualifier = argumentAnnotationMetadata != null ? argumentAnnotationMetadata.values().stream().filter(this::isArgumentType).findFirst() : Optional.empty();
boolean isParametrized = argumentQualifier.isPresent();
if (isParametrized) {
superType = TYPE_ABSTRACT_PARAMETRIZED_BEAN_DEFINITION;
}
org.objectweb.asm.commons.Method constructorMethod = org.objectweb.asm.commons.Method.getMethod(CONSTRUCTOR_ABSTRACT_BEAN_DEFINITION);
GeneratorAdapter protectedConstructor = new GeneratorAdapter(
classWriter.visitMethod(ACC_PROTECTED, CONSTRUCTOR_NAME, constructorMethod.getDescriptor(), null, null),
ACC_PROTECTED,
CONSTRUCTOR_NAME,
constructorMethod.getDescriptor()
);
constructorVisitor = protectedConstructor;
Type[] beanDefinitionConstructorArgumentTypes = constructorMethod.getArgumentTypes();
protectedConstructor.loadThis();
for (int i = 0; i < beanDefinitionConstructorArgumentTypes.length; i++) {
protectedConstructor.loadArg(i);
}
protectedConstructor.invokeConstructor(isSuperFactory ? TYPE_ABSTRACT_BEAN_DEFINITION : superType, BEAN_DEFINITION_CLASS_CONSTRUCTOR);
GeneratorAdapter defaultConstructor = startConstructor(classWriter);
GeneratorAdapter defaultConstructorVisitor = new GeneratorAdapter(
defaultConstructor,
ACC_PUBLIC,
CONSTRUCTOR_NAME,
DESCRIPTOR_DEFAULT_CONSTRUCTOR
);
defaultConstructor.visitVarInsn(ALOAD, 0);
defaultConstructor.visitLdcInsn(this.beanType);
if (constructorMetadata == null || constructorMetadata == AnnotationMetadata.EMPTY_METADATA) {
defaultConstructor.visitInsn(ACONST_NULL);
} else {
if (constructorMetadata instanceof AnnotationMetadataHierarchy) {
AnnotationMetadataWriter.instantiateNewMetadataHierarchy(
beanDefinitionType,
classWriter,
defaultConstructor,
(AnnotationMetadataHierarchy) constructorMetadata,
loadTypeMethods);
} else {
AnnotationMetadataWriter.instantiateNewMetadata(
beanDefinitionType,
classWriter,
defaultConstructor,
(DefaultAnnotationMetadata) constructorMetadata,
loadTypeMethods);
}
}
defaultConstructor.visitInsn(requiresReflection ? ICONST_1 : ICONST_0);
if (argumentAnnotationMetadata == null || argumentAnnotationMetadata.isEmpty()) {
defaultConstructor.visitInsn(ACONST_NULL);
} else {
pushBuildArgumentsForMethod(
beanDefinitionType,
classWriter,
defaultConstructorVisitor,
argumentTypes,
argumentAnnotationMetadata,
genericTypes,
loadTypeMethods
);
for (AnnotationMetadata value : argumentAnnotationMetadata.values()) {
DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, value);
}
}
defaultConstructorVisitor.invokeConstructor(
beanDefinitionType,
BEAN_DEFINITION_CLASS_CONSTRUCTOR
);
defaultConstructorVisitor.visitInsn(RETURN);
defaultConstructorVisitor.visitMaxs(DEFAULT_MAX_STACK, 1);
defaultConstructorVisitor.visitEnd();
}
}
private GeneratorAdapter buildProtectedConstructor(org.objectweb.asm.commons.Method constructorType) {
GeneratorAdapter protectedConstructor = new GeneratorAdapter(
classWriter.visitMethod(ACC_PROTECTED, CONSTRUCTOR_NAME, constructorType.getDescriptor(), null, null),
ACC_PROTECTED,
CONSTRUCTOR_NAME,
constructorType.getDescriptor()
);
Type[] arguments = constructorType.getArgumentTypes();
protectedConstructor.loadThis();
for (int i = 0; i < arguments.length; i++) {
protectedConstructor.loadArg(i);
}
if (isSuperFactory) {
protectedConstructor.invokeConstructor(TYPE_ABSTRACT_BEAN_DEFINITION, constructorType);
} else {
protectedConstructor.invokeConstructor(superType, constructorType);
}
return protectedConstructor;
}
private String generateBeanDefSig(String typeParameter) {
SignatureVisitor sv = new SignatureWriter();
visitSuperTypeParameters(sv, typeParameter);
String beanTypeInternalName = getInternalName(typeParameter);
for (Class interfaceType : interfaceTypes) {
SignatureVisitor bfi = sv.visitInterface();
bfi.visitClassType(Type.getInternalName(interfaceType));
SignatureVisitor iisv = bfi.visitTypeArgument('=');
iisv.visitClassType(beanTypeInternalName);
iisv.visitEnd();
bfi.visitEnd();
}
return sv.toString();
}
private void visitSuperTypeParameters(SignatureVisitor sv, String... typeParameters) {
SignatureVisitor psv = sv.visitSuperclass();
psv.visitClassType(isSuperFactory ? TYPE_ABSTRACT_BEAN_DEFINITION.getInternalName() : superType.getInternalName());
if (superType == TYPE_ABSTRACT_BEAN_DEFINITION || superType == TYPE_ABSTRACT_PARAMETRIZED_BEAN_DEFINITION || isSuperFactory) {
for (String typeParameter : typeParameters) {
SignatureVisitor ppsv = psv.visitTypeArgument('=');
String beanTypeInternalName = getInternalName(typeParameter);
ppsv.visitClassType(beanTypeInternalName);
ppsv.visitEnd();
}
}
psv.visitEnd();
}
@Internal
public static final class MethodVisitData {
private Object declaringType;
private boolean requiresReflection;
private Object returnType;
private String methodName;
private Map<String, Object> argumentTypes;
private Map<String, AnnotationMetadata> argumentAnnotationMetadata;
private Map<String, Map<String, Object>> genericTypes;
private AnnotationMetadata annotationMetadata;
MethodVisitData(
Object declaringType,
boolean requiresReflection,
Object returnType,
String methodName,
Map<String, Object> argumentTypes,
Map<String, AnnotationMetadata> argumentAnnotationMetadata,
Map<String, Map<String, Object>> genericTypes,
AnnotationMetadata annotationMetadata) {
this.declaringType = declaringType;
this.requiresReflection = requiresReflection;
this.returnType = returnType;
this.methodName = methodName;
this.argumentTypes = argumentTypes;
this.argumentAnnotationMetadata = argumentAnnotationMetadata;
this.genericTypes = genericTypes;
this.annotationMetadata = annotationMetadata;
}
public Object getDeclaringType() {
return declaringType;
}
public boolean isRequiresReflection() {
return requiresReflection;
}
public Object getReturnType() {
return returnType;
}
public String getMethodName() {
return methodName;
}
public Map<String, Object> getArgumentTypes() {
return argumentTypes;
}
public Map<String, AnnotationMetadata> getArgumentAnnotationMetadata() {
return argumentAnnotationMetadata;
}
public Map<String, Map<String, Object>> getGenericTypes() {
return genericTypes;
}
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
}
}