in src/main/java/org/codehaus/groovy/transform/trait/TraitASTTransformation.java [519:625]
private void processField(final FieldNode field, final MethodNode initializer, final MethodNode staticInitializer,
final ClassNode fieldHelper, final ClassNode helper, final ClassNode staticFieldHelper, final ClassNode trait,
final Set<String> knownFields) {
if (field.isProtected()) {
sourceUnit.addError(new SyntaxException("Cannot have protected field in a trait (" + trait.getName() + "#" + field.getName() + ")",
field.getLineNumber(), field.getColumnNumber()));
return;
}
Expression initialExpression = field.getInitialExpression();
MethodNode selectedMethod = field.isStatic() ? staticInitializer : initializer;
ClassNode target = field.isStatic() && staticFieldHelper != null ? staticFieldHelper : fieldHelper;
if (initialExpression != null) {
VariableExpression thisObject = varX(selectedMethod.getParameters()[0]);
ExpressionStatement initCode = new ExpressionStatement(initialExpression);
processBody(thisObject, initCode, trait, helper, fieldHelper, knownFields);
if (field.isFinal()) {
String baseName = field.isStatic() ? Traits.STATIC_INIT_METHOD : Traits.INIT_METHOD;
MethodNode fieldInitializer = new MethodNode(
baseName + Traits.remappedFieldName(trait, field.getName()),
ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC,
field.getOriginType(),
new Parameter[]{createSelfParameter(trait, field.isStatic())},
ClassNode.EMPTY_ARRAY,
returnS(initCode.getExpression())
);
helper.addMethod(fieldInitializer);
} else {
BlockStatement code = (BlockStatement) selectedMethod.getCode();
MethodCallExpression mce;
if (field.isStatic()) {
if (staticFieldHelper != null) {
target = staticFieldHelper;
}
mce = callX(
classX(InvokerHelper.class),
"invokeStaticMethod",
args(
thisObject,
constX(Traits.helperSetterName(field)),
initCode.getExpression()
)
);
} else {
mce = callX(
castX(createReceiverType(field.isStatic(), fieldHelper), thisObject),
Traits.helperSetterName(field),
castX(field.getOriginType(), initCode.getExpression())
);
}
mce.setImplicitThis(false);
mce.setSourcePosition(initialExpression);
code.addStatement(stmt(mce));
}
}
// define setter/getter helper methods (setter added even for final fields for legacy compatibility)
addGeneratedMethod(target,
Traits.helperSetterName(field),
ACC_PUBLIC | ACC_ABSTRACT,
field.getOriginType(),
new Parameter[]{new Parameter(field.getOriginType(), "val")},
ClassNode.EMPTY_ARRAY,
null
);
addGeneratedMethod(target,
Traits.helperGetterName(field),
ACC_PUBLIC | ACC_ABSTRACT,
field.getOriginType(),
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
null
);
// dummy fields are only used to carry annotations if instance field
// and to differentiate from static fields otherwise
int mods = field.getModifiers() & Traits.FIELD_PREFIX_MASK;
String dummyFieldName = String.format("$0x%04x", mods) + Traits.remappedFieldName(field.getOwner(), field.getName());
FieldNode dummyField = new FieldNode(
dummyFieldName,
ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC,
field.getOriginType(),
fieldHelper,
null
);
dummyField.setSynthetic(true);
// copy annotations from field to dummy field
List<AnnotationNode> copy = new ArrayList<>();
List<AnnotationNode> skip = new ArrayList<>();
GeneralUtils.copyAnnotatedNodeAnnotations(field, copy, skip);
dummyField.addAnnotations(copy);
fieldHelper.addField(dummyField);
// retain legacy field (will be given lower precedence than above)
dummyFieldName = (field.isStatic() ? Traits.STATIC_FIELD_PREFIX : Traits.FIELD_PREFIX) +
(field.isPublic() ? Traits.PUBLIC_FIELD_PREFIX : Traits.PRIVATE_FIELD_PREFIX) +
Traits.remappedFieldName(field.getOwner(), field.getName());
dummyField = new FieldNode(
dummyFieldName,
ACC_PUBLIC | ACC_STATIC | ACC_FINAL | ACC_SYNTHETIC,
field.getOriginType().getPlainNodeReference(),
fieldHelper,
null
);
dummyField.setSynthetic(true);
dummyField.addAnnotations(copy);
fieldHelper.addField(dummyField);
}