You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2020/05/29 17:51:56 UTC
[groovy] branch GROOVY-9499 updated (ad95958 -> 98c0022)
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a change to branch GROOVY-9499
in repository https://gitbox.apache.org/repos/asf/groovy.git.
discard ad95958 GROOVY-9499: AIC as argument to this/super constructor call
add 52fa307 GROOVY-6809 and GROOVY-9168: AIC before delegate constructor call (port to 2_5_X)
add 0ad802f tweak comment
new 98c0022 GROOVY-9499: AIC as argument to this/super constructor call
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (ad95958)
\
N -- N -- N refs/heads/GROOVY-9499 (98c0022)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../classgen/InnerClassCompletionVisitor.java | 2 +-
.../groovy/classgen/InnerClassVisitor.java | 33 ++++++++++------------
.../org/codehaus/groovy/classgen/Verifier.java | 11 ++++----
.../vmplugin/v5/PluginDefaultGroovyMethods.java | 10 +++----
4 files changed, 26 insertions(+), 30 deletions(-)
[groovy] 01/01: GROOVY-9499: AIC as argument to this/super
constructor call
Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-9499
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 98c0022dceb99b7e7c859b90677ed825b839c27b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri May 29 12:30:16 2020 -0500
GROOVY-9499: AIC as argument to this/super constructor call
Groovy 2.5 backport
---
.../groovy/classgen/InnerClassVisitor.java | 136 ++--
.../groovy/classgen/InnerClassVisitorHelper.java | 6 +-
.../org/codehaus/groovy/classgen/Verifier.java | 1 -
src/test/gls/innerClass/InnerClassTest.groovy | 901 +++++++++++++--------
4 files changed, 643 insertions(+), 401 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
index 90dd015..e90eb2f 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
@@ -44,22 +44,16 @@ 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;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
-import static org.codehaus.groovy.ast.tools.GenericsUtils.nonGeneric;
-
public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcodes {
- private final SourceUnit sourceUnit;
private ClassNode classNode;
- private FieldNode thisField;
- private MethodNode currentMethod;
private FieldNode currentField;
- private boolean processingObjInitStatements;
- private boolean inClosure;
+ private MethodNode currentMethod;
+ private final SourceUnit sourceUnit;
+ private boolean inClosure, processingObjInitStatements;
public InnerClassVisitor(CompilationUnit cu, SourceUnit su) {
sourceUnit = su;
@@ -73,12 +67,11 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
@Override
public void visitClass(ClassNode node) {
classNode = node;
- thisField = null;
InnerClassNode innerClass = null;
if (!node.isEnum() && !node.isInterface() && node instanceof InnerClassNode) {
innerClass = (InnerClassNode) node;
- if (!isStatic(innerClass) && innerClass.getVariableScope() == null) {
- thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, node.getOuterClass().getPlainNodeReference(), null);
+ if (innerClass.getVariableScope() == null && (innerClass.getModifiers() & ACC_STATIC) == 0) {
+ innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, node.getOuterClass().getPlainNodeReference(), null);
}
}
@@ -140,7 +133,7 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
}
@Override
- public void visitConstructorCallExpression(final ConstructorCallExpression call) {
+ public void visitConstructorCallExpression(ConstructorCallExpression call) {
super.visitConstructorCallExpression(call);
if (!call.isUsingAnonymousInnerClass()) {
passThisReference(call);
@@ -159,49 +152,24 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
VariableScope scope = innerClass.getVariableScope();
if (scope == null) return;
-
- int additionalParamCount = scope.getReferencedLocalVariablesCount();
- final boolean[] precedesSuperOrThisCall = new boolean[1];
- if (currentMethod instanceof ConstructorNode) {
- 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);
- for (Parameter p : ctor.getParameters()) {
- if (p.hasInitialExpression()) {
- p.getInitialExpression().visit(visitor);
- }
- }
- }
- if (!precedesSuperOrThisCall[0]) additionalParamCount += 1;
+ 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
- int pCount = additionalParamCount;
- for (Expression expr : expressions) {
- pCount++;
- // add one parameter for each expression in the
- // constructor call
- Parameter param = new Parameter(ClassHelper.OBJECT_TYPE, "p" + pCount);
+ // 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 to super call
+ // add the corresponsing argument to the super constructor call
superCallArguments.add(new VariableExpression(param));
}
@@ -213,45 +181,38 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
block.addStatement(new ExpressionStatement(cce));
- pCount = 0;
- boolean isStatic = isStaticThis(innerClass, scope);
- ClassNode outerClassType;
- if (!isStatic && inClosure) {
- outerClassType = ClassHelper.CLOSURE_TYPE.getPlainNodeReference();
- } else {
- outerClassType = getClassNode(outerClass, isStatic).getPlainNodeReference();
- }
- if (!precedesSuperOrThisCall[0]) {
+ int pCount = 0;
+ if (!isStatic) {
// need to pass "this" to access unknown methods/properties
expressions.add(pCount, VariableExpression.THIS_EXPRESSION);
- Parameter thisParameter = new Parameter(outerClassType, "p" + pCount);
- parameters.add(pCount, thisParameter);
+ 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"
- thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, outerClassType, null);
+ FieldNode thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, enclosingType, null);
addFieldInit(thisParameter, thisField, block);
- }/* else {
- thisField = innerClass.addField("this$0", ACC_FINAL | ACC_SYNTHETIC, nonGeneric(ClassHelper.CLASS_Type), classX(outerClassType));
- }*/
+ }
// for each shared variable, add a Reference field
for (Iterator<Variable> it = scope.getReferencedLocalVariablesIterator(); it.hasNext();) {
- pCount++;
Variable var = it.next();
+
VariableExpression ve = new VariableExpression(var);
ve.setClosureSharedVariable(true);
ve.setUseReferenceDirectly(true);
expressions.add(pCount, ve);
- ClassNode rawReferenceType = ClassHelper.REFERENCE_TYPE.getPlainNodeReference();
- Parameter p = new Parameter(rawReferenceType, "p" + pCount);
- parameters.add(pCount, p);
+ 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, rawReferenceType, initial);
+ FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, referenceType, initial);
pField.setHolder(true);
pField.setOriginType(ClassHelper.getWrapper(var.getOriginType()));
}
@@ -259,17 +220,42 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
innerClass.addConstructor(ACC_SYNTHETIC, parameters.toArray(Parameter.EMPTY_ARRAY), ClassNode.EMPTY_ARRAY, block);
}
- private boolean isStaticThis(InnerClassNode innerClass, VariableScope scope) {
- if (inClosure) return false;
- boolean ret = innerClass.isStaticClass();
- if (innerClass.getEnclosingMethod() != null) {
- ret = ret || innerClass.getEnclosingMethod().isStatic();
- } else if (currentField != null) {
- ret = ret || currentField.isStatic();
- } else if (currentMethod != null && "<clinit>".equals(currentMethod.getName())) {
- ret = true;
+ 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 ret;
+ return isStatic;
}
// this is the counterpart of addThisReference(). To non-static inner classes, outer this should be
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
index 9452768..4b67f23 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
@@ -24,7 +24,6 @@ import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.VariableScope;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
@@ -46,6 +45,7 @@ import java.util.ArrayList;
import java.util.List;
public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
+
protected static void setPropertyGetterDispatcher(BlockStatement block, Expression thiz, Parameter[] parameters) {
List<ConstantExpression> gStringStrings = new ArrayList<ConstantExpression>();
gStringStrings.add(new ConstantExpression(""));
@@ -102,9 +102,7 @@ public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
}
protected static boolean isStatic(InnerClassNode node) {
- VariableScope scope = node.getVariableScope();
- if (scope != null) return scope.getParent().isInStaticContext();
- return (node.getModifiers() & Opcodes.ACC_STATIC) != 0;
+ return node.getDeclaredField("this$0") == null;
}
protected static ClassNode getClassNode(ClassNode node, boolean isStatic) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 4a7f394..10697b6 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -57,7 +57,6 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.ast.tools.GenericsUtils;
-import org.codehaus.groovy.ast.tools.PropertyNodeUtils;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.MopWriter;
import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.ClassNodeSkip;
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy b/src/test/gls/innerClass/InnerClassTest.groovy
index ab548b6..b6b5e8e 100644
--- a/src/test/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/gls/innerClass/InnerClassTest.groovy
@@ -18,12 +18,20 @@
*/
package gls.innerClass
-import gls.CompilableTestSupport
+import groovy.transform.CompileStatic
+import groovy.transform.NotYetImplemented
+import org.codehaus.groovy.control.CompilationFailedException
+import org.junit.Test
-class InnerClassTest extends CompilableTestSupport {
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+@CompileStatic
+final class InnerClassTest {
+
+ @Test
void testTimerAIC() {
- assertScript """
+ assertScript '''
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@@ -31,42 +39,85 @@ class InnerClassTest extends CompilableTestSupport {
Timer timer = new Timer()
timer.schedule(new TimerTask() {
+ @Override
void run() {
called.countDown()
}
}, 0)
assert called.await(10, TimeUnit.SECONDS)
- """
+ '''
}
- void testAICReferenceInClosure() {
- assertScript """
- def y = [true]
+ @Test
+ void testAccessLocalVariableFromClosureInAIC() {
+ assertScript '''
+ def x = [true]
def o = new Object() {
- def foo() {
- def c = {
- assert y[0]
+ def m() {
+ def c = { ->
+ assert x[0]
+ }
+ c()
}
- c()
- }
}
- o.foo()
- """
+ o.m()
+ '''
+
+ shouldFail '''
+ def x = [false]
+ def o = new Object() {
+ def m() {
+ def c = { ->
+ assert x[0]
+ }
+ c()
+ }
+ }
+ o.m()
+ '''
}
- void testExtendsObjectAndAccessAFinalVariableInScope() {
- assertScript """
+ @Test
+ void testAccessFinalLocalVariableFromMethodInAIC() {
+ assertScript '''
final String objName = "My name is Guillaume"
assert new Object() {
String toString() { objName }
}.toString() == objName
- """
+ '''
+ }
+
+ @Test // GROOVY-9499
+ void testAccessStaticMethodFromAICInSuperCtorCall() {
+ assertScript '''
+ class One {
+ One(ref) {
+ HASH_CODE = ref.hashCode()
+ }
+ public static int HASH_CODE
+ }
+
+ class Two extends One {
+ Two() {
+ super(new Object() { // AIC before special ctor call completes
+ int hashCode() {
+ hash() // should be able to call static method safely
+ }
+ })
+ }
+ static int hash() { 42 }
+ }
+
+ def obj = new Two()
+ assert One.HASH_CODE == 42
+ '''
}
- void testExtendsObjectAndReferenceAMethodParameterWithinAGString() {
- assertScript """
+ @Test
+ void testAccessMethodParameterFromGStringInAICMethod() {
+ assertScript '''
Object makeObj0(String name) {
new Object() {
String toString() { "My name is \${name}" }
@@ -74,11 +125,12 @@ class InnerClassTest extends CompilableTestSupport {
}
assert makeObj0("Guillaume").toString() == "My name is Guillaume"
- """
+ '''
}
- void testExtendsObjectAndReferenceAGStringPropertyDependingOnAMethodParameter() {
- assertScript """
+ @Test
+ void testAccessMethodParameterFromGStringInAICProperty() {
+ assertScript '''
Object makeObj1(String name) {
new Object() {
String objName = "My name is \${name}"
@@ -88,11 +140,12 @@ class InnerClassTest extends CompilableTestSupport {
}
assert makeObj1("Guillaume").toString() == "My name is Guillaume"
- """
+ '''
}
+ @Test
void testUsageOfInitializerBlockWithinAnAIC() {
- assertScript """
+ assertScript '''
Object makeObj2(String name) {
new Object() {
String objName
@@ -108,56 +161,74 @@ class InnerClassTest extends CompilableTestSupport {
}
assert makeObj2("Guillaume").toString() == "My name is Guillaume"
- """
+ '''
}
+ @Test
void testStaticInnerClass() {
- assertScript """
- import java.lang.reflect.Modifier
-
+ assertScript '''
+ import static java.lang.reflect.Modifier.*
+
class A {
- static class B{}
+ static class B {}
}
- def x = new A.B()
- assert x != null
-
- def mods = A.B.modifiers
- assert Modifier.isPublic(mods)
- """
+ def b = new A.B()
+ assert b != null
+
+ int modifiers = A.B.modifiers
+ assert isPublic(modifiers)
+ '''
+ }
- assertScript """
+ @Test
+ void testStaticInnerClass2() {
+ assertScript '''
class A {
static class B{}
}
assert A.declaredClasses.length==1
assert A.declaredClasses[0]==A.B
- """
+ '''
}
- void testNonStaticInnerClass_FAILS() {
- if (notYetImplemented()) return
+ @Test
+ void testNonStaticInnerClass() {
+ assertScript '''
+ class A {
+ class B {
+ final String foo = 'foo'
+ }
+ }
+ def b = new A.B(new A())
+ assert b.foo == 'foo'
+ '''
+ }
- shouldNotCompile """
+ @Test @NotYetImplemented
+ void testNonStaticInnerClass2() {
+ shouldFail CompilationFailedException, '''
class A {
class B {}
}
- def x = new A.B()
- """
+ def x = new A.B() // requires reference to A
+ '''
}
+ @Test
void testAnonymousInnerClass() {
- assertScript """
+ assertScript '''
class Foo {}
def x = new Foo(){
def bar() { 1 }
}
assert x.bar() == 1
- """
+ '''
}
+ @Test
void testLocalVariable() {
- assertScript """
+ assertScript '''
class Foo {}
final val = 2
def x = new Foo() {
@@ -165,20 +236,22 @@ class InnerClassTest extends CompilableTestSupport {
}
assert x.bar() == val
assert x.bar() == 2
- """
+ '''
}
+ @Test
void testConstructor() {
- shouldNotCompile """
+ shouldFail CompilationFailedException, '''
class Foo {}
def x = new Foo() {
Foo() {}
}
- """
+ '''
}
+ @Test
void testUsageOfOuterField() {
- assertScript """
+ assertScript '''
interface Run {
def run()
}
@@ -198,9 +271,12 @@ class InnerClassTest extends CompilableTestSupport {
assert foo.foo() == 1
foo.x(2)
assert foo.foo() == 2
- """
+ '''
+ }
- assertScript """
+ @Test
+ void testUsageOfOuterField2() {
+ assertScript '''
interface Run {
def run()
}
@@ -219,9 +295,12 @@ class InnerClassTest extends CompilableTestSupport {
assert Foo.foo() == 1
Foo.x(2)
assert Foo.foo() == 2
- """
+ '''
+ }
- assertScript """
+ @Test
+ void testUsageOfOuterField3() {
+ assertScript '''
interface X {
def m()
}
@@ -240,9 +319,11 @@ class InnerClassTest extends CompilableTestSupport {
}
def a = new A()
assert "pm" == a.foo()
- """
+ '''
+ }
- //GROOVY-6141
+ @Test // GROOVY-6141
+ void testUsageOfOuterField4() {
assertScript '''
class A {
def x = 1
@@ -271,8 +352,49 @@ class InnerClassTest extends CompilableTestSupport {
'''
}
- // GROOVY-9501
- void testUsageOfOuterField2() {
+ @Test // GROOVY-9189
+ void testUsageOfOuterField5() {
+ assertScript '''
+ interface Run {
+ def run()
+ }
+ class Foo {
+ private static x = 1
+
+ static foo(def runner = new Run() {
+ def run() { return x }
+ }) {
+ runner.run()
+ }
+
+ static x(y) { x = y }
+ }
+ assert Foo.foo() == 1
+ Foo.x(2)
+ assert Foo.foo() == 2
+ '''
+ }
+
+ @Test // GROOVY-9168
+ void testUsageOfOuterField6() {
+ assertScript '''
+ class A {
+ // AIC in this position can use static properties:
+ A(Runnable action = new Runnable() { void run() { answer = 42 }}) {
+ this.action = action
+ }
+ Runnable action
+ static int answer
+ }
+
+ def a = new A()
+ a.action.run();
+ assert a.answer == 42
+ '''
+ }
+
+ @Test // GROOVY-9501
+ void testUsageOfOuterField7() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -280,13 +402,16 @@ class InnerClassTest extends CompilableTestSupport {
assert Outer.Inner.error == null
}
}
+
abstract class Outer {
private static volatile boolean flag
+
void newThread() {
Thread thread = new Inner()
thread.start()
thread.join()
}
+
private final class Inner extends Thread {
@Override
void run() {
@@ -304,8 +429,8 @@ class InnerClassTest extends CompilableTestSupport {
'''
}
- // inner class is static instead of final
- void testUsageOfOuterField3() {
+ @Test // inner class is static instead of final
+ void testUsageOfOuterField8() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -313,13 +438,16 @@ class InnerClassTest extends CompilableTestSupport {
assert Outer.Inner.error == null
}
}
+
abstract class Outer {
private static volatile boolean flag
+
void newThread() {
Thread thread = new Inner()
thread.start()
thread.join()
}
+
private static class Inner extends Thread {
@Override
void run() {
@@ -337,8 +465,8 @@ class InnerClassTest extends CompilableTestSupport {
'''
}
- // GROOVY-9569
- void testUsageOfOuterField4() {
+ @Test // GROOVY-9569
+ void testUsageOfOuterField9() {
assertScript '''
class Main extends Outer {
static main(args) {
@@ -346,14 +474,17 @@ class InnerClassTest extends CompilableTestSupport {
assert Outer.Inner.error == null
}
}
+
@groovy.transform.CompileStatic
abstract class Outer {
private static volatile boolean flag
+
void newThread() {
Thread thread = new Inner()
thread.start()
thread.join()
}
+
private static class Inner extends Thread {
@Override
void run() {
@@ -371,45 +502,161 @@ class InnerClassTest extends CompilableTestSupport {
'''
}
- void testUsageOfOuterFieldOverridden_FAILS() {
- if (notYetImplemented()) return
+ @Test
+ void testUsageOfOuterField10() {
+ assertScript '''
+ class Outer {
+ static final String OUTER_CONSTANT = 'Constant Value'
+
+ class Inner {
+ String access() {
+ return OUTER_CONSTANT
+ }
+ }
+
+ void testInnerClassAccessOuterConst() {
+ def inner = new Inner()
+ assert inner.access() == OUTER_CONSTANT
+ }
+ }
+
+ def outer = new Outer()
+ outer.testInnerClassAccessOuterConst()
+ '''
+ }
+
+ @Test // GROOVY-5259
+ void testUsageOfOuterField11() {
+ assertScript '''
+ class Base {
+ Base(String string) {
+ }
+ }
+
+ class Outer {
+ static final String OUTER_CONSTANT = 'Constant Value'
+
+ class Inner extends Base {
+ Inner() {
+ super(OUTER_CONSTANT) // "this" is not initialized yet
+ }
+
+ String access() {
+ return OUTER_CONSTANT
+ }
+ }
+
+ void testInnerClassAccessOuterConst() {
+ def inner = new Inner()
+ assert inner.access() == OUTER_CONSTANT
+ }
+ }
+
+ def outer = new Outer()
+ outer.testInnerClassAccessOuterConst()
+ '''
+ }
+
+ @Test
+ void testUsageOfOuterSuperField() {
+ assertScript '''
+ class InnerBase {
+ InnerBase(String string) {
+ }
+ }
+
+ class OuterBase {
+ protected static final String OUTER_CONSTANT = 'Constant Value'
+ }
+
+ class Outer extends OuterBase {
+
+ class Inner extends InnerBase {
+ Inner() {
+ super(OUTER_CONSTANT)
+ }
+
+ String access() {
+ return OUTER_CONSTANT
+ }
+ }
+
+ void testInnerClassAccessOuterConst() {
+ def inner = new Inner()
+ assert inner.access() == OUTER_CONSTANT
+ }
+ }
+
+ def outer = new Outer()
+ outer.testInnerClassAccessOuterConst()
+ '''
+ }
+
+ @Test
+ void testUsageOfOuterField_WrongCallToSuper() {
+ shouldFail '''
+ class Outer {
+ protected static final String OUTER_CONSTANT = 'Constant Value'
+
+ class Inner {
+ Inner() {
+ // there is no Object#<init>(String) method, but it throws a VerifyError for uninitialized this
+ super(OUTER_CONSTANT)
+ }
+
+ String access() {
+ return OUTER_CONSTANT
+ }
+ }
+
+ void testInnerClassAccessOuterConst() {
+ def inner = new Inner()
+ inner.access()
+ }
+ }
+
+ def outer = new Outer()
+ outer.testInnerClassAccessOuterConst()
+ '''
+ }
- assertScript """
+ @Test
+ void testUsageOfOuterFieldOverridden() {
+ assertScript '''
interface Run {
def run()
}
class Foo {
private x = 1
+
def foo() {
- def runner = new Run(){
- def run() { return x }
+ def runner = new Run() {
+ def run() { return x } // <-- dynamic variable
}
runner.run()
}
- void setX(y) { x=y }
+
+ void setX(val) { x = val }
}
class Bar extends Foo {
- def x = "string"
+ def x = 'string' // hides 'foo.@x' and overrides 'foo.setX(val)'
}
def bar = new Bar()
- assert bar.foo() == 1
- bar.x(2)
- assert bar.foo() == 2
- bar.x = "new string"
- assert bar.foo() == 2
- """
-
- //TODO: static part
-
+ assert bar.foo() == 'string'
+ bar.x = 'new string'
+ assert bar.foo() == 'new string'
+ '''
}
+ @Test
void testUsageOfOuterMethod() {
- assertScript """
+ assertScript '''
interface Run {
def run()
}
class Foo {
- private x(){1}
+ private x() { return 1 }
+
def foo() {
def runner = new Run(){
def run() { return x() }
@@ -419,14 +666,17 @@ class InnerClassTest extends CompilableTestSupport {
}
def foo = new Foo()
assert foo.foo() == 1
- """
+ '''
+ }
- assertScript """
+ @Test
+ void testUsageOfOuterMethod2() {
+ assertScript '''
interface Run {
def run()
}
class Foo {
- private static x() {1}
+ private static x() { return 1 }
def foo() {
def runner = new Run() {
@@ -437,108 +687,99 @@ class InnerClassTest extends CompilableTestSupport {
}
def foo = new Foo()
assert foo.foo() == 1
- """
+ '''
}
- // GROOVY-9501
- void testUsageOfOuterField7() {
+ @Test
+ void testUsageOfOuterMethod3() {
assertScript '''
- class Main extends Outer {
- static main(args) {
- newInstance().newThread()
- assert Outer.Inner.error == null
- }
+ interface Run {
+ def run()
}
+ class Foo {
+ private static x() { return 1 }
- abstract class Outer {
- private static volatile boolean flag
-
- void newThread() {
- Thread thread = new Inner()
- thread.start()
- thread.join()
- }
-
- private final class Inner extends Thread {
- @Override
- void run() {
- try {
- if (!flag) {
- // do work
- }
- } catch (e) {
- error = e
- }
- }
- public static error
+ def foo(def runner = new Run() {
+ def run() { return x() }
+ }) {
+ runner.run()
}
}
+ def foo = new Foo()
+ assert foo.foo() == 1
'''
}
- // inner class is static instead of final
- void testUsageOfOuterField8() {
+ @Test // GROOVY-9189
+ void testUsageOfOuterMethod4() {
assertScript '''
- class Main extends Outer {
- static main(args) {
- newInstance().newThread()
- assert Outer.Inner.error == null
- }
+ interface Run {
+ def run()
}
+ class Foo {
+ private static x() { return 1 }
- abstract class Outer {
- private static volatile boolean flag
-
- void newThread() {
- Thread thread = new Inner()
- thread.start()
- thread.join()
+ static def foo(def runner = new Run() {
+ def run() { return x() }
+ }) {
+ runner.run()
}
+ }
+ def foo = new Foo()
+ assert foo.foo() == 1
+ '''
+ }
- private static class Inner extends Thread {
- @Override
- void run() {
- try {
- if (!flag) {
- // do work
- }
- } catch (e) {
- error = e
- }
- }
- public static error
+ @Test // GROOVY-9168
+ void testUsageOfOuterMethod5() {
+ assertScript '''
+ class A {
+ // AIC in this position can use static methods:
+ A(Runnable action = new Runnable() { void run() { setAnswer(42) }}) {
+ this.action = action
}
+ Runnable action
+ static int answer
}
+
+ def a = new A()
+ a.action.run();
+ assert a.answer == 42
'''
}
+ @Test
void testUsageOfOuterMethodOverridden() {
- assertScript """
+ assertScript '''
interface Run {
def run()
}
class Foo {
- private x(){1}
+ private x() { return 1 }
+
def foo() {
- def runner = new Run(){
+ def runner = new Run() {
def run() { return x() }
}
runner.run()
}
}
- class Bar extends Foo{
- def x() { 2 }
+ class Bar extends Foo {
+ def x() { return 2 }
}
def bar = new Bar()
assert bar.foo() == 1
- """
+ '''
+ }
- assertScript """
+ @Test
+ void testUsageOfOuterMethodOverridden2() {
+ assertScript '''
interface Run {
def run()
}
class Foo {
- private static x() { 1 }
+ private static x() { return 1 }
static foo() {
def runner = new Run() {
@@ -548,31 +789,33 @@ class InnerClassTest extends CompilableTestSupport {
}
}
class Bar extends Foo {
- static x() { 2 }
+ static x() { return 2 }
}
def bar = new Bar()
assert bar.foo() == 1
- """
+ '''
}
+ @Test
void testClassOutputOrdering() {
// this does actually not do much, but before this
// change the inner class was tried to be executed
- // because a class ordering bug. The main method
- // makes the Foo class executeable, but Foo$Bar is
+ // because a class ordering bug. The main method
+ // makes the Foo class executeable, but Foo$Bar is
// not. So if Foo$Bar is returned, asserScript will
// fail. If Foo is returned, asserScript will not
// fail.
- assertScript """
+ assertScript '''
class Foo {
static class Bar{}
static main(args){}
}
- """
+ '''
}
+ @Test
void testInnerClassDotThisUsage() {
- assertScript """
+ assertScript '''
class A{
int x = 0;
class B{
@@ -591,9 +834,12 @@ class InnerClassTest extends CompilableTestSupport {
c.foo()
assert a.x == 1
assert b.y == 4
- """
+ '''
+ }
- assertScript """
+ @Test
+ void testInnerClassDotThisUsage2() {
+ assertScript '''
interface X {
def m()
}
@@ -611,41 +857,60 @@ class InnerClassTest extends CompilableTestSupport {
class B extends A {}
def b = new B()
assert b.foo() instanceof B
- """
+ '''
}
+ @Test // GROOVY-4028
void testImplicitThisPassingWithNamedArguments() {
- def oc = new MyOuterClass4028()
- assert oc.foo().propMap.size() == 2
+ assertScript '''
+ class Outer {
+ def inner() {
+ new Inner(fName: 'Roshan', lName: 'Dawrani')
+ }
+ class Inner {
+ Map props
+ Inner(Map props) {
+ this.props = props
+ }
+ }
+ }
+ def outer = new Outer()
+ def inner = outer.inner()
+ assert inner.props.size() == 2
+ '''
}
+ @Test
void testThis0() {
- assertScript """
-class A {
- static def field = 10
- void main (a) {
- new C ().r ()
- }
-
- class C {
- def r () {
- 4.times {
- new B(it).u (it)
- }
- }
- }
-
- class B {
- def s
- B (s) { this.s = s}
- def u (i) { println i + s + field }
- }}"""
+ assertScript '''
+ class A {
+ static def field = 10
+ void main (a) {
+ new C ().r ()
+ }
+
+ class C {
+ def r () {
+ 4.times {
+ new B(it).u (it)
+ }
+ }
+ }
+
+ class B {
+ def s
+ B (s) { this.s = s}
+ def u (i) { println i + s + field }
+ }
+ }
+ '''
}
+ @Test
void testReferencedVariableInAIC() {
- assertScript """
- interface X{}
-
+ assertScript '''
+ interface X {}
+
final double delta = 0.1
(0 ..< 1).collect { n ->
new X () {
@@ -654,10 +919,14 @@ class A {
}
}
}
- """
- assertScript """
- interface X{}
-
+ '''
+ }
+
+ @Test
+ void testReferencedVariableInAIC2() {
+ assertScript '''
+ interface X {}
+
final double delta1 = 0.1
final double delta2 = 0.1
(0 ..< 1).collect { n ->
@@ -667,10 +936,10 @@ class A {
}
}
}
- """
+ '''
}
- // GROOVY-5989
+ @Test // GROOVY-5989
void testReferenceToOuterClassNestedInterface() {
assertScript '''
interface Koo { class Inner { } }
@@ -683,55 +952,102 @@ class A {
'''
}
- // GROOVY-5679
- // GROOVY-5681
+ @Test // GROOVY-5679, GROOVY-5681
void testEnclosingMethodIsSet() {
- new GroovyShell().evaluate '''import groovy.transform.ASTTest
- import static org.codehaus.groovy.control.CompilePhase.*
- import org.codehaus.groovy.ast.InnerClassNode
- import org.codehaus.groovy.ast.expr.ConstructorCallExpression
-import org.codehaus.groovy.classgen.Verifier
-
- class A {
- int x
-
- /*@ASTTest(phase=SEMANTIC_ANALYSIS, value={
- def cce = lookup('inner')[0].expression
- def icn = cce.type
- assert icn instanceof InnerClassNode
- assert icn.enclosingMethod == node
- })
- A() { inner: new Runnable() { void run() {} } }
+ assertScript '''
+ import groovy.transform.ASTTest
+ import org.codehaus.groovy.ast.expr.*
+ import static org.codehaus.groovy.classgen.Verifier.*
+ import static org.codehaus.groovy.control.CompilePhase.*
- @ASTTest(phase=SEMANTIC_ANALYSIS, value={
- def cce = lookup('inner')[0].expression
- def icn = cce.type
- assert icn instanceof InnerClassNode
- assert icn.enclosingMethod == node
- })
- void foo() { inner: new Runnable() { void run() {} } }*/
+ class A {
+ @ASTTest(phase=CLASS_GENERATION, value={
+ def initialExpression = node.parameters[0].getNodeMetaData(INITIAL_EXPRESSION)
+ assert initialExpression instanceof ConstructorCallExpression
+ def icn = initialExpression.type
+ assert icn instanceof org.codehaus.groovy.ast.InnerClassNode
+ assert icn.enclosingMethod != null
+ assert icn.enclosingMethod.name == 'bar'
+ assert icn.enclosingMethod.parameters.length == 0 // ensure the enclosing method is bar(), not bar(Object)
+ })
+ void bar(action = new Runnable() { void run() { x = 123 }}) {
+ action.run()
+ }
+ int x
+ }
+ def a = new A()
+ a.bar()
+ assert a.x == 123
+ '''
+ }
+
+ @Test @NotYetImplemented // GROOVY-9151
+ void testEnclosingMethodIsSet2() {
+ assertScript '''
+ import groovy.transform.ASTTest
+ import org.codehaus.groovy.ast.expr.*
+ import static org.codehaus.groovy.classgen.Verifier.*
+ import static org.codehaus.groovy.control.CompilePhase.*
@ASTTest(phase=CLASS_GENERATION, value={
- def initialExpression = node.parameters[0].getNodeMetaData(Verifier.INITIAL_EXPRESSION)
- assert initialExpression instanceof ConstructorCallExpression
- def icn = initialExpression.type
- assert icn instanceof InnerClassNode
- assert icn.enclosingMethod != null
- assert icn.enclosingMethod.name == 'bar'
- assert icn.enclosingMethod.parameters.length == 0 // ensure the enclosing method is bar(), not bar(Object)
+ def init = node.parameters[0].getNodeMetaData(INITIAL_EXPRESSION)
+ assert init instanceof MapExpression
+ assert init.mapEntryExpressions[0].valueExpression instanceof ConstructorCallExpression
+ def type = init.mapEntryExpressions[0].valueExpression.type
+
+ assert type.enclosingMethod != null
+ assert type.enclosingMethod.name == 'bar'
+ assert type.enclosingMethod.parameters.length == 0 // ensure the enclosing method is bar(), not bar(Map)
})
- void bar(action=new Runnable() { void run() { x = 123 }}) {
- action.run()
+ void bar(Map args = [action: new Runnable() { void run() { result = 123 }}]) {
+ args.action.run()
}
- }
- def a = new A()
- a.bar()
- assert a.x == 123
+ bar()
'''
}
- // GROOVY-4896, GROOVY-6810
+ @Test // GROOVY-5681, GROOVY-9151
+ void testEnclosingMethodIsSet3() {
+ assertScript '''
+ import groovy.transform.ASTTest
+ import org.codehaus.groovy.ast.expr.*
+ import org.codehaus.groovy.ast.stmt.*
+ import static org.codehaus.groovy.classgen.Verifier.*
+ import static org.codehaus.groovy.control.CompilePhase.*
+
+ @ASTTest(phase=CLASS_GENERATION, value={
+ def init = node.parameters[0].getNodeMetaData(INITIAL_EXPRESSION)
+ assert init instanceof ConstructorCallExpression
+ assert init.type.enclosingMethod != null
+ assert init.type.enclosingMethod.name == 'bar'
+ assert init.type.enclosingMethod.parameters.length == 0 // ensure the enclosing method is bar(), not bar(Runnable)
+
+ assert init.type.getMethods('run')[0].code instanceof BlockStatement
+ assert init.type.getMethods('run')[0].code.statements[0] instanceof ExpressionStatement
+ assert init.type.getMethods('run')[0].code.statements[0].expression instanceof DeclarationExpression
+
+ init = init.type.getMethods('run')[0].code.statements[0].expression.rightExpression
+ assert init instanceof ConstructorCallExpression
+ assert init.isUsingAnonymousInnerClass()
+ assert init.type.enclosingMethod != null
+ assert init.type.enclosingMethod.name == 'run'
+ assert init.type.enclosingMethod.parameters.length == 0
+ })
+ void bar(Runnable runner = new Runnable() {
+ @Override void run() {
+ def comparator = new Comparator<Integer>() {
+ int compare(Integer one, Integer two) {
+ }
+ }
+ }
+ }) {
+ args.action.run()
+ }
+ '''
+ }
+
+ @Test // GROOVY-6810
void testThisReferenceForAICInOpenBlock() {
assertScript '''
import java.security.AccessController
@@ -763,7 +1079,10 @@ import org.codehaus.groovy.classgen.Verifier
def t = new Test()
injectVariables(t, ['p': 'q'])
'''
+ }
+ @Test // GROOVY-4896
+ void testThisReferenceForAICInOpenBlock2() {
assertScript '''
def doSomethingUsingLocal(){
logExceptions {
@@ -827,8 +1146,8 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-5582
- void testAICextendingAbstractInnerClass() {
+ @Test // GROOVY-5582
+ void testAICExtendingAbstractInnerClass() {
assertScript '''
class Outer {
int outer() { 1 }
@@ -846,7 +1165,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-6831
+ @Test // GROOVY-6831
void testNestedPropertyHandling() {
assertScript '''
class Outer {
@@ -869,7 +1188,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-7312
+ @Test // GROOVY-7312
void testInnerClassOfInterfaceIsStatic() {
assertScript '''
import java.lang.reflect.Modifier
@@ -881,7 +1200,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-7312
+ @Test // GROOVY-7312
void testInnerClassOfInterfaceIsStatic2() {
assertScript '''
import java.lang.reflect.Modifier
@@ -899,7 +1218,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-8914
+ @Test // GROOVY-8914
void testNestedClassInheritingFromNestedClass() {
// control
assert new Outer8914.Nested()
@@ -912,9 +1231,9 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-6809
- void _FIXME_testReferenceToUninitializedThis() {
- assertScript '''
+ @Test // GROOVY-6809
+ void testReferenceToUninitializedThis() {
+ def err = shouldFail '''
class Test {
static main(args) {
def a = new A()
@@ -925,22 +1244,19 @@ import org.codehaus.groovy.classgen.Verifier
def b = new B()
}
- void sayA() {
- println 'saying A'
- }
-
class B extends A {
B() {
- super(A.this) // does not exist
- sayA()
+ super(A.this)
}
}
}
}
'''
+
+ assert err =~ / Could not find matching constructor for: Test.A\(Test.A\)/
}
- // GROOVY-6809
+ @Test // GROOVY-6809
void testReferenceToUninitializedThis2() {
assertScript '''
class A {
@@ -960,7 +1276,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-6809
+ @Test // GROOVY-6809
void testReferenceToUninitializedThis3() {
assertScript '''
class A {
@@ -977,39 +1293,27 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- // GROOVY-7609
- void _FIXME_testReferenceToUninitializedThis4() {
- assertScript '''
- class Login {
- Login() {
- def navBar = new LoginNavigationBar()
- }
-
- class LoginNavigationBar {
- ExploreDestinationsDropdown exploreDestinationsDropdown
-
- LoginNavigationBar() {
- exploreDestinationsDropdown = new ExploreDestinationsDropdown()
- }
-
- class ExploreDestinationsDropdown /*extends NavigationBarDropdown<ExploreDestinationsDropdown>*/ {
- ExploreDestinationsDropdown() {
- //super(Login.this.sw, 0)
- Login.this.sw
- }
- }
- }
-
- static main(args) {
- new Login()
- }
+ @Test @NotYetImplemented // GROOVY-9168
+ void testReferenceToUninitializedThis4() {
+ def err = shouldFail '''
+ class Outer {
+ class Inner {
+ }
+ Outer(Inner inner) {
+ }
+ Outer() {
+ this(new Inner())
+ }
}
+ new Outer()
'''
+
+ assert err =~ / Cannot reference 'this' before supertype constructor has been called. /
}
- // GROOVY-9168
- void _FIXME_testReferenceToUninitializedThis5() {
- assertScript '''
+ @Test @NotYetImplemented // GROOVY-9168
+ void testReferenceToUninitializedThis5() {
+ def err = shouldFail '''
class Outer {
class Inner {
}
@@ -1018,9 +1322,11 @@ import org.codehaus.groovy.classgen.Verifier
}
new Outer()
'''
+
+ assert err =~ / Cannot reference 'this' before supertype constructor has been called. /
}
- // GROOVY-9168
+ @Test // GROOVY-9168
void testReferenceToUninitializedThis6() {
assertScript '''
import groovy.transform.ASTTest
@@ -1048,45 +1354,10 @@ import org.codehaus.groovy.classgen.Verifier
assert a.action.call() == 42
'''
}
-
- // GROOVY-9168
- void _FIXME_testReferenceToUninitializedThis7() {
- assertScript '''
- class A {
- // AIC in this position can use static properties:
- A(Runnable action = new Runnable() { void run() { answer = 42 }}) {
- this.action = action
- }
- Runnable action
- static int answer
- }
-
- def a = new A()
- a.action.run();
- assert a.answer == 42
- '''
- }
-
- // GROOVY-9168
- void _FIXME_testReferenceToUninitializedThis8() {
- assertScript '''
- class A {
- // AIC in this position can use static methods:
- A(Runnable action = new Runnable() { void run() { setAnswer(42) }}) {
- this.action = action
- }
- Runnable action
- protected static int answer
- static void setAnswer(int value) { answer = value }
- }
-
- def a = new A()
- a.action.run();
- assert a.answer == 42
- '''
- }
}
+//------------------------------------------------------------------------------
+
class Parent8914 {
static class Nested {}
}
@@ -1094,15 +1365,3 @@ class Parent8914 {
class Outer8914 {
static class Nested extends Parent8914.Nested {}
}
-
-class MyOuterClass4028 {
- def foo() {
- new MyInnerClass4028(fName: 'Roshan', lName: 'Dawrani')
- }
- class MyInnerClass4028 {
- Map propMap
- def MyInnerClass4028(Map propMap) {
- this.propMap = propMap
- }
- }
-}