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 2022/03/25 21:01:32 UTC
[groovy] branch GROOVY_3_0_X updated: GROOVY-8244: proxy for trait/interface only overrides abstract method(s)
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
new 6e9b60a GROOVY-8244: proxy for trait/interface only overrides abstract method(s)
6e9b60a is described below
commit 6e9b60aa33243fcb924ac0d66bde45e01045e40a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Mar 25 14:45:05 2022 -0500
GROOVY-8244: proxy for trait/interface only overrides abstract method(s)
3_0_X backport
---
.../org/codehaus/groovy/classgen/Verifier.java | 366 ++++++++++-----------
.../groovy/runtime/ProxyGeneratorAdapter.java | 48 ++-
.../traitx/TraitASTTransformationTest.groovy | 13 +
3 files changed, 213 insertions(+), 214 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 492c559..e333fde 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -19,7 +19,6 @@
package org.codehaus.groovy.classgen;
import groovy.lang.GroovyClassLoader;
-import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
@@ -101,6 +100,7 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.bytecodeX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.castX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName;
@@ -142,9 +142,9 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
public static final String DEFAULT_PARAMETER_GENERATED = "DEFAULT_PARAMETER_GENERATED";
private static final Parameter[] SET_METACLASS_PARAMS = {new Parameter(ClassHelper.METACLASS_TYPE, "mc")};
- private static final Class<?> GENERATED_ANNOTATION = Generated.class;
- private static final Class<?> INTERNAL_ANNOTATION = Internal.class;
- private static final Class<?> TRANSIENT_ANNOTATION = Transient.class;
+ private static final ClassNode GENERATED_ANNOTATION = ClassHelper.make(Generated.class);
+ private static final ClassNode INTERNAL_ANNOTATION = ClassHelper.make(Internal .class);
+ private static final ClassNode TRANSIENT_ANNOTATION = ClassHelper.make(Transient.class);
// NOTE: timeStamp constants shouldn't belong to Verifier but kept here for binary compatibility
public static final String __TIMESTAMP = "__timeStamp";
@@ -165,83 +165,73 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
return methodNode;
}
- private static FieldNode setMetaClassFieldIfNotExists(final ClassNode node, FieldNode metaClassField) {
- if (metaClassField != null) return metaClassField;
- final String classInternalName = BytecodeHelper.getClassInternalName(node);
- metaClassField =
- node.addField("metaClass", ACC_PRIVATE | ACC_TRANSIENT | ACC_SYNTHETIC, ClassHelper.METACLASS_TYPE,
- bytecodeX(ClassHelper.METACLASS_TYPE, mv -> {
- mv.visitVarInsn(ALOAD, 0);
- mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
- })
- );
- metaClassField.setSynthetic(true);
- return metaClassField;
- }
-
private static FieldNode getMetaClassField(final ClassNode node) {
- FieldNode ret = node.getDeclaredField("metaClass");
- if (ret != null) {
- ClassNode mcFieldType = ret.getType();
+ FieldNode metaClassField = node.getDeclaredField("metaClass");
+ if (metaClassField != null) {
+ ClassNode mcFieldType = metaClassField.getType();
if (!mcFieldType.equals(ClassHelper.METACLASS_TYPE)) {
throw new RuntimeParserException("The class " + node.getName() +
" cannot declare field 'metaClass' of type " + mcFieldType.getName() + " as it needs to be of " +
- "the type " + ClassHelper.METACLASS_TYPE.getName() + " for internal groovy purposes", ret);
+ "the type " + ClassHelper.METACLASS_TYPE.getName() + " for internal groovy purposes", metaClassField);
}
- return ret;
+ return metaClassField;
}
ClassNode current = node;
- while (current != ClassHelper.OBJECT_TYPE) {
+ while (!ClassHelper.OBJECT_TYPE.equals(current)) {
current = current.getSuperClass();
if (current == null) break;
- ret = current.getDeclaredField("metaClass");
- if (ret == null) continue;
- if (isPrivate(ret.getModifiers())) continue;
- return ret;
+ metaClassField = current.getDeclaredField("metaClass");
+ if (metaClassField == null) continue;
+ if (isPrivate(metaClassField.getModifiers())) continue;
+ return metaClassField;
}
return null;
}
+ private static FieldNode setMetaClassFieldIfNotExists(final ClassNode node, FieldNode metaClassField) {
+ if (metaClassField == null) {
+ String classInternalName = BytecodeHelper.getClassInternalName(node);
+ metaClassField =
+ node.addField("metaClass", ACC_PRIVATE | ACC_SYNTHETIC | ACC_TRANSIENT, ClassHelper.METACLASS_TYPE,
+ bytecodeX(ClassHelper.METACLASS_TYPE, mv -> {
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
+ })
+ );
+ metaClassField.setSynthetic(true);
+ }
+ return metaClassField;
+ }
+
+ //--------------------------------------------------------------------------
+
@Override
public void visitClass(final ClassNode node) {
this.classNode = node;
- if (classNode.isInterface()
- || Traits.isTrait(node)) { // maybe possible to have this true in joint compilation mode
- //interfaces have no constructors, but this code expects one,
- //so create a dummy and don't add it to the class node
- ConstructorNode dummy = new ConstructorNode(0, null);
- addInitialization(node, dummy);
+ checkForDuplicateInterfaces(node);
+
+ if (node.isInterface() || Traits.isTrait(node)) {
+ // interfaces have no constructors but this expects one,
+ // so create a dummy but do not add it to the class node
+ addInitialization(node, new ConstructorNode(0, null));
node.visitContents(this);
- if (classNode.getNodeMetaData(ClassNodeSkip.class) == null) {
- classNode.setNodeMetaData(ClassNodeSkip.class, true);
+ if (node.getNodeMetaData(ClassNodeSkip.class) == null) {
+ node.setNodeMetaData(ClassNodeSkip.class, Boolean.TRUE);
}
return;
}
- ClassNode[] classNodes = classNode.getInterfaces();
- List<String> interfaces = new ArrayList<>();
- for (ClassNode classNode : classNodes) {
- interfaces.add(classNode.getName());
- }
- Set<String> interfaceSet = new HashSet<>(interfaces);
- if (interfaceSet.size() != interfaces.size()) {
- throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
- }
-
addDefaultParameterMethods(node);
addDefaultParameterConstructors(node);
- final String classInternalName = BytecodeHelper.getClassInternalName(node);
+ String classInternalName = BytecodeHelper.getClassInternalName(node);
addStaticMetaClassField(node, classInternalName);
- boolean knownSpecialCase =
- node.isDerivedFrom(ClassHelper.GSTRING_TYPE)
- || node.isDerivedFrom(ClassHelper.GROOVY_OBJECT_SUPPORT_TYPE);
-
- addFastPathHelperFieldsAndHelperMethod(node, classInternalName, knownSpecialCase);
- if (!knownSpecialCase) addGroovyObjectInterfaceAndMethods(node, classInternalName);
+ addFastPathHelperFieldsAndHelperMethod(node, classInternalName);
+ if (!node.isDerivedFrom(ClassHelper.GROOVY_OBJECT_SUPPORT_TYPE))
+ addGroovyObjectInterfaceAndMethods(node, classInternalName);
addDefaultConstructor(node);
@@ -251,7 +241,6 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
node.visitContents(this);
checkForDuplicateMethods(node);
addCovariantMethods(node);
-
checkFinalVariables(node);
}
@@ -283,6 +272,20 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
};
}
+ private static void checkForDuplicateInterfaces(final ClassNode cn) {
+ ClassNode[] interfaces = cn.getInterfaces();
+ int nInterfaces = interfaces.length;
+ if (nInterfaces == 0) return;
+
+ if (nInterfaces > 1) {
+ List<String> interfaceNames = new ArrayList<>(nInterfaces);
+ for (ClassNode in : interfaces) interfaceNames.add(in.getName());
+ if (interfaceNames.size() != new HashSet<>(interfaceNames).size()) {
+ throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaceNames, cn);
+ }
+ }
+ }
+
private static void checkForDuplicateMethods(final ClassNode cn) {
Set<String> descriptors = new HashSet<>();
for (MethodNode mn : cn.getMethods()) {
@@ -322,8 +325,8 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
return null;
}
- private static void addFastPathHelperFieldsAndHelperMethod(final ClassNode node, final String classInternalName, final boolean knownSpecialCase) {
- if (node.getNodeMetaData(ClassNodeSkip.class) != null) return;
+ private static void addFastPathHelperFieldsAndHelperMethod(final ClassNode node, final String classInternalName) {
+ if (Boolean.TRUE.equals(node.getNodeMetaData(ClassNodeSkip.class))) return;
FieldNode stMCB = checkFieldDoesNotExist(node, STATIC_METACLASS_BOOL);
if (stMCB == null) {
stMCB = node.addField(
@@ -401,71 +404,60 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
protected void addGroovyObjectInterfaceAndMethods(final ClassNode node, final String classInternalName) {
- if (!node.isDerivedFromGroovyObject()) node.addInterface(ClassHelper.make(GroovyObject.class));
+ if (!node.isDerivedFromGroovyObject()) node.addInterface(ClassHelper.GROOVY_OBJECT_TYPE);
FieldNode metaClassField = getMetaClassField(node);
- boolean shouldAnnotate = classNode.getModule().getContext() != null;
- AnnotationNode generatedAnnotation = shouldAnnotate ? new AnnotationNode(ClassHelper.make(GENERATED_ANNOTATION)) : null;
- AnnotationNode internalAnnotation = shouldAnnotate ? new AnnotationNode(ClassHelper.make(INTERNAL_ANNOTATION)) : null;
- AnnotationNode transientAnnotation = shouldAnnotate ? new AnnotationNode(ClassHelper.make(TRANSIENT_ANNOTATION)) : null;
-
+ boolean shouldAnnotate = (classNode.getModule().getContext() != null);
if (!node.hasMethod("getMetaClass", Parameter.EMPTY_ARRAY)) {
metaClassField = setMetaClassFieldIfNotExists(node, metaClassField);
- MethodNode methodNode = addMethod(node, !shouldAnnotate,
- "getMetaClass",
- ACC_PUBLIC,
- ClassHelper.METACLASS_TYPE,
- Parameter.EMPTY_ARRAY,
- ClassNode.EMPTY_ARRAY,
- new BytecodeSequence(new BytecodeInstruction() {
- @Override
- public void visit(final MethodVisitor mv) {
- Label nullLabel = new Label();
- /*
- * the code is:
- * if (this.metaClass==null) {
- * this.metaClass = this.$getStaticMetaClass()
- * return this.metaClass
- * } else {
- * return this.metaClass
- * }
- * with the optimization that the result of the
- * first this.metaClass is duped on the operand
- * stack and reused for the return in the else part
- */
- mv.visitVarInsn(ALOAD, 0);
- mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
- mv.visitInsn(DUP);
- mv.visitJumpInsn(IFNULL, nullLabel);
- mv.visitInsn(ARETURN);
-
- mv.visitLabel(nullLabel);
- mv.visitInsn(POP);
- mv.visitVarInsn(ALOAD, 0);
- mv.visitInsn(DUP);
- mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
- mv.visitFieldInsn(PUTFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
- mv.visitVarInsn(ALOAD, 0);
- mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
- mv.visitInsn(ARETURN);
- }
- })
- );
+ Statement getMetaClassCode = new BytecodeSequence(new BytecodeInstruction() {
+ @Override
+ public void visit(final MethodVisitor mv) {
+ Label nullLabel = new Label();
+ /*
+ * the code is:
+ * if (this.metaClass==null) {
+ * this.metaClass = this.$getStaticMetaClass()
+ * return this.metaClass
+ * } else {
+ * return this.metaClass
+ * }
+ * with the optimization that the result of the
+ * first this.metaClass is duped on the operand
+ * stack and reused for the return in the else part
+ */
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+ mv.visitInsn(DUP);
+ mv.visitJumpInsn(IFNULL, nullLabel);
+ mv.visitInsn(ARETURN);
+
+ mv.visitLabel(nullLabel);
+ mv.visitInsn(POP);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
+ mv.visitFieldInsn(PUTFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+ mv.visitInsn(ARETURN);
+ }
+ });
+
+ MethodNode methodNode = addMethod(node, !shouldAnnotate, "getMetaClass", ACC_PUBLIC,
+ ClassHelper.METACLASS_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getMetaClassCode);
if (shouldAnnotate) {
- methodNode.addAnnotation(generatedAnnotation);
- methodNode.addAnnotation(internalAnnotation);
- methodNode.addAnnotation(transientAnnotation);
+ methodNode.addAnnotation(new AnnotationNode(GENERATED_ANNOTATION));
+ methodNode.addAnnotation(new AnnotationNode(INTERNAL_ANNOTATION ));
+ methodNode.addAnnotation(new AnnotationNode(TRANSIENT_ANNOTATION));
}
}
- Parameter[] parameters = new Parameter[]{new Parameter(ClassHelper.METACLASS_TYPE, "mc")};
- if (!node.hasMethod("setMetaClass", parameters)) {
+ if (!node.hasMethod("setMetaClass", SET_METACLASS_PARAMS)) {
metaClassField = setMetaClassFieldIfNotExists(node, metaClassField);
Statement setMetaClassCode;
if (isFinal(metaClassField.getModifiers())) {
- ConstantExpression text = new ConstantExpression("cannot set read-only meta class");
- ConstructorCallExpression cce = new ConstructorCallExpression(ClassHelper.make(IllegalArgumentException.class), text);
- setMetaClassCode = new ExpressionStatement(cce);
+ setMetaClassCode = stmt(ctorX(ClassHelper.make(IllegalArgumentException.class), constX("cannot set read-only meta class")));
} else {
setMetaClassCode = new BytecodeSequence(new BytecodeInstruction() {
@Override
@@ -476,22 +468,17 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
*/
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
- mv.visitFieldInsn(PUTFIELD, classInternalName,
- "metaClass", "Lgroovy/lang/MetaClass;");
+ mv.visitFieldInsn(PUTFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
mv.visitInsn(RETURN);
}
});
}
- MethodNode methodNode = addMethod(node, !shouldAnnotate,
- "setMetaClass",
- ACC_PUBLIC, ClassHelper.VOID_TYPE,
- SET_METACLASS_PARAMS, ClassNode.EMPTY_ARRAY,
- setMetaClassCode
- );
+ MethodNode methodNode = addMethod(node, !shouldAnnotate, "setMetaClass", ACC_PUBLIC,
+ ClassHelper.VOID_TYPE, SET_METACLASS_PARAMS, ClassNode.EMPTY_ARRAY, setMetaClassCode);
if (shouldAnnotate) {
- methodNode.addAnnotation(generatedAnnotation);
- methodNode.addAnnotation(internalAnnotation);
+ methodNode.addAnnotation(new AnnotationNode(GENERATED_ANNOTATION));
+ methodNode.addAnnotation(new AnnotationNode(INTERNAL_ANNOTATION ));
}
}
}
@@ -638,10 +625,10 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
Parameter[] params = node.getParameters();
if (params.length == 1) {
Parameter param = params[0];
- if (param.getType() == null || param.getType() == ClassHelper.OBJECT_TYPE) {
+ if (param.getType() == null || ClassHelper.OBJECT_TYPE.equals(param.getType())) {
param.setType(ClassHelper.STRING_TYPE.makeArray());
ClassNode returnType = node.getReturnType();
- if (returnType == ClassHelper.OBJECT_TYPE) {
+ if (ClassHelper.OBJECT_TYPE.equals(returnType)) {
node.setReturnType(ClassHelper.VOID_TYPE);
}
}
@@ -662,7 +649,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
// no method found, we need to replace
if (m == null) return true;
// method is in current class, nothing to be done
- if (m.getDeclaringClass() == this.getClassNode()) return false;
+ if (m.getDeclaringClass() == getClassNode()) return false;
// do not overwrite final
if (isFinal(m.getModifiers())) return false;
return true;
@@ -737,18 +724,20 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
Parameter[] parameters = method.getParameters();
ClassNode methodReturnType = method.getReturnType();
for (MethodNode node : classNode.getAbstractMethods()) {
- if (!node.getDeclaringClass().equals(classNode)) continue;
- if (node.getName().equals(methodName) && node.getParameters().length == parameters.length) {
- if (parameters.length == 1) {
- // setter
+ if (node.getName().equals(methodName)
+ && node.getDeclaringClass().equals(classNode)
+ && node.getParameters().length == parameters.length) {
+ if (parameters.length == 1) { // setter
ClassNode abstractMethodParameterType = node.getParameters()[0].getType();
ClassNode methodParameterType = parameters[0].getType();
- if (!methodParameterType.isDerivedFrom(abstractMethodParameterType) && !methodParameterType.implementsInterface(abstractMethodParameterType)) {
+ if (!methodParameterType.isDerivedFrom(abstractMethodParameterType)
+ && !methodParameterType.implementsInterface(abstractMethodParameterType)) {
continue;
}
}
ClassNode nodeReturnType = node.getReturnType();
- if (!methodReturnType.isDerivedFrom(nodeReturnType) && !methodReturnType.implementsInterface(nodeReturnType)) {
+ if (!methodReturnType.isDerivedFrom(nodeReturnType)
+ && !methodReturnType.implementsInterface(nodeReturnType)) {
continue;
}
// matching method, remove abstract status and use the same body
@@ -769,9 +758,9 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
protected void addDefaultParameterMethods(final ClassNode type) {
List<MethodNode> methods = new ArrayList<>(type.getMethods());
addDefaultParameters(methods, (arguments, params, method) -> {
- BlockStatement code = new BlockStatement();
+ BlockStatement block = new BlockStatement();
- MethodNode newMethod = new MethodNode(method.getName(), method.getModifiers(), method.getReturnType(), params, method.getExceptions(), code);
+ MethodNode newMethod = new MethodNode(method.getName(), method.getModifiers() & ~ACC_ABSTRACT, method.getReturnType(), params, method.getExceptions(), block);
MethodNode oldMethod = type.getDeclaredMethod(method.getName(), params);
if (oldMethod != null) {
@@ -782,10 +771,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
sourceOf(method));
}
- List<AnnotationNode> annotations = method.getAnnotations();
- if (annotations != null && !annotations.isEmpty()) {
- newMethod.addAnnotations(annotations);
- }
+ newMethod.addAnnotations(method.getAnnotations());
newMethod.setGenericsTypes(method.getGenericsTypes());
// GROOVY-5632, GROOVY-9151: check for references to parameters that have been removed
@@ -804,7 +790,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
if (e.getAccessedVariable() instanceof Parameter) {
Parameter p = (Parameter) e.getAccessedVariable();
if (p.hasInitialExpression() && !Arrays.asList(params).contains(p)) {
- VariableScope blockScope = code.getVariableScope();
+ VariableScope blockScope = block.getVariableScope();
VariableExpression localVariable = (VariableExpression) blockScope.getDeclaredVariable(p.getName());
if (localVariable == null) {
// create a variable declaration so that the name can be found in the new method
@@ -812,7 +798,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
localVariable.setModifiers(p.getModifiers());
blockScope.putDeclaredVariable(localVariable);
localVariable.setInStaticContext(blockScope.isInStaticContext());
- code.addStatement(declS(localVariable, p.getInitialExpression()));
+ block.addStatement(declS(localVariable, p.getInitialExpression()));
}
if (!localVariable.isClosureSharedVariable()) {
localVariable.setClosureSharedVariable(inClosure);
@@ -832,7 +818,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
for (Parameter p : method.getParameters()) {
if (p.hasInitialExpression() && p.getInitialExpression() == argument) {
- if (code.getVariableScope().getDeclaredVariable(p.getName()) != null) {
+ if (block.getVariableScope().getDeclaredVariable(p.getName()) != null) {
it.set(varX(p.getName()));
}
break;
@@ -846,9 +832,9 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
call.setImplicitThis(true);
if (method.isVoidMethod()) {
- code.addStatement(new ExpressionStatement(call));
+ block.addStatement(new ExpressionStatement(call));
} else {
- code.addStatement(new ReturnStatement(call));
+ block.addStatement(new ReturnStatement(call));
}
// GROOVY-5681: set anon. inner enclosing method reference
@@ -861,7 +847,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
super.visitConstructorCallExpression(call);
}
};
- visitor.visitBlockStatement(code);
+ visitor.visitBlockStatement(block);
addPropertyMethod(newMethod);
newMethod.putNodeMetaData(DEFAULT_PARAMETER_GENERATED, Boolean.TRUE);
@@ -1011,34 +997,24 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
protected void addInitialization(final ClassNode node, final ConstructorNode constructorNode) {
Statement firstStatement = constructorNode.getFirstStatement();
- // if some transformation decided to generate constructor then it probably knows who it does
- if (firstStatement instanceof BytecodeSequence)
- return;
- ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
+ // if some transformation decided to generate constructor then it probably knows best
+ if (firstStatement instanceof BytecodeSequence) return;
- // in case of this(...) let the other constructor do the init
- if (first != null && (first.isThisCall())) return;
+ ConstructorCallExpression specialCtorCall = getFirstIfSpecialConstructorCall(firstStatement);
- List<Statement> statements = new ArrayList<Statement>();
- List<Statement> staticStatements = new ArrayList<Statement>();
- final boolean isEnum = node.isEnum();
- List<Statement> initStmtsAfterEnumValuesInit = new ArrayList<Statement>();
- Set<String> explicitStaticPropsInEnum = new HashSet<String>();
- if (isEnum) {
- for (PropertyNode propNode : node.getProperties()) {
- if (!propNode.isSynthetic() && propNode.getField().isStatic()) {
- explicitStaticPropsInEnum.add(propNode.getField().getName());
- }
- }
- for (FieldNode fieldNode : node.getFields()) {
- if (!fieldNode.isSynthetic() && fieldNode.isStatic() && fieldNode.getType() != node) {
- explicitStaticPropsInEnum.add(fieldNode.getName());
- }
- }
- }
+ // in case of this(...) let the other constructor initialize
+ if (specialCtorCall != null && (specialCtorCall.isThisCall())) return;
+
+ boolean isEnum = node.isEnum();
+ List<Statement> statements = new ArrayList<>();
+ List<Statement> staticStatements = new ArrayList<>();
+ List<Statement> initStmtsAfterEnumValuesInit = new ArrayList<>();
if (!Traits.isTrait(node)) {
+ Set<String> explicitStaticPropsInEnum = !isEnum
+ ? Collections.emptySet() : getExplicitStaticProperties(node);
+
for (FieldNode fn : node.getFields()) {
addFieldInitialization(statements, staticStatements, fn, isEnum,
initStmtsAfterEnumValuesInit, explicitStaticPropsInEnum);
@@ -1046,25 +1022,26 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
BlockStatement block = getCodeAsBlock(constructorNode);
- List<Statement> otherStatements = block.getStatements();
- if (!otherStatements.isEmpty()) {
- if (first != null) {
- // it is super(..) since this(..) is already covered
- otherStatements.remove(0);
+ List<Statement> blockStatements = block.getStatements();
+ if (!blockStatements.isEmpty()) {
+ if (specialCtorCall != null) {
+ blockStatements.remove(0);
statements.add(0, firstStatement);
// GROOVY-7686: place local variable references above super ctor call
if (node instanceof InnerClassNode && ((InnerClassNode) node).isAnonymous()) {
extractVariableReferenceInitializers(statements).forEach(s -> statements.add(0, s));
}
}
- Statement stmtThis$0 = getImplicitThis$0StmtIfInnerClass(otherStatements);
- if (stmtThis$0 != null) {
- // since there can be field init statements that depend on method/property dispatching
- // that uses this$0, it needs to bubble up before the super call itself (GROOVY-4471)
- statements.add(0, stmtThis$0);
+ if (node instanceof InnerClassNode) {
+ // GROOVY-4471: place this$0 init above other field init and super ctor call;
+ // there can be field initializers that depend on method/property dispatching
+ Statement initThis$0 = getImplicitThis$0Stmt(blockStatements);
+ if (initThis$0 != null) {
+ statements.add(0, initThis$0);
+ }
}
statements.addAll(node.getObjectInitializerStatements());
- statements.addAll(otherStatements);
+ statements.addAll(blockStatements);
} else {
statements.addAll(node.getObjectInitializerStatements());
}
@@ -1075,10 +1052,8 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
if (!staticStatements.isEmpty()) {
if (isEnum) {
- /*
- * GROOVY-3161: initialize statements for explicitly declared static fields
- * inside an enum should come after enum values are initialized
- */
+ // GROOVY-3161: initialization statements for explicitly declared static
+ // fields inside an enum should come after enum values are initialized
staticStatements.removeAll(initStmtsAfterEnumValuesInit);
node.addStaticInitializerStatements(staticStatements, true);
if (!initStmtsAfterEnumValuesInit.isEmpty()) {
@@ -1090,34 +1065,48 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
}
}
- /*
+ private static Set<String> getExplicitStaticProperties(final ClassNode cn) {
+ Set<String> staticProperties = new HashSet<>();
+ for (PropertyNode pn : cn.getProperties()) {
+ if (!pn.isSynthetic() && pn.getField().isStatic()) {
+ staticProperties.add(pn.getField().getName());
+ }
+ }
+ for (FieldNode fn : cn.getFields()) {
+ if (!fn.isSynthetic() && fn.isStatic() && fn.getType() != cn) {
+ staticProperties.add(fn.getName());
+ }
+ }
+ return staticProperties;
+ }
+
+ /**
* When InnerClassVisitor adds <code>this.this$0 = $p$n</code>, it adds it
* as a BlockStatement having that ExpressionStatement.
*/
- private Statement getImplicitThis$0StmtIfInnerClass(final List<Statement> otherStatements) {
- if (!(classNode instanceof InnerClassNode)) return null;
- for (Statement stmt : otherStatements) {
+ private static Statement getImplicitThis$0Stmt(final List<Statement> statements) {
+ for (Statement stmt : statements) {
if (stmt instanceof BlockStatement) {
List<Statement> stmts = ((BlockStatement) stmt).getStatements();
for (Statement bstmt : stmts) {
if (bstmt instanceof ExpressionStatement) {
- if (extractImplicitThis$0StmtIfInnerClassFromExpression(stmts, bstmt)) return bstmt;
+ if (extractImplicitThis$0StmtFromExpression(stmts, bstmt)) return bstmt;
}
}
} else if (stmt instanceof ExpressionStatement) {
- if (extractImplicitThis$0StmtIfInnerClassFromExpression(otherStatements, stmt)) return stmt;
+ if (extractImplicitThis$0StmtFromExpression(statements, stmt)) return stmt;
}
}
return null;
}
- private static boolean extractImplicitThis$0StmtIfInnerClassFromExpression(final List<Statement> stmts, final Statement bstmt) {
- Expression expr = ((ExpressionStatement) bstmt).getExpression();
+ private static boolean extractImplicitThis$0StmtFromExpression(final List<Statement> stmts, final Statement exprStmt) {
+ Expression expr = ((ExpressionStatement) exprStmt).getExpression();
if (expr instanceof BinaryExpression) {
Expression lExpr = ((BinaryExpression) expr).getLeftExpression();
if (lExpr instanceof FieldExpression) {
if ("this$0".equals(((FieldExpression) lExpr).getFieldName())) {
- stmts.remove(bstmt); // remove from here and let the caller reposition it
+ stmts.remove(exprStmt); // remove from here and let the caller reposition it
return true;
}
}
@@ -1201,6 +1190,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
protected Statement createGetterBlock(final PropertyNode propertyNode, final FieldNode field) {
return new BytecodeSequence(new BytecodeInstruction() {
+ @Override
public void visit(final MethodVisitor mv) {
if (field.isStatic()) {
mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
diff --git a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
index 6869e83..6e7ccf5 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ProxyGeneratorAdapter.java
@@ -492,40 +492,35 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
+ // do not generate bytecode for final, native, private or synthetic methods
+ if ((access & (ACC_FINAL | ACC_NATIVE | ACC_PRIVATE | ACC_SYNTHETIC)) != 0) return null;
+
Object key = Arrays.asList(name, desc);
- if (visitedMethods.contains(key)) return null;
- if (Modifier.isPrivate(access) || Modifier.isNative(access) || ((access & ACC_SYNTHETIC) != 0)) {
- // do not generate bytecode for private methods
- return null;
- }
- int accessFlags = access;
- visitedMethods.add(key);
- if ((objectDelegateMethods.contains(name + desc) || delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) {
- if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
- if (Modifier.isAbstract(access)) {
- // prevents the proxy from being abstract
- accessFlags -= ACC_ABSTRACT;
- }
- if (delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) {
+ if (!visitedMethods.add(key)) return null;
+
+ boolean objectDelegate = objectDelegateMethods.contains(name + desc);
+ boolean closureDelegate = delegatedClosures.containsKey(name);
+ boolean wildcardDelegate = hasWildcard && !"<init>".equals(name);
+
+ if ((objectDelegate || closureDelegate || wildcardDelegate) && !Modifier.isStatic(access)) {
+ if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)
+ // GROOVY-8244: proxy for abstract class/trait/interface only overrides abstract method(s)
+ && (!Modifier.isAbstract(superClass.getModifiers()) || !isImplemented(superClass, name, desc))) {
+
+ if (closureDelegate || wildcardDelegate || !(objectDelegate && generateDelegateField)) {
delegatedClosures.put(name, Boolean.TRUE);
- return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
+ return makeDelegateToClosureCall(name, desc, signature, exceptions, access & ~ACC_ABSTRACT);
}
- if (generateDelegateField && objectDelegateMethods.contains(name + desc)) {
- return makeDelegateCall(name, desc, signature, exceptions, accessFlags);
- }
- delegatedClosures.put(name, Boolean.TRUE);
- return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
+ return makeDelegateCall(name, desc, signature, exceptions, access & ~ACC_ABSTRACT);
}
} else if ("getProxyTarget".equals(name) && "()Ljava/lang/Object;".equals(desc)) {
return createGetProxyTargetMethod(access, name, desc, signature, exceptions);
+
} else if ("<init>".equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
return createConstructor(access, name, desc, signature, exceptions);
- } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
- if (isImplemented(superClass, name, desc)) {
- return null;
- }
- accessFlags -= ACC_ABSTRACT;
- MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
+
+ } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name) && !isImplemented(superClass, name, desc)) {
+ MethodVisitor mv = super.visitMethod(access & ~ACC_ABSTRACT, name, desc, signature, exceptions);
mv.visitCode();
Type[] args = Type.getArgumentTypes(desc);
if (emptyBody) {
@@ -564,6 +559,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
}
mv.visitEnd();
}
+
return null;
}
diff --git a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 133b960..be886ad 100644
--- a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -1578,6 +1578,19 @@ final class TraitASTTransformationTest {
'''
}
+ @Test // GROOVY-8244
+ void testSAMCoercion6() {
+ assertScript '''
+ trait T {
+ abstract def foo(int a, int b = 2)
+ }
+ T t = { int a, int b ->
+ return a + b
+ }
+ assert t.foo(40) == 42
+ '''
+ }
+
@Test
void testMethodMissingInTrait() {
assertScript '''