-
Notifications
You must be signed in to change notification settings - Fork 193
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8337794
commit b4e715c
Showing
7 changed files
with
978 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
316 changes: 316 additions & 0 deletions
316
base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/InnerClassVisitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
/* | ||
* 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.FieldExpression; | ||
import org.codehaus.groovy.ast.expr.MethodCallExpression; | ||
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 groovyjarjarasm.asm.Opcodes; | ||
|
||
import java.util.ArrayList; | ||
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 corresponding 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 | ||
ClassNode enclosingType = (inClosure ? ClassHelper.CLOSURE_TYPE : outerClass).getPlainNodeReference(); | ||
expressions.add(pCount, new VariableExpression("this", enclosingType)); | ||
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, final ConstructorCallExpression call) { | ||
boolean isStatic = innerClass.isStaticClass(); | ||
if (!isStatic) { | ||
if (currentMethod != null) { | ||
if (currentMethod instanceof ConstructorNode) { | ||
ConstructorNode ctor = (ConstructorNode) currentMethod; | ||
final boolean[] precedesSuperOrThisCall = new boolean[1]; | ||
|
||
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); | ||
} | ||
for (Parameter param : ctor.getParameters()) { | ||
if (param.hasInitialExpression()) { | ||
param.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"); | ||
// GRECLIPSE add -- GROOVY-8104 | ||
if (i == 0 && classNode.getDeclaredField("this$0").getType().equals(ClassHelper.CLOSURE_TYPE)) | ||
this0 = new MethodCallExpression(this0, "getThisObject", MethodCallExpression.NO_ARGUMENTS); | ||
// GRECLIPSE end | ||
} | ||
argsListExp.getExpressions().add(0, this0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.