private void transformReferencesToThis()

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