Skip to content

Commit 3232859

Browse files
committed
next step
7024 8050
1 parent a36ae26 commit 3232859

File tree

4 files changed

+100
-333
lines changed

4 files changed

+100
-333
lines changed

src/main/java/groovy/lang/MetaClassImpl.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,8 +1232,9 @@ private Object invokeOuterMethod(final Class<?> sender, final Object object, fin
12321232
var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
12331233
if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
12341234
MetaClass omc = registry.getMetaClass(outerClass);
1235+
Object target = getOuterReference(sender, object);
12351236
try {
1236-
return omc.invokeMethod(outerClass, outerClass, methodName, arguments, false, false);
1237+
return omc.invokeMethod(outerClass, target, methodName, arguments, false, false);
12371238
} catch (MissingMethodException e) {
12381239
mme.addSuppressed(e);
12391240
}
@@ -1242,6 +1243,32 @@ private Object invokeOuterMethod(final Class<?> sender, final Object object, fin
12421243
throw mme;
12431244
}
12441245

1246+
private Object getOuterReference(final Class<?> innerClass, final Object object) {
1247+
Object outer = null;
1248+
// non-static inner class may have outer class reference available in this$0 field
1249+
if (!(object instanceof Class) && (innerClass.getModifiers() & Opcodes.ACC_STATIC) == 0) {
1250+
try {
1251+
innerClass.getDeclaredField("this$0");
1252+
outer = getAttribute(object,"this$0");
1253+
if (outer instanceof GeneratedClosure) {
1254+
outer = ((Closure<?>) outer).getThisObject(); // skip closure(s)
1255+
}
1256+
} catch (NoSuchFieldException ignored) {
1257+
// an AIC is non-static but may not have this$0
1258+
} catch (Throwable t) {
1259+
throw new GroovyRuntimeException(t);
1260+
}
1261+
}
1262+
if (outer == null) {
1263+
Class<?> outerClass = innerClass.getEnclosingClass();
1264+
while (GeneratedClosure.class.isAssignableFrom(outerClass)) {
1265+
outerClass = outerClass.getEnclosingClass(); // skip closure(s)
1266+
}
1267+
outer = outerClass;
1268+
}
1269+
return outer;
1270+
}
1271+
12451272
private MetaMethod getMetaMethod(final Class<?> sender, final Object object, final String methodName, final boolean isCallToSuper, final Object[] arguments) {
12461273
MetaMethod method = null;
12471274
if (CALL_METHOD.equals(methodName) && object instanceof GeneratedClosure) {
@@ -1951,8 +1978,10 @@ private Object getOuterProperty(final Class<?> sender, final Object object, fina
19511978
var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
19521979
if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
19531980
MetaClass omc = registry.getMetaClass(outerClass);
1981+
Object target = getOuterReference(sender, object);
19541982
try {
1955-
return omc.getProperty(outerClass, outerClass, name, false, false);
1983+
return /*target instanceof GroovyObject go ? go.getProperty(name)
1984+
: */omc.getProperty(outerClass, target, name, false, false);
19561985
} catch (MissingPropertyException e) {
19571986
mpe.addSuppressed(e);
19581987
}
@@ -2847,8 +2876,10 @@ private void setOuterProperty(Class<?> sender, final Object object, final String
28472876
var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
28482877
if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
28492878
MetaClass omc = registry.getMetaClass(outerClass);
2879+
Object target = getOuterReference(sender, object);
28502880
try {
2851-
omc.setProperty(outerClass, outerClass, name, newValue, false, false);
2881+
if (target instanceof GroovyObject go) go.setProperty(name, newValue);
2882+
else omc.setProperty(outerClass, target, name, newValue, false, false);
28522883
return;
28532884
} catch (MissingPropertyException e) {
28542885
mpe.addSuppressed(e);

src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java

Lines changed: 3 additions & 234 deletions
Original file line numberDiff line numberDiff line change
@@ -18,69 +18,38 @@
1818
*/
1919
package org.codehaus.groovy.classgen;
2020

21-
import groovy.transform.CompileStatic;
22-
import groovy.transform.stc.POJO;
23-
import org.codehaus.groovy.ast.ClassHelper;
2421
import org.codehaus.groovy.ast.ClassNode;
2522
import org.codehaus.groovy.ast.ConstructorNode;
2623
import org.codehaus.groovy.ast.FieldNode;
2724
import org.codehaus.groovy.ast.InnerClassNode;
28-
import org.codehaus.groovy.ast.MethodNode;
2925
import org.codehaus.groovy.ast.Parameter;
3026
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
3127
import org.codehaus.groovy.ast.expr.Expression;
3228
import org.codehaus.groovy.ast.expr.TupleExpression;
3329
import org.codehaus.groovy.ast.expr.VariableExpression;
3430
import org.codehaus.groovy.ast.stmt.BlockStatement;
35-
import org.codehaus.groovy.ast.stmt.TryCatchStatement;
36-
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
3731
import org.codehaus.groovy.control.CompilationUnit;
3832
import org.codehaus.groovy.control.SourceUnit;
39-
import org.objectweb.asm.MethodVisitor;
4033

41-
import java.util.Arrays;
4234
import java.util.List;
4335
import java.util.StringJoiner;
44-
import java.util.function.BiConsumer;
4536

46-
import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.hasAnnotation;
4737
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
48-
import static org.apache.groovy.ast.tools.ClassNodeUtils.getMethod;
4938
import static org.apache.groovy.ast.tools.ConstructorNodeUtils.getFirstIfSpecialConstructorCall;
5039
import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
51-
import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
52-
import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
53-
import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
54-
import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
55-
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
5640
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
57-
import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
58-
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
5941
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
60-
import static org.codehaus.groovy.ast.tools.GeneralUtils.catchS;
61-
import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
6242
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
6343
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisX;
64-
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
6544
import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
66-
import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
67-
import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
6845
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
69-
import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
70-
import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
7146
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
7247
import static org.codehaus.groovy.transform.trait.Traits.isTrait;
7348
import static org.objectweb.asm.Opcodes.ACC_FINAL;
7449
import static org.objectweb.asm.Opcodes.ACC_MANDATED;
7550
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
7651
import static org.objectweb.asm.Opcodes.ACC_STATIC;
7752
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
78-
import static org.objectweb.asm.Opcodes.ACONST_NULL;
79-
import static org.objectweb.asm.Opcodes.ALOAD;
80-
import static org.objectweb.asm.Opcodes.ARETURN;
81-
import static org.objectweb.asm.Opcodes.CHECKCAST;
82-
import static org.objectweb.asm.Opcodes.GETFIELD;
83-
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
8453

8554
public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
8655

@@ -104,25 +73,13 @@ public void visitClass(final ClassNode node) {
10473

10574
if (node.isEnum() || node.isInterface() || isTrait(node.getOuterClass())) return;
10675

107-
// if the class has an inner class, add methods to support private member access
108-
if (node.getInnerClasses().hasNext()) {
109-
addDispatcherMethods(node);
110-
}
111-
11276
if (node instanceof InnerClassNode innerClass) {
11377
thisField = node.getDeclaredField("this$0");
11478
if (innerClass.getVariableScope() == null && node.getDeclaredConstructors().isEmpty()) {
11579
// add empty default constructor
11680
addGeneratedConstructor(innerClass, ACC_PUBLIC, Parameter.EMPTY_ARRAY, null, null);
11781
}
118-
11982
super.visitClass(node);
120-
121-
boolean innerPojo = hasAnnotation(node, new ClassNode(POJO.class))
122-
&& hasAnnotation(node, new ClassNode(CompileStatic.class));
123-
if (!innerPojo && !isStatic(innerClass)) {
124-
addMopMethods(innerClass);
125-
}
12683
}
12784
}
12885

@@ -156,195 +113,7 @@ private static void makeBridgeConstructor(final ClassNode c, final Parameter[] p
156113
}
157114
}
158115

159-
private static String getTypeDescriptor(ClassNode node, boolean isStatic) {
160-
return BytecodeHelper.getTypeDescription(getClassNode(node, isStatic));
161-
}
162-
163-
private static String getInternalName(ClassNode node, boolean isStatic) {
164-
return BytecodeHelper.getClassInternalName(getClassNode(node, isStatic));
165-
}
166-
167-
private static void addDispatcherMethods(ClassNode classNode) {
168-
final int objectDistance = getObjectDistance(classNode);
169-
170-
// since we added an anonymous inner class we should also
171-
// add the dispatcher methods
172-
173-
// add method dispatcher
174-
BlockStatement block = new BlockStatement();
175-
MethodNode method = classNode.addSyntheticMethod(
176-
"this$dist$invoke$" + objectDistance,
177-
ACC_PUBLIC,
178-
OBJECT_TYPE,
179-
params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
180-
ClassNode.EMPTY_ARRAY,
181-
block
182-
);
183-
setMethodDispatcherCode(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
184-
185-
// add property setter
186-
block = new BlockStatement();
187-
method = classNode.addSyntheticMethod(
188-
"this$dist$set$" + objectDistance,
189-
ACC_PUBLIC,
190-
VOID_TYPE,
191-
params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
192-
ClassNode.EMPTY_ARRAY,
193-
block
194-
);
195-
setPropertySetterDispatcher(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
196-
197-
// add property getter
198-
block = new BlockStatement();
199-
method = classNode.addSyntheticMethod(
200-
"this$dist$get$" + objectDistance,
201-
ACC_PUBLIC,
202-
OBJECT_TYPE,
203-
params(param(STRING_TYPE, "name")),
204-
ClassNode.EMPTY_ARRAY,
205-
block
206-
);
207-
setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
208-
}
209-
210-
private void getThis(final MethodVisitor mv, final String classInternalName, final String outerClassDescriptor, final String innerClassInternalName) {
211-
mv.visitVarInsn(ALOAD, 0);
212-
if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
213-
mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", BytecodeHelper.getTypeDescription(CLOSURE_TYPE));
214-
mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(CLOSURE_TYPE), "getThisObject", "()Ljava/lang/Object;", false);
215-
mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
216-
} else {
217-
mv.visitFieldInsn(GETFIELD, classInternalName, "this$0", outerClassDescriptor);
218-
}
219-
}
220-
221-
private void addMopMethods(final InnerClassNode node) {
222-
final boolean isStatic = isStatic(node);
223-
final ClassNode outerClass = node.getOuterClass();
224-
final int outerClassDistance = getObjectDistance(outerClass);
225-
final String classInternalName = BytecodeHelper.getClassInternalName(node);
226-
final String outerClassInternalName = getInternalName(outerClass, isStatic);
227-
final String outerClassDescriptor = getTypeDescriptor(outerClass, isStatic);
228-
229-
addMissingHandler(node,
230-
"methodMissing",
231-
ACC_PUBLIC,
232-
OBJECT_TYPE,
233-
params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
234-
(methodBody, parameters) -> {
235-
if (isStatic) {
236-
setMethodDispatcherCode(methodBody, classX(outerClass), parameters);
237-
} else {
238-
methodBody.addStatement(
239-
new BytecodeSequence(new BytecodeInstruction() {
240-
@Override
241-
public void visit(final MethodVisitor mv) {
242-
getThis(mv, classInternalName, outerClassDescriptor, outerClassInternalName);
243-
mv.visitVarInsn(ALOAD, 1);
244-
mv.visitVarInsn(ALOAD, 2);
245-
mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$invoke$" + outerClassDistance, "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
246-
mv.visitInsn(ARETURN);
247-
}
248-
})
249-
);
250-
}
251-
}
252-
);
253-
254-
ClassNode[] nameValueTypes = {STRING_TYPE, OBJECT_TYPE};
255-
MethodNode propertyMissing = getMethod(node, "propertyMissing", (m) -> !m.isStatic() && !m.isPrivate()
256-
&& Arrays.equals(Arrays.stream(m.getParameters()).map(Parameter::getType).toArray(),nameValueTypes));
257-
ClassNode returnType = propertyMissing != null ? propertyMissing.getReturnType() : VOID_TYPE; // GROOVY-11822
258-
259-
addMissingHandler(node,
260-
"propertyMissing",
261-
ACC_PUBLIC,
262-
returnType,
263-
params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
264-
(methodBody, parameters) -> {
265-
if (isStatic) {
266-
setPropertySetterDispatcher(methodBody, classX(outerClass), parameters);
267-
} else {
268-
methodBody.addStatement(
269-
new BytecodeSequence(new BytecodeInstruction() {
270-
@Override
271-
public void visit(final MethodVisitor mv) {
272-
getThis(mv, classInternalName, outerClassDescriptor, outerClassInternalName);
273-
mv.visitVarInsn(ALOAD, 1);
274-
mv.visitVarInsn(ALOAD, 2);
275-
mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$set$" + outerClassDistance, "(Ljava/lang/String;Ljava/lang/Object;)V", false);
276-
if (!ClassHelper.isPrimitiveVoid(returnType)) mv.visitInsn(ACONST_NULL);
277-
BytecodeHelper.doReturn(mv, returnType);
278-
}
279-
})
280-
);
281-
}
282-
}
283-
);
284-
285-
addMissingHandler(node,
286-
"propertyMissing",
287-
ACC_PUBLIC,
288-
OBJECT_TYPE,
289-
params(param(STRING_TYPE, "name")),
290-
(methodBody, parameters) -> {
291-
if (isStatic) {
292-
setPropertyGetterDispatcher(methodBody, classX(outerClass), parameters);
293-
} else {
294-
methodBody.addStatement(
295-
new BytecodeSequence(new BytecodeInstruction() {
296-
@Override
297-
public void visit(final MethodVisitor mv) {
298-
getThis(mv, classInternalName, outerClassDescriptor, outerClassInternalName);
299-
mv.visitVarInsn(ALOAD, 1);
300-
mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$get$" + outerClassDistance, "(Ljava/lang/String;)Ljava/lang/Object;", false);
301-
mv.visitInsn(ARETURN);
302-
}
303-
})
304-
);
305-
}
306-
}
307-
);
308-
}
309-
310-
private void addMissingHandler(final InnerClassNode innerClass, final String methodName, final int modifiers,
311-
final ClassNode returnType, final Parameter[] parameters, final BiConsumer<BlockStatement, Parameter[]> consumer) {
312-
MethodNode method = innerClass.getDeclaredMethod(methodName, parameters);
313-
if (method == null) {
314-
// try {
315-
// <consumer dispatch>
316-
// } catch (MissingMethodException notFound) {
317-
// throw new MissingMethodException(notFound.method, this, notFound.arguments)
318-
// }
319-
Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy type
320-
ClassNode exceptionT;
321-
Expression newException;
322-
if (methodName.endsWith("methodMissing")) {
323-
exceptionT = ClassHelper.make(groovy.lang.MissingMethodException.class);
324-
newException = ctorX(exceptionT, args(callX(varX(catchParam),"getMethod"), callThisX("getClass"), callX(varX(catchParam),"getArguments")));
325-
} else {
326-
exceptionT = ClassHelper.make(groovy.lang.MissingPropertyException.class);
327-
newException = ctorX(exceptionT, args(callX(varX(catchParam),"getProperty"), callThisX("getClass"), callX(varX(catchParam),"getCause")));
328-
}
329-
catchParam.setType(exceptionT);
330-
catchParam.setOriginType(exceptionT);
331-
BlockStatement handleMissing = block();
332-
consumer.accept(handleMissing, parameters);
333-
TryCatchStatement tryCatch = tryCatchS(handleMissing);
334-
tryCatch.addCatch(catchS(catchParam, throwS(newException)));
335-
336-
innerClass.addSyntheticMethod(methodName, modifiers, returnType, parameters, ClassNode.EMPTY_ARRAY, tryCatch);
337-
338-
// if there is a user-defined method, add compiler error and continue
339-
} else if (isStatic(innerClass) && (method.getModifiers() & ACC_SYNTHETIC) == 0) {
340-
addError("\"" + methodName + "\" implementations are not supported on static inner classes as " +
341-
"a synthetic version of \"" + methodName + "\" is added during compilation for the purpose " +
342-
"of outer class delegation.",
343-
method);
344-
}
345-
}
346-
347-
private void addThisReference(ConstructorNode node) {
116+
private void addThisReference(final ConstructorNode node) {
348117
if (!shouldHandleImplicitThisForInnerClass(classNode)) return;
349118

350119
// add "this$0" field init
@@ -396,7 +165,7 @@ private void addThisReference(ConstructorNode node) {
396165
node.setCode(block);
397166
}
398167

399-
private boolean shouldImplicitlyPassThisZero(ConstructorCallExpression cce) {
168+
private boolean shouldImplicitlyPassThisZero(final ConstructorCallExpression cce) {
400169
boolean pass = false;
401170
if (cce.isThisCall()) {
402171
pass = true;
@@ -415,7 +184,7 @@ private boolean shouldImplicitlyPassThisZero(ConstructorCallExpression cce) {
415184
return pass;
416185
}
417186

418-
private String getUniqueName(Parameter[] params, ConstructorNode node) {
187+
private String getUniqueName(final Parameter[] params, final ConstructorNode node) {
419188
String namePrefix = "$p";
420189
outer:
421190
for (int i = 0; i < 100; i++) {

0 commit comments

Comments
 (0)