Search in sources :

Example 11 with MethodElement

use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.

the class NullableParameterRule method validate.

@Override
public RouteValidationResult validate(List<UriMatchTemplate> templates, ParameterElement[] parameters, MethodElement method) {
    List<String> errorMessages = new ArrayList<>();
    boolean isClient = method.hasAnnotation("io.micronaut.http.client.annotation.Client");
    // Optional variables can be required in clients
    if (!isClient) {
        Map<String, UriMatchVariable> variables = new HashMap<>();
        Set<UriMatchVariable> required = new HashSet<>();
        for (UriMatchTemplate template : templates) {
            for (UriMatchVariable variable : template.getVariables()) {
                if (!variable.isOptional() || variable.isExploded()) {
                    required.add(variable);
                }
                variables.compute(variable.getName(), (key, var) -> {
                    if (var == null) {
                        if (variable.isOptional() && !variable.isExploded()) {
                            return variable;
                        } else {
                            return null;
                        }
                    } else {
                        if (!var.isOptional() || var.isExploded()) {
                            if (variable.isOptional() && !variable.isExploded()) {
                                return variable;
                            } else {
                                return var;
                            }
                        } else {
                            return var;
                        }
                    }
                });
            }
        }
        for (UriMatchVariable variable : required) {
            if (templates.stream().anyMatch(t -> !t.getVariableNames().contains(variable.getName()))) {
                variables.putIfAbsent(variable.getName(), variable);
            }
        }
        for (UriMatchVariable variable : variables.values()) {
            Arrays.stream(parameters).flatMap(p -> getTypedElements(p).stream()).filter(p -> p.getName().equals(variable.getName())).forEach(p -> {
                ClassElement type = p.getType();
                boolean hasDefaultValue = p.findAnnotation(Bindable.class).flatMap(av -> av.stringValue("defaultValue")).isPresent();
                if (!isNullable(p) && type != null && !type.isAssignable(Optional.class) && !hasDefaultValue) {
                    errorMessages.add(String.format("The uri variable [%s] is optional, but the corresponding method argument [%s %s] is not defined as an Optional or annotated with a Nullable annotation.", variable.getName(), p.getType().toString(), p.getName()));
                }
            });
        }
    }
    return new RouteValidationResult(errorMessages.toArray(new String[0]));
}
Also used : Bindable(io.micronaut.core.bind.annotation.Bindable) Arrays(java.util.Arrays) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) Set(java.util.Set) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) Collectors(java.util.stream.Collectors) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) List(java.util.List) TypedElement(io.micronaut.inject.ast.TypedElement) Map(java.util.Map) MethodElement(io.micronaut.inject.ast.MethodElement) Optional(java.util.Optional) NameUtils(io.micronaut.core.naming.NameUtils) Collections(java.util.Collections) Optional(java.util.Optional) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ClassElement(io.micronaut.inject.ast.ClassElement) UriMatchTemplate(io.micronaut.http.uri.UriMatchTemplate) UriMatchVariable(io.micronaut.http.uri.UriMatchVariable) RouteValidationResult(io.micronaut.validation.routes.RouteValidationResult) HashSet(java.util.HashSet)

Example 12 with MethodElement

use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.

the class BeanIntrospectionWriter method writeInstantiateMethod.

private void writeInstantiateMethod(ClassWriter classWriter, MethodElement constructor, String methodName, Class... args) {
    final String desc = getMethodDescriptor(Object.class, Arrays.asList(args));
    final GeneratorAdapter instantiateInternal = new GeneratorAdapter(classWriter.visitMethod(ACC_PUBLIC, methodName, desc, null, null), ACC_PUBLIC, methodName, desc);
    invokeBeanConstructor(instantiateInternal, constructor, (writer, con) -> {
        List<ParameterElement> constructorArguments = Arrays.asList(con.getParameters());
        Collection<Type> argumentTypes = constructorArguments.stream().map(pe -> JavaModelUtils.getTypeReference(pe.getType())).collect(Collectors.toList());
        int i = 0;
        for (Type argumentType : argumentTypes) {
            writer.loadArg(0);
            writer.push(i++);
            writer.arrayLoad(TYPE_OBJECT);
            pushCastToType(writer, argumentType);
        }
    });
    instantiateInternal.returnValue();
    instantiateInternal.visitMaxs(3, 1);
    instantiateInternal.visitEnd();
}
Also used : ElementQuery(io.micronaut.inject.ast.ElementQuery) Arrays(java.util.Arrays) ArrayUtils(io.micronaut.core.util.ArrayUtils) FieldElement(io.micronaut.inject.ast.FieldElement) Internal(io.micronaut.core.annotation.Internal) BeanIntrospectionReference(io.micronaut.core.beans.BeanIntrospectionReference) Type(org.objectweb.asm.Type) Map(java.util.Map) NameUtils(io.micronaut.core.naming.NameUtils) AbstractBeanIntrospectionReference(io.micronaut.core.beans.AbstractBeanIntrospectionReference) AbstractAnnotationMetadataWriter(io.micronaut.inject.writer.AbstractAnnotationMetadataWriter) AnnotationMetadataWriter(io.micronaut.inject.annotation.AnnotationMetadataWriter) AnnotationMetadataHierarchy(io.micronaut.inject.annotation.AnnotationMetadataHierarchy) Collection(java.util.Collection) Set(java.util.Set) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Introspected(io.micronaut.core.annotation.Introspected) List(java.util.List) MethodElement(io.micronaut.inject.ast.MethodElement) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) ClassWriter(org.objectweb.asm.ClassWriter) AbstractInitializableBeanIntrospection(io.micronaut.inject.beans.AbstractInitializableBeanIntrospection) Label(org.objectweb.asm.Label) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) StringSwitchWriter(io.micronaut.inject.writer.StringSwitchWriter) DefaultAnnotationMetadata(io.micronaut.inject.annotation.DefaultAnnotationMetadata) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Method(org.objectweb.asm.commons.Method) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) TypedElement(io.micronaut.inject.ast.TypedElement) Nullable(io.micronaut.core.annotation.Nullable) AnnotationMetadataReference(io.micronaut.inject.annotation.AnnotationMetadataReference) BiConsumer(java.util.function.BiConsumer) Argument(io.micronaut.core.type.Argument) ClassWriterOutputVisitor(io.micronaut.inject.writer.ClassWriterOutputVisitor) LinkedHashSet(java.util.LinkedHashSet) OutputStream(java.io.OutputStream) Opcodes(org.objectweb.asm.Opcodes) IOException(java.io.IOException) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) NonNull(io.micronaut.core.annotation.NonNull) DispatchWriter(io.micronaut.inject.writer.DispatchWriter) BeanIntrospection(io.micronaut.core.beans.BeanIntrospection) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Collections(java.util.Collections) Type(org.objectweb.asm.Type) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) ParameterElement(io.micronaut.inject.ast.ParameterElement)

Example 13 with MethodElement

use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.

the class BeanIntrospectionWriter method visitProperty.

/**
 * Visit a property.
 *
 * @param type               The property type
 * @param genericType        The generic type
 * @param name               The property name
 * @param readMethod         The read method
 * @param writeMethod        The write methodname
 * @param isReadOnly         Is the property read only
 * @param annotationMetadata The property annotation metadata
 * @param typeArguments      The type arguments
 */
void visitProperty(@NonNull TypedElement type, @NonNull TypedElement genericType, @NonNull String name, @Nullable MethodElement readMethod, @Nullable MethodElement writeMethod, boolean isReadOnly, @Nullable AnnotationMetadata annotationMetadata, @Nullable Map<String, ClassElement> typeArguments) {
    DefaultAnnotationMetadata.contributeDefaults(this.annotationMetadata, annotationMetadata);
    if (typeArguments != null) {
        for (ClassElement element : typeArguments.values()) {
            DefaultAnnotationMetadata.contributeRepeatable(this.annotationMetadata, element);
        }
    }
    int readMethodIndex = -1;
    if (readMethod != null) {
        readMethodIndex = dispatchWriter.addMethod(classElement, readMethod, true);
    }
    int writeMethodIndex = -1;
    int withMethodIndex = -1;
    if (writeMethod != null) {
        writeMethodIndex = dispatchWriter.addMethod(classElement, writeMethod, true);
    }
    boolean isMutable = !isReadOnly || hasAssociatedConstructorArgument(name, genericType);
    if (isMutable) {
        if (writeMethod == null) {
            final String prefix = this.annotationMetadata.stringValue(Introspected.class, "withPrefix").orElse("with");
            ElementQuery<MethodElement> elementQuery = ElementQuery.of(MethodElement.class).onlyAccessible().onlyDeclared().onlyInstance().named((n) -> n.startsWith(prefix) && n.equals(prefix + NameUtils.capitalize(name))).filter((methodElement -> {
                ParameterElement[] parameters = methodElement.getParameters();
                return parameters.length == 1 && methodElement.getGenericReturnType().getName().equals(classElement.getName()) && type.getType().isAssignable(parameters[0].getType());
            }));
            MethodElement withMethod = classElement.getEnclosedElement(elementQuery).orElse(null);
            if (withMethod != null) {
                withMethodIndex = dispatchWriter.addMethod(classElement, withMethod, true);
            } else {
                MethodElement constructor = this.constructor == null ? defaultConstructor : this.constructor;
                if (constructor != null) {
                    withMethodIndex = dispatchWriter.addDispatchTarget(new CopyConstructorDispatchTarget(constructor, name));
                }
            }
        }
    // Otherwise, set method would be used in BeanProperty
    } else {
        withMethodIndex = dispatchWriter.addDispatchTarget(new ExceptionDispatchTarget(UnsupportedOperationException.class, "Cannot mutate property [" + name + "] that is not mutable via a setter method or constructor argument for type: " + beanType.getClassName()));
    }
    beanProperties.add(new BeanPropertyData(genericType, name, annotationMetadata, typeArguments, readMethodIndex, writeMethodIndex, withMethodIndex, isReadOnly));
}
Also used : ElementQuery(io.micronaut.inject.ast.ElementQuery) Arrays(java.util.Arrays) ArrayUtils(io.micronaut.core.util.ArrayUtils) FieldElement(io.micronaut.inject.ast.FieldElement) Internal(io.micronaut.core.annotation.Internal) BeanIntrospectionReference(io.micronaut.core.beans.BeanIntrospectionReference) Type(org.objectweb.asm.Type) Map(java.util.Map) NameUtils(io.micronaut.core.naming.NameUtils) AbstractBeanIntrospectionReference(io.micronaut.core.beans.AbstractBeanIntrospectionReference) AbstractAnnotationMetadataWriter(io.micronaut.inject.writer.AbstractAnnotationMetadataWriter) AnnotationMetadataWriter(io.micronaut.inject.annotation.AnnotationMetadataWriter) AnnotationMetadataHierarchy(io.micronaut.inject.annotation.AnnotationMetadataHierarchy) Collection(java.util.Collection) Set(java.util.Set) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) Introspected(io.micronaut.core.annotation.Introspected) List(java.util.List) MethodElement(io.micronaut.inject.ast.MethodElement) JavaModelUtils(io.micronaut.inject.processing.JavaModelUtils) ClassWriter(org.objectweb.asm.ClassWriter) AbstractInitializableBeanIntrospection(io.micronaut.inject.beans.AbstractInitializableBeanIntrospection) Label(org.objectweb.asm.Label) ClassElement(io.micronaut.inject.ast.ClassElement) HashMap(java.util.HashMap) StringSwitchWriter(io.micronaut.inject.writer.StringSwitchWriter) DefaultAnnotationMetadata(io.micronaut.inject.annotation.DefaultAnnotationMetadata) ConstructorElement(io.micronaut.inject.ast.ConstructorElement) ParameterElement(io.micronaut.inject.ast.ParameterElement) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Method(org.objectweb.asm.commons.Method) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) TypedElement(io.micronaut.inject.ast.TypedElement) Nullable(io.micronaut.core.annotation.Nullable) AnnotationMetadataReference(io.micronaut.inject.annotation.AnnotationMetadataReference) BiConsumer(java.util.function.BiConsumer) Argument(io.micronaut.core.type.Argument) ClassWriterOutputVisitor(io.micronaut.inject.writer.ClassWriterOutputVisitor) LinkedHashSet(java.util.LinkedHashSet) OutputStream(java.io.OutputStream) Opcodes(org.objectweb.asm.Opcodes) IOException(java.io.IOException) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) NonNull(io.micronaut.core.annotation.NonNull) DispatchWriter(io.micronaut.inject.writer.DispatchWriter) BeanIntrospection(io.micronaut.core.beans.BeanIntrospection) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Collections(java.util.Collections) Introspected(io.micronaut.core.annotation.Introspected) MethodElement(io.micronaut.inject.ast.MethodElement) ClassElement(io.micronaut.inject.ast.ClassElement)

Example 14 with MethodElement

use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.

the class BeanDefinitionWriter method visitDefaultConstructor.

@Override
public void visitDefaultConstructor(AnnotationMetadata annotationMetadata, VisitorContext visitorContext) {
    if (this.constructor == null) {
        ClassElement bean = ClassElement.of(beanType.getClassName());
        MethodElement defaultConstructor = MethodElement.of(bean, annotationMetadata, bean, bean, "<init>");
        constructor = defaultConstructor;
        // now prepare the implementation of the build method. See BeanFactory interface
        visitBuildMethodDefinition(defaultConstructor, false);
        // now override the injectBean method
        visitInjectMethodDefinition();
    }
}
Also used : MethodElement(io.micronaut.inject.ast.MethodElement) ClassElement(io.micronaut.inject.ast.ClassElement)

Example 15 with MethodElement

use of io.micronaut.inject.ast.MethodElement in project micronaut-core by micronaut-projects.

the class BeanDefinitionWriter method visitBuildFactoryMethodDefinition.

private void visitBuildFactoryMethodDefinition(ClassElement factoryClass, Element factoryMethod) {
    if (buildMethodVisitor == null) {
        ParameterElement[] parameters;
        if (factoryMethod instanceof MethodElement) {
            parameters = ((MethodElement) factoryMethod).getParameters();
        } else {
            parameters = new ParameterElement[0];
        }
        List<ParameterElement> parameterList = Arrays.asList(parameters);
        boolean isParametrized = isParametrized(parameters);
        boolean isIntercepted = isConstructorIntercepted(factoryMethod);
        Type factoryType = JavaModelUtils.getTypeReference(factoryClass);
        defineBuilderMethod(isParametrized);
        // load this
        GeneratorAdapter buildMethodVisitor = this.buildMethodVisitor;
        // for Factory beans first we need to lookup the the factory bean
        // before invoking the method to instantiate
        // the below code looks up the factory bean.
        // Load the BeanContext for the method call
        buildMethodVisitor.loadArg(1);
        pushCastToType(buildMethodVisitor, DefaultBeanContext.class);
        // load the first argument of the method (the BeanResolutionContext) to be passed to the method
        buildMethodVisitor.loadArg(0);
        // second argument is the bean type
        buildMethodVisitor.push(factoryType);
        buildMethodVisitor.invokeVirtual(Type.getType(DefaultBeanContext.class), org.objectweb.asm.commons.Method.getMethod(METHOD_GET_BEAN));
        // store a reference to the bean being built at index 3
        int factoryVar = buildMethodVisitor.newLocal(JavaModelUtils.getTypeReference(factoryClass));
        buildMethodVisitor.storeLocal(factoryVar);
        buildMethodVisitor.loadLocal(factoryVar);
        pushCastToType(buildMethodVisitor, factoryClass);
        String methodDescriptor = getMethodDescriptorForReturnType(beanType, parameterList);
        boolean hasInjectScope = false;
        if (isIntercepted) {
            int constructorIndex = initInterceptedConstructorWriter(buildMethodVisitor, parameterList, new FactoryMethodDef(factoryType, factoryMethod, methodDescriptor, factoryVar));
            // populate an Object[] of all constructor arguments
            final int parametersIndex = createParameterArray(parameterList, buildMethodVisitor);
            invokeConstructorChain(buildMethodVisitor, constructorIndex, parametersIndex, parameterList);
        } else {
            if (!parameterList.isEmpty()) {
                hasInjectScope = pushConstructorArguments(buildMethodVisitor, parameters);
            }
            if (factoryMethod instanceof MethodElement) {
                buildMethodVisitor.visitMethodInsn(INVOKEVIRTUAL, factoryType.getInternalName(), factoryMethod.getName(), methodDescriptor, false);
            } else {
                buildMethodVisitor.getField(factoryType, factoryMethod.getName(), beanType);
            }
        }
        this.buildInstanceLocalVarIndex = buildMethodVisitor.newLocal(beanType);
        buildMethodVisitor.storeLocal(buildInstanceLocalVarIndex);
        if (!isPrimitiveBean) {
            pushBeanDefinitionMethodInvocation(buildMethodVisitor, "injectBean");
            pushCastToType(buildMethodVisitor, beanType);
            buildMethodVisitor.storeLocal(buildInstanceLocalVarIndex);
        }
        destroyInjectScopeBeansIfNecessary(buildMethodVisitor, hasInjectScope);
        buildMethodVisitor.loadLocal(buildInstanceLocalVarIndex);
        initLifeCycleMethodsIfNecessary();
    }
}
Also used : Type(org.objectweb.asm.Type) AdvisedBeanType(io.micronaut.inject.AdvisedBeanType) MethodElement(io.micronaut.inject.ast.MethodElement) GeneratorAdapter(org.objectweb.asm.commons.GeneratorAdapter) DefaultBeanContext(io.micronaut.context.DefaultBeanContext) ParameterElement(io.micronaut.inject.ast.ParameterElement) AbstractConstructorInjectionPoint(io.micronaut.context.AbstractConstructorInjectionPoint) ConstructorInjectionPoint(io.micronaut.inject.ConstructorInjectionPoint)

Aggregations

MethodElement (io.micronaut.inject.ast.MethodElement)18 ClassElement (io.micronaut.inject.ast.ClassElement)14 ParameterElement (io.micronaut.inject.ast.ParameterElement)12 Map (java.util.Map)12 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)11 FieldElement (io.micronaut.inject.ast.FieldElement)11 List (java.util.List)11 ArrayList (java.util.ArrayList)10 Collections (java.util.Collections)10 Set (java.util.Set)10 NonNull (io.micronaut.core.annotation.NonNull)9 ElementQuery (io.micronaut.inject.ast.ElementQuery)9 HashMap (java.util.HashMap)9 Type (org.objectweb.asm.Type)9 GeneratorAdapter (org.objectweb.asm.commons.GeneratorAdapter)9 Internal (io.micronaut.core.annotation.Internal)8 ConstructorElement (io.micronaut.inject.ast.ConstructorElement)8 Arrays (java.util.Arrays)8 HashSet (java.util.HashSet)8 ArrayUtils (io.micronaut.core.util.ArrayUtils)7