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"))
));
}