private static void applyTrait()

in src/main/java/org/codehaus/groovy/transform/trait/TraitComposer.java [120:303]


    private static void applyTrait(final ClassNode trait, final ClassNode cNode, final TraitHelpersTuple helpers, SourceUnit unit) {
        ClassNode helperClassNode = helpers.getHelper();
        ClassNode fieldHelperClassNode = helpers.getFieldHelper();
        ClassNode staticFieldHelperClassNode = helpers.getStaticFieldHelper();
        Map<String, ClassNode> genericsSpec = GenericsUtils.createGenericsSpec(trait, GenericsUtils.createGenericsSpec(cNode));

        for (MethodNode methodNode : helperClassNode.getMethods()) {
            String name = methodNode.getName();
            Parameter[] helperMethodParams = methodNode.getParameters();
            int nParams = helperMethodParams.length;
            if (nParams > 0 && methodNode.isStatic() && !methodNode.isAbstract() && !methodNode.isPrivate() // GROOVY-7213, GROOVY-8859
                    && (!name.contains("$") || (methodNode.getModifiers() & Opcodes.ACC_SYNTHETIC) == 0)) {
                ArgumentListExpression argList = new ArgumentListExpression();
                argList.addExpression(varX("this"));
                Parameter[] origParams = new Parameter[nParams - 1];
                Parameter[] params = new Parameter[nParams - 1];
                System.arraycopy(methodNode.getParameters(), 1, params, 0, params.length);

                MethodNode originalMethod = trait.getMethod(name, params);
                Map<String, ClassNode> methodGenericsSpec = GenericsUtils.addMethodGenerics(
                        Optional.ofNullable(originalMethod).orElse(methodNode), genericsSpec);

                for (int i = 1; i < nParams; i += 1) {
                    Parameter parameter = helperMethodParams[i];
                    ClassNode originType = parameter.getOriginType();
                    ClassNode fixedType = GenericsUtils.correctToGenericsSpecRecurse(methodGenericsSpec, originType);
                    Parameter newParam = new Parameter(fixedType, parameter.getName());
                    List<AnnotationNode> copied = new LinkedList<>();
                    List<AnnotationNode> notCopied = new LinkedList<>();
                    GeneralUtils.copyAnnotatedNodeAnnotations(parameter, copied, notCopied);
                    newParam.addAnnotations(copied);
                    params[i - 1] = newParam;
                    origParams[i - 1] = parameter;
                    argList.addExpression(varX(newParam));
                }
                createForwarderMethod(trait, cNode, methodNode, originalMethod, helperClassNode, methodGenericsSpec, helperMethodParams, origParams, params, argList, unit);
            }
        }

        MethodCallExpression staticInitCall = callX(
                classX(helperClassNode),
                Traits.STATIC_INIT_METHOD,
                classX(cNode));
        MethodNode staticInitMethod = new MethodNode(
                Traits.STATIC_INIT_METHOD, Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, ClassHelper.VOID_TYPE,
                new Parameter[] {new Parameter(ClassHelper.CLASS_Type,"clazz")}, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE);
        staticInitMethod.setDeclaringClass(helperClassNode);
        staticInitCall.setMethodTarget(staticInitMethod);
        cNode.addStaticInitializerStatements(Collections.singletonList(stmt(staticInitCall)), false);
        if (fieldHelperClassNode != null && !cNode.declaresInterface(fieldHelperClassNode)) {
            // we should implement the field helper interface too
            cNode.addInterface(fieldHelperClassNode);
            // implementation of methods
            List<MethodNode> declaredMethods = new LinkedList<>();
            int pos = 0; // keep direct getters at start but in declaration order
            for (MethodNode declaredMethod : fieldHelperClassNode.getMethods()) {
                if (declaredMethod.getName().endsWith(Traits.DIRECT_GETTER_SUFFIX)) {
                    declaredMethods.add(pos++, declaredMethod);
                } else {
                    declaredMethods.add(declaredMethod);
                }
            }

            if (staticFieldHelperClassNode != null) {
                for (MethodNode declaredMethod : staticFieldHelperClassNode.getMethods()) {
                    if (declaredMethod.getName().endsWith(Traits.DIRECT_GETTER_SUFFIX)) {
                        declaredMethods.add(pos++, declaredMethod);
                    } else {
                        declaredMethods.add(declaredMethod);
                    }
                }
            }

            for (MethodNode methodNode : declaredMethods) {
                String fieldName = methodNode.getName();
                if (fieldName.endsWith(Traits.DIRECT_GETTER_SUFFIX) || fieldName.endsWith(Traits.DIRECT_SETTER_SUFFIX)) {
                    int suffixIdx = fieldName.lastIndexOf('$');
                    fieldName = fieldName.substring(0, suffixIdx);
                    String operation = methodNode.getName().substring(suffixIdx + 1);
                    boolean getter = "get".equals(operation);
                    ClassNode returnType = GenericsUtils.correctToGenericsSpecRecurse(genericsSpec, methodNode.getReturnType());
                    int fieldMods = 0;
                    int isStatic = 0;
                    boolean publicField = true;
                    FieldNode helperField = null;
                    fieldMods = 0;
                    isStatic = 0;

                    // look first for field with encoded modifier information
                    for (Integer mod : Traits.FIELD_PREFIXES) {
                        helperField = fieldHelperClassNode.getField(String.format("$0x%04x", mod) + fieldName);
                        if (helperField != null) {
                            if ((mod & Opcodes.ACC_STATIC) != 0) isStatic = Opcodes.ACC_STATIC;
                            fieldMods = fieldMods | mod;
                            break;
                        }
                    }

                    if (helperField == null) {
                        // look for possible legacy fields (trait compiled pre 2.4.8)
                        helperField = fieldHelperClassNode.getField(Traits.FIELD_PREFIX + Traits.PUBLIC_FIELD_PREFIX + fieldName);
                        if (helperField == null) {
                            publicField = false;
                            helperField = fieldHelperClassNode.getField(Traits.FIELD_PREFIX + Traits.PRIVATE_FIELD_PREFIX + fieldName);
                        }
                        if (helperField == null) {
                            publicField = true;
                            // try to find a static one
                            helperField = fieldHelperClassNode.getField(Traits.STATIC_FIELD_PREFIX+Traits.PUBLIC_FIELD_PREFIX + fieldName);
                            if (helperField == null) {
                                publicField = false;
                                helperField = fieldHelperClassNode.getField(Traits.STATIC_FIELD_PREFIX+Traits.PRIVATE_FIELD_PREFIX + fieldName);
                            }
                            fieldMods = fieldMods | Opcodes.ACC_STATIC;
                            isStatic = Opcodes.ACC_STATIC;
                        }
                        fieldMods = fieldMods | (publicField?Opcodes.ACC_PUBLIC:Opcodes.ACC_PRIVATE);
                    }
                    if (getter) {
                        // add field
                        if (helperField!=null) {
                            List<AnnotationNode> copied = new LinkedList<>();
                            List<AnnotationNode> notCopied = new LinkedList<>();
                            GeneralUtils.copyAnnotatedNodeAnnotations(helperField, copied, notCopied);
                            FieldNode fieldNode = cNode.addField(fieldName, fieldMods, returnType, null);
                            fieldNode.addAnnotations(copied);
                            // getInitialExpression above will be null if not in same source unit
                            // so instead set within (static) initializer
                            if (fieldNode.isFinal()) {
                                String baseName = fieldNode.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD;
                                StaticMethodCallExpression mce = callX(helperClassNode, baseName + fieldNode.getName(), args(varX("this")));
                                if (helperClassNode.hasPossibleStaticMethod(mce.getMethod(), mce.getArguments())) {
                                    Statement stmt = stmt(assignX(varX(fieldNode.getName(), fieldNode.getType()), mce));
                                    if (isStatic == 0) {
                                        cNode.addObjectInitializerStatements(stmt);
                                    } else {
                                        List<Statement> staticStatements = new ArrayList<>();
                                        staticStatements.add(stmt);
                                        cNode.addStaticInitializerStatements(staticStatements, true);
                                    }
                                }
                            }
                        }
                    }
                    Parameter[] newParams;
                    if (getter) {
                        newParams = Parameter.EMPTY_ARRAY;
                    } else {
                        ClassNode originType = methodNode.getParameters()[0].getOriginType();
                        ClassNode fixedType = originType.isGenericsPlaceHolder()?ClassHelper.OBJECT_TYPE:GenericsUtils.correctToGenericsSpecRecurse(genericsSpec, originType);
                        newParams = new Parameter[]{new Parameter(fixedType, "val")};
                    }

                    Expression fieldExpr = varX(cNode.getField(fieldName));
                    boolean finalSetter = !getter && (fieldMods & Opcodes.ACC_FINAL) != 0;
                    Statement body =
                            getter ? returnS(fieldExpr) :
                                    (finalSetter ? null : stmt(
                                            assignX(
                                                    fieldExpr,
                                                    varX(newParams[0])
                                            )
                                    ));
                    // add getter/setter even though setter not strictly needed for final fields
                    // but add empty body for setter for legacy compatibility
                    MethodNode impl = new MethodNode(
                            methodNode.getName(),
                            Opcodes.ACC_PUBLIC | isStatic,
                            returnType,
                            newParams,
                            ClassNode.EMPTY_ARRAY,
                            body
                    );
                    AnnotationNode an = new AnnotationNode(COMPILESTATIC_CLASSNODE);
                    impl.addAnnotation(an);
                    cNode.addTransform(StaticCompileTransformation.class, an);
                    addGeneratedMethod(cNode, impl);
                }
            }
        }
        cNode.addObjectInitializerStatements(stmt(
                callX(classX(helperClassNode), Traits.INIT_METHOD, varX("this"))
        ));
    }