Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.codehaus.groovy.classgen; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.CodeVisitorSupport; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GroovyCodeVisitor; import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.Variable; import org.codehaus.groovy.ast.VariableScope; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.expr.TupleExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.BlockStatement; import org.codehaus.groovy.ast.stmt.ExpressionStatement; import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.transform.trait.Traits; import org.objectweb.asm.Opcodes; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcodes { private ClassNode classNode; private FieldNode currentField; private MethodNode currentMethod; private final SourceUnit sourceUnit; private boolean inClosure, processingObjInitStatements; public InnerClassVisitor(CompilationUnit cu, SourceUnit su) { sourceUnit = su; } @Override protected SourceUnit getSourceUnit() { return sourceUnit; } @Override public void visitClass(ClassNode node) { classNode = node; InnerClassNode innerClass = null; if (!node.isEnum() && !node.isInterface() && node instanceof InnerClassNode) { innerClass = (InnerClassNode) node; if (innerClass.getVariableScope() == null && (innerClass.getModifiers() & ACC_STATIC) == 0) { innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, node.getOuterClass().getPlainNodeReference(), null); } } super.visitClass(node); if (node.isEnum() || node.isInterface()) return; if (innerClass == null) return; if (node.getSuperClass().isInterface() || Traits.isAnnotatedWithTrait(node.getSuperClass())) { node.addInterface(node.getUnresolvedSuperClass()); node.setUnresolvedSuperClass(ClassHelper.OBJECT_TYPE); } } @Override public void visitClosureExpression(ClosureExpression expression) { boolean inClosureOld = inClosure; inClosure = true; super.visitClosureExpression(expression); inClosure = inClosureOld; } @Override protected void visitObjectInitializerStatements(ClassNode node) { processingObjInitStatements = true; super.visitObjectInitializerStatements(node); processingObjInitStatements = false; } @Override protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { currentMethod = node; visitAnnotations(node); visitClassCodeContainer(node.getCode()); // GROOVY-5681: initial expressions should be visited too! for (Parameter param : node.getParameters()) { if (param.hasInitialExpression()) { param.getInitialExpression().visit(this); } visitAnnotations(param); } currentMethod = null; } @Override public void visitField(FieldNode node) { currentField = node; super.visitField(node); currentField = null; } @Override public void visitProperty(PropertyNode node) { final FieldNode field = node.getField(); final Expression init = field.getInitialExpression(); field.setInitialValueExpression(null); super.visitProperty(node); field.setInitialValueExpression(init); } @Override public void visitConstructorCallExpression(ConstructorCallExpression call) { super.visitConstructorCallExpression(call); if (!call.isUsingAnonymousInnerClass()) { passThisReference(call); return; } InnerClassNode innerClass = (InnerClassNode) call.getType(); ClassNode outerClass = innerClass.getOuterClass(); ClassNode superClass = innerClass.getSuperClass(); if (!superClass.isInterface() && superClass.getOuterClass() != null && !(superClass.isStaticClass() || (superClass.getModifiers() & ACC_STATIC) != 0)) { insertThis0ToSuperCall(call, innerClass); } if (!innerClass.getDeclaredConstructors().isEmpty()) return; if ((innerClass.getModifiers() & ACC_STATIC) != 0) return; VariableScope scope = innerClass.getVariableScope(); if (scope == null) return; boolean isStatic = !inClosure && isStatic(innerClass, scope, call); // expressions = constructor call arguments List<Expression> expressions = ((TupleExpression) call.getArguments()).getExpressions(); // block = init code for the constructor we produce BlockStatement block = new BlockStatement(); // parameters = parameters of the constructor int additionalParamCount = (isStatic ? 0 : 1) + scope.getReferencedLocalVariablesCount(); List<Parameter> parameters = new ArrayList<>(expressions.size() + additionalParamCount); // superCallArguments = arguments for the super call == the constructor call arguments List<Expression> superCallArguments = new ArrayList<>(expressions.size()); // first we add a super() call for all expressions given in the constructor call expression for (int i = 0, n = expressions.size(); i < n; i += 1) { // add one parameter for each expression in the constructor call Parameter param = new Parameter(ClassHelper.OBJECT_TYPE, "p" + additionalParamCount + i); parameters.add(param); // add the corresponsing argument to the super constructor call superCallArguments.add(new VariableExpression(param)); } // add the super call ConstructorCallExpression cce = new ConstructorCallExpression(ClassNode.SUPER, new TupleExpression(superCallArguments)); block.addStatement(new ExpressionStatement(cce)); int pCount = 0; if (!isStatic) { // need to pass "this" to access unknown methods/properties expressions.add(pCount, VariableExpression.THIS_EXPRESSION); ClassNode enclosingType = (inClosure ? ClassHelper.CLOSURE_TYPE : outerClass).getPlainNodeReference(); Parameter thisParameter = new Parameter(enclosingType, "p" + pCount); parameters.add(pCount++, thisParameter); // "this" reference is saved in a field named "this$0" FieldNode thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, enclosingType, null); addFieldInit(thisParameter, thisField, block); } // for each shared variable, add a Reference field for (Iterator<Variable> it = scope.getReferencedLocalVariablesIterator(); it.hasNext();) { Variable var = it.next(); VariableExpression ve = new VariableExpression(var); ve.setClosureSharedVariable(true); ve.setUseReferenceDirectly(true); expressions.add(pCount, ve); ClassNode referenceType = ClassHelper.REFERENCE_TYPE.getPlainNodeReference(); Parameter p = new Parameter(referenceType, "p" + pCount); p.setOriginType(var.getOriginType()); parameters.add(pCount++, p); VariableExpression initial = new VariableExpression(p); initial.setSynthetic(true); initial.setUseReferenceDirectly(true); FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, referenceType, initial); pField.setHolder(true); pField.setOriginType(ClassHelper.getWrapper(var.getOriginType())); } innerClass.addConstructor(ACC_SYNTHETIC, parameters.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, block); } private boolean isStatic(InnerClassNode innerClass, VariableScope scope, ConstructorCallExpression call) { boolean isStatic = innerClass.isStaticClass(); if (!isStatic) { if (currentMethod != null) { if (currentMethod instanceof ConstructorNode) { boolean[] precedesSuperOrThisCall = new boolean[1]; ConstructorNode ctor = (ConstructorNode) currentMethod; GroovyCodeVisitor visitor = new CodeVisitorSupport() { @Override public void visitConstructorCallExpression(ConstructorCallExpression cce) { if (cce == call) { precedesSuperOrThisCall[0] = true; } else { super.visitConstructorCallExpression(cce); } } }; if (ctor.firstStatementIsSpecialConstructorCall()) currentMethod.getFirstStatement().visit(visitor); Arrays.stream(ctor.getParameters()).filter(Parameter::hasInitialExpression) .forEach(p -> p.getInitialExpression().visit(visitor)); isStatic = precedesSuperOrThisCall[0]; } else { isStatic = currentMethod.isStatic(); } } else if (currentField != null) { isStatic = currentField.isStatic(); } } return isStatic; } // this is the counterpart of addThisReference(). To non-static inner classes, outer this should be // passed as the first argument implicitly. private void passThisReference(ConstructorCallExpression call) { ClassNode cn = call.getType().redirect(); if (!shouldHandleImplicitThisForInnerClass(cn)) return; boolean isInStaticContext = true; if (currentMethod != null) isInStaticContext = currentMethod.getVariableScope().isInStaticContext(); else if (currentField != null) isInStaticContext = currentField.isStatic(); else if (processingObjInitStatements) isInStaticContext = false; // if constructor call is not in static context, return if (isInStaticContext) { // constructor call is in static context and the inner class is non-static - 1st arg is supposed to be // passed as enclosing "this" instance // Expression args = call.getArguments(); if (args instanceof TupleExpression && ((TupleExpression) args).getExpressions().isEmpty()) { addError("No enclosing instance passed in constructor call of a non-static inner class", call); } return; } insertThis0ToSuperCall(call, cn); } private void insertThis0ToSuperCall(final ConstructorCallExpression call, final ClassNode cn) { // calculate outer class which we need for this$0 ClassNode parent = classNode; int level = 0; for (; parent != null && parent != cn.getOuterClass(); parent = parent.getOuterClass()) { level++; } // if constructor call is not in outer class, don't pass 'this' implicitly. Return. if (parent == null) return; //add this parameter to node Expression argsExp = call.getArguments(); if (argsExp instanceof TupleExpression) { TupleExpression argsListExp = (TupleExpression) argsExp; Expression this0 = VariableExpression.THIS_EXPRESSION; for (int i = 0; i != level; ++i) this0 = new PropertyExpression(this0, "this$0"); argsListExp.getExpressions().add(0, this0); } } }