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/28 22:57:09 UTC
[groovy] 01/01: GROOVY-9499: AIC as argument to this/super
constructor call
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 ad95958a0141e4b2ff65aceaeeba204bd72d1069
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu May 28 17:56:48 2020 -0500
GROOVY-9499: AIC as argument to this/super constructor call
Groovy 2.5 backport
---
.../classgen/InnerClassCompletionVisitor.java | 4 +-
.../groovy/classgen/InnerClassVisitor.java | 128 +--
.../groovy/classgen/InnerClassVisitorHelper.java | 6 +-
.../org/codehaus/groovy/classgen/Verifier.java | 15 +-
src/test/gls/innerClass/InnerClassTest.groovy | 926 +++++++++++++++------
5 files changed, 772 insertions(+), 307 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 452ac65..91e3eb1 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -160,7 +160,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
private void getThis(MethodVisitor mv, String classInternalName, String outerClassDescriptor, String innerClassInternalName) {
mv.visitVarInsn(ALOAD, 0);
- if (CLOSURE_TYPE.equals(thisField.getType())) {
+ if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", CLOSURE_DESCRIPTOR);
mv.visitMethodInsn(INVOKEVIRTUAL, CLOSURE_INTERNAL_NAME, "getThisObject", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
@@ -168,7 +168,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", outerClassDescriptor);
}
}
-
+
private void addDefaultMethods(InnerClassNode node) {
final boolean isStatic = isStatic(node);
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
index d2d570f..33f07f1 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitor.java
@@ -20,7 +20,10 @@ 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;
@@ -45,14 +48,11 @@ import java.util.List;
public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcodes {
- private final SourceUnit sourceUnit;
private ClassNode classNode;
- private static final int PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC + Opcodes.ACC_SYNTHETIC;
- private FieldNode thisField = null;
- private MethodNode currentMethod;
private FieldNode currentField;
- private boolean processingObjInitStatements = false;
- private boolean inClosure = false;
+ private MethodNode currentMethod;
+ private final SourceUnit sourceUnit;
+ private boolean inClosure, processingObjInitStatements;
public InnerClassVisitor(CompilationUnit cu, SourceUnit su) {
sourceUnit = su;
@@ -65,13 +65,12 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
@Override
public void visitClass(ClassNode node) {
- this.classNode = node;
- thisField = null;
+ classNode = node;
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", PUBLIC_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);
}
}
@@ -85,7 +84,7 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
node.setUnresolvedSuperClass(ClassHelper.OBJECT_TYPE);
}
}
-
+
@Override
public void visitClosureExpression(ClosureExpression expression) {
boolean inClosureOld = inClosure;
@@ -103,7 +102,7 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
@Override
protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
- this.currentMethod = node;
+ currentMethod = node;
visitAnnotations(node);
visitClassCodeContainer(node.getCode());
// GROOVY-5681: initial expressions should be visited too!
@@ -113,14 +112,14 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
}
visitAnnotations(param);
}
- this.currentMethod = null;
+ currentMethod = null;
}
@Override
public void visitField(FieldNode node) {
- this.currentField = node;
+ currentField = node;
super.visitField(node);
- this.currentField = null;
+ currentField = null;
}
@Override
@@ -143,9 +142,8 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
InnerClassNode innerClass = (InnerClassNode) call.getType();
ClassNode outerClass = innerClass.getOuterClass();
ClassNode superClass = innerClass.getSuperClass();
- if (superClass instanceof InnerClassNode
- && !superClass.isInterface()
- && !(superClass.isStaticClass()||((superClass.getModifiers()&ACC_STATIC)==ACC_STATIC))) {
+ if (!superClass.isInterface() && superClass.getOuterClass() != null
+ && !(superClass.isStaticClass() || (superClass.getModifiers() & ACC_STATIC) != 0)) {
insertThis0ToSuperCall(call, innerClass);
}
if (!innerClass.getDeclaredConstructors().isEmpty()) return;
@@ -154,26 +152,25 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
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
- final int additionalParamCount = 1 + scope.getReferencedLocalVariablesCount();
+ int additionalParamCount = (isStatic ? 0 : 1) + scope.getReferencedLocalVariablesCount();
List<Parameter> parameters = new ArrayList<Parameter>(expressions.size() + additionalParamCount);
// superCallArguments = arguments for the super call == the constructor call arguments
List<Expression> superCallArguments = new ArrayList<Expression>(expressions.size());
- // first we add a super() call for all expressions given in the
+ // 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);
+ 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));
}
@@ -185,23 +182,24 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
block.addStatement(new ExpressionStatement(cce));
- // we need to add "this" to access unknown methods/properties
- // this is saved in a field named this$0
- pCount = 0;
- expressions.add(pCount, VariableExpression.THIS_EXPRESSION);
- boolean isStatic = isStaticThis(innerClass,scope);
- ClassNode outerClassType = getClassNode(outerClass, isStatic);
- if (!isStatic && inClosure) outerClassType = ClassHelper.CLOSURE_TYPE;
- outerClassType = outerClassType.getPlainNodeReference();
- Parameter thisParameter = new Parameter(outerClassType, "p" + pCount);
- parameters.add(pCount, thisParameter);
+ 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);
- thisField = innerClass.addField("this$0", PUBLIC_SYNTHETIC, outerClassType, null);
- addFieldInit(thisParameter, thisField, block);
+ // "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);
+
+ pCount += 1;
+ }
// for each shared variable we add a reference and save it as field
for (Iterator it = scope.getReferencedLocalVariablesIterator(); it.hasNext();) {
- pCount++;
org.codehaus.groovy.ast.Variable var = (org.codehaus.groovy.ast.Variable) it.next();
VariableExpression ve = new VariableExpression(var);
ve.setClosureSharedVariable(true);
@@ -215,25 +213,51 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
final VariableExpression initial = new VariableExpression(p);
initial.setSynthetic(true);
initial.setUseReferenceDirectly(true);
- final FieldNode pField = innerClass.addFieldFirst(ve.getName(), PUBLIC_SYNTHETIC,rawReferenceType, initial);
+ final FieldNode pField = innerClass.addFieldFirst(ve.getName(), ACC_PUBLIC | ACC_SYNTHETIC, rawReferenceType, initial);
pField.setHolder(true);
pField.setOriginType(ClassHelper.getWrapper(var.getOriginType()));
+ pCount += 1;
}
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
@@ -252,7 +276,7 @@ public class InnerClassVisitor extends InnerClassVisitorHelper implements Opcode
// 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
+ // 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();
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 0ed2678..ab69fd8 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;
@@ -894,8 +893,20 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
protected void addConstructor(Parameter[] newParams, ConstructorNode ctor, Statement code, ClassNode node) {
- ConstructorNode genConstructor = node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
+ final ConstructorNode genConstructor = node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
+ genConstructor.putNodeMetaData(DEFAULT_PARAMETER_GENERATED, Boolean.TRUE);
markAsGenerated(node, genConstructor);
+
+ // set anon. inner enclosing method reference
+ code.visit(new CodeVisitorSupport() {
+ @Override
+ public void visitConstructorCallExpression(ConstructorCallExpression call) {
+ if (call.isUsingAnonymousInnerClass()) {
+ call.getType().setEnclosingMethod(genConstructor);
+ }
+ super.visitConstructorCallExpression(call);
+ }
+ });
}
/**
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy b/src/test/gls/innerClass/InnerClassTest.groovy
index 8e622ee..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,54 +39,98 @@ 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() {
+ new Object() {
String toString() { "My name is \${name}" }
- }
+ }
}
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)
+ }
- assertScript """
+ String access() {
+ return OUTER_CONSTANT
+ }
+ }
+
+ void testInnerClassAccessOuterConst() {
+ def inner = new Inner()
+ inner.access()
+ }
+ }
+
+ def outer = new Outer()
+ outer.testInnerClassAccessOuterConst()
+ '''
+ }
+
+ @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,56 +952,103 @@ 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()
'''
}
+ @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() {
- // GROOVY-6810
assertScript '''
import java.security.AccessController
import java.security.PrivilegedAction
@@ -763,8 +1079,10 @@ import org.codehaus.groovy.classgen.Verifier
def t = new Test()
injectVariables(t, ['p': 'q'])
'''
+ }
- //GROOVY-4896
+ @Test // GROOVY-4896
+ void testThisReferenceForAICInOpenBlock2() {
assertScript '''
def doSomethingUsingLocal(){
logExceptions {
@@ -828,8 +1146,8 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- void testAICextendingAbstractInnerClass() {
- //GROOVY-5582
+ @Test // GROOVY-5582
+ void testAICExtendingAbstractInnerClass() {
assertScript '''
class Outer {
int outer() { 1 }
@@ -847,8 +1165,8 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
+ @Test // GROOVY-6831
void testNestedPropertyHandling() {
- // GROOVY-6831
assertScript '''
class Outer {
private static List items = []
@@ -870,8 +1188,8 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
+ @Test // GROOVY-7312
void testInnerClassOfInterfaceIsStatic() {
- //GROOVY-7312
assertScript '''
import java.lang.reflect.Modifier
interface Baz {
@@ -882,8 +1200,8 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- void testInnerClassOfInterfaceIsStaticVariant() {
- //GROOVY-7312
+ @Test // GROOVY-7312
+ void testInnerClassOfInterfaceIsStatic2() {
assertScript '''
import java.lang.reflect.Modifier
import groovy.transform.ASTTest
@@ -900,7 +1218,7 @@ import org.codehaus.groovy.classgen.Verifier
'''
}
- //GROOVY-8914
+ @Test // GROOVY-8914
void testNestedClassInheritingFromNestedClass() {
// control
assert new Outer8914.Nested()
@@ -912,8 +1230,134 @@ import org.codehaus.groovy.classgen.Verifier
assert new OuterReferencingPrecompiled.Nested()
'''
}
+
+ @Test // GROOVY-6809
+ void testReferenceToUninitializedThis() {
+ def err = shouldFail '''
+ class Test {
+ static main(args) {
+ def a = new A()
+ }
+
+ static class A {
+ A() {
+ def b = new B()
+ }
+
+ class B extends A {
+ B() {
+ super(A.this)
+ }
+ }
+ }
+ }
+ '''
+
+ assert err =~ / Could not find matching constructor for: Test.A\(Test.A\)/
+ }
+
+ @Test // GROOVY-6809
+ void testReferenceToUninitializedThis2() {
+ assertScript '''
+ class A {
+ A() {
+ this(new Runnable() {
+ @Override
+ void run() {
+ }
+ })
+ }
+
+ private A(Runnable action) {
+ }
+ }
+
+ new A()
+ '''
+ }
+
+ @Test // GROOVY-6809
+ void testReferenceToUninitializedThis3() {
+ assertScript '''
+ class A {
+ A(x) {
+ }
+ }
+ class B extends A {
+ B() {
+ super(new Object() {})
+ }
+ }
+
+ new B()
+ '''
+ }
+
+ @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. /
+ }
+
+ @Test @NotYetImplemented // GROOVY-9168
+ void testReferenceToUninitializedThis5() {
+ def err = shouldFail '''
+ class Outer {
+ class Inner {
+ }
+ Outer(Inner inner = new Inner()) {
+ }
+ }
+ new Outer()
+ '''
+
+ assert err =~ / Cannot reference 'this' before supertype constructor has been called. /
+ }
+
+ @Test // GROOVY-9168
+ void testReferenceToUninitializedThis6() {
+ assertScript '''
+ import groovy.transform.ASTTest
+ import java.util.concurrent.Callable
+ import org.codehaus.groovy.ast.expr.*
+ import static org.codehaus.groovy.classgen.Verifier.*
+ import static org.codehaus.groovy.control.CompilePhase.*
+
+ class A {
+ @ASTTest(phase=CLASS_GENERATION, value={
+ def init = node.parameters[0].getNodeMetaData(INITIAL_EXPRESSION)
+ assert init instanceof ConstructorCallExpression
+ assert init.isUsingAnonymousInnerClass()
+ assert init.type.enclosingMethod != null
+ assert init.type.enclosingMethod.name == '<init>'
+ assert init.type.enclosingMethod.parameters.length == 0 // ensure the enclosing method is A(), not A(Runnable)
+ })
+ A(Callable action = new Callable() { def call() { return 42 }}) {
+ this.action = action
+ }
+ Callable action
+ }
+
+ def a = new A()
+ assert a.action.call() == 42
+ '''
+ }
}
+//------------------------------------------------------------------------------
+
class Parent8914 {
static class Nested {}
}
@@ -921,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
- }
- }
-}