in src/main/java/org/codehaus/groovy/transform/CategoryASTTransformation.java [115:259]
private void transformReferencesToThis(final ClassNode targetClass, final ClassNode sourceClass, final SourceUnit sourceUnit) {
final Reference<Parameter> selfParameter = new Reference<>();
final LinkedList<Set<String>> varStack = new LinkedList<>();
Set<String> names = new HashSet<>();
for (FieldNode fn : sourceClass.getFields()) names.add(fn.getName());
for (PropertyNode pn : sourceClass.getProperties()) names.add(pn.getName());
varStack.add(names);
ClassCodeExpressionTransformer transformer = new ClassCodeExpressionTransformer() {
private boolean inClosure; // GROOVY-6510: track closure containment
private void addVariablesToStack(final Parameter[] parameter) {
Set<String> names = new HashSet<>(varStack.getLast());
for (Parameter p : parameter) names.add(p.getName());
varStack.add(names);
}
private Expression createThisExpression() {
VariableExpression ve = new VariableExpression("$this", targetClass);
ve.setClosureSharedVariable(true);
return ve;
}
@Override
protected SourceUnit getSourceUnit() {
return sourceUnit;
}
@Override
public Expression transform(final Expression expression) {
if (expression instanceof VariableExpression) {
VariableExpression ve = (VariableExpression) expression;
if (ve.isThisExpression()) {
Expression thisExpression = createThisExpression();
thisExpression.setSourcePosition(ve);
return thisExpression;
} else if (!inClosure && !ve.isSuperExpression() && !varStack.getLast().contains(ve.getName())) {
PropertyExpression pe = new PropertyExpression(createThisExpression(), ve.getName());
pe.setSourcePosition(ve);
return pe;
}
} else if (expression instanceof MethodCallExpression) {
MethodCallExpression mce = (MethodCallExpression) expression;
if (inClosure && mce.isImplicitThis() && isThisExpression(mce.getObjectExpression())) {
// GROOVY-6510: preserve implicit-this semantics
mce.setArguments(transform(mce.getArguments()));
mce.setMethod(transform(mce.getMethod()));
return mce;
}
} else if (expression instanceof ClosureExpression) {
ClosureExpression ce = (ClosureExpression) expression;
addVariablesToStack(hasImplicitParameter(ce) ? params(param(ClassHelper.OBJECT_TYPE, "it")) : getParametersSafe(ce));
ce.getVariableScope().putReferencedLocalVariable(selfParameter.get());
addAll(varStack.getLast(), "owner", "delegate", "thisObject");
boolean closure = inClosure; inClosure = true;
ce.getCode().visit(this);
varStack.removeLast();
inClosure = closure;
}
return super.transform(expression);
}
@Override
public void visitBlockStatement(final BlockStatement statement) {
Set<String> names = new HashSet<>(varStack.getLast());
varStack.add(names);
super.visitBlockStatement(statement);
varStack.remove(names);
}
@Override
public void visitCatchStatement(final CatchStatement statement) {
varStack.getLast().add(statement.getVariable().getName());
super.visitCatchStatement(statement);
varStack.getLast().remove(statement.getVariable().getName());
}
@Override
public void visitClosureExpression(final ClosureExpression expression) {
}
@Override
public void visitDeclarationExpression(final DeclarationExpression expression) {
if (expression.isMultipleAssignmentDeclaration()) {
for (Expression e : expression.getTupleExpression().getExpressions()) {
VariableExpression ve = (VariableExpression) e;
varStack.getLast().add(ve.getName());
}
} else {
VariableExpression ve = expression.getVariableExpression();
varStack.getLast().add(ve.getName());
}
super.visitDeclarationExpression(expression);
}
@Override
public void visitExpressionStatement(final ExpressionStatement statement) {
// GROOVY-3543: visit the declaration expressions so that declaration variables get added on the varStack
if (statement.getExpression() instanceof DeclarationExpression) {
statement.getExpression().visit(this);
}
super.visitExpressionStatement(statement);
}
@Override
public void visitForLoop(final ForStatement statement) {
Expression exp = statement.getCollectionExpression();
exp.visit(this);
Parameter loopParam = statement.getIndexVariable();
if (loopParam != null) {
varStack.getLast().add(loopParam.getName());
}
loopParam = statement.getValueVariable();
if (loopParam != null) {
varStack.getLast().add(loopParam.getName());
}
super.visitForLoop(statement);
}
@Override
public void visitMethod(final MethodNode node) {
addVariablesToStack(node.getParameters());
super.visitMethod(node);
varStack.removeLast();
}
};
for (MethodNode method : sourceClass.getMethods()) {
if (!method.isStatic()) {
Parameter p = new Parameter(targetClass, "$this");
p.setClosureSharedVariable(true);
selfParameter.set(p);
Parameter[] oldParams = method.getParameters();
Parameter[] newParams = new Parameter[oldParams.length + 1];
newParams[0] = p;
System.arraycopy(oldParams, 0, newParams, 1, oldParams.length);
method.setModifiers(method.getModifiers() | Opcodes.ACC_STATIC);
method.setParameters(newParams);
transformer.visitMethod(method);
}
}
}