Skip to content

Commit

Permalink
GROOVY-11512: trait boolean property generates isser and getter method
Browse files Browse the repository at this point in the history
unless either method is explicitly declared
  • Loading branch information
eric-milles committed Nov 1, 2024
1 parent 65506f4 commit 88c6336
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/main/java/org/codehaus/groovy/ast/PropertyNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public String getGetterName() {
public String getGetterNameOrDefault() {
if (getterName != null) return getterName;
String defaultName = "get" + capitalize(getName());
if (ClassHelper.boolean_TYPE.equals(getOriginType())
if (ClassHelper.isPrimitiveBoolean(getOriginType())
&& !getDeclaringClass().hasMethod(defaultName, Parameter.EMPTY_ARRAY)) {
defaultName = "is" + capitalize(getName());
}
Expand Down
27 changes: 11 additions & 16 deletions src/main/java/org/codehaus/groovy/classgen/Verifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
import static org.codehaus.groovy.ast.tools.GeneralUtils.getInterfacesAndSuperInterfaces;
import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.ast.tools.GenericsUtils.addMethodGenerics;
Expand Down Expand Up @@ -778,41 +779,35 @@ public void visitProperty(final PropertyNode node) {
}
String setterName = node.getSetterNameOrDefault();

int accessorModifiers = adjustPropertyModifiersForMethod(node);

Statement getterBlock = node.getGetterBlock();
if (getterBlock == null) {
if (getterBlock == null && !node.isPrivate()) {
MethodNode getter = classNode.getGetterMethod(getterName, !node.isStatic());
if (getter == null && isPrimitiveBoolean(node.getType())) {
getter = classNode.getGetterMethod("is" + capitalize(name));
getter = classNode.getGetterMethod("is" + capitalize(name), !node.isStatic());
}
if (!node.isPrivate() && methodNeedsReplacement(getter)) {
if (methodNeedsReplacement(getter)) {
getterBlock = createGetterBlock(node, field);
}
}
Statement setterBlock = node.getSetterBlock();
if (setterBlock == null) {
if (setterBlock == null && !node.isPrivate() && !node.isFinal()) {
MethodNode setter = classNode.getSetterMethod(setterName, false); // atypical: allow setter with non-void return type
if (!node.isFinal() && !node.isPrivate() && methodNeedsReplacement(setter)) {
if (methodNeedsReplacement(setter)) {
setterBlock = createSetterBlock(node, field);
}
}

int accessorModifiers = adjustPropertyModifiersForMethod(node);

if (getterBlock != null) {
visitGetter(node, field, getterBlock, accessorModifiers, getterName);

if (node.getGetterName() == null && getterName.startsWith("get") && isPrimitiveBoolean(node.getType())) {
String altGetterName = "is" + capitalize(name);
MethodNode altGetter = classNode.getGetterMethod(altGetterName, !node.isStatic());
if (methodNeedsReplacement(altGetter)) {
visitGetter(node, field, getterBlock, accessorModifiers, altGetterName);
}
if (node.getGetterName() == null && isPrimitiveBoolean(node.getType())) {
visitGetter(node, field, getterBlock, accessorModifiers, "is" + capitalize(name));
}
}

if (setterBlock != null) {
Parameter[] setterParameterTypes = {new Parameter(node.getType(), "value")};
MethodNode setter = new MethodNode(setterName, accessorModifiers, ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock);
MethodNode setter = new MethodNode(setterName, accessorModifiers, ClassHelper.VOID_TYPE, params(param(node.getType(),"value")), ClassNode.EMPTY_ARRAY, setterBlock);
setter.setSynthetic(true);
addPropertyMethod(setter);
if (!field.isSynthetic()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean;
import static org.codehaus.groovy.ast.ClassHelper.isWrapperBoolean;
import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
Expand All @@ -94,6 +93,7 @@
import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.ast.tools.PropertyNodeUtils.adjustPropertyModifiersForMethod;
import static org.codehaus.groovy.transform.trait.SuperCallTraitTransformer.UNRESOLVED_HELPER_CLASS;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
Expand Down Expand Up @@ -451,51 +451,54 @@ private static BlockStatement getBlockStatement(final MethodNode initMethod, fin
private static void processProperty(final ClassNode cNode, final PropertyNode node) {
String name = node.getName();
FieldNode field = node.getField();
int propNodeModifiers = node.getModifiers() & 0x1F; // GROOVY-3726

String getterName = node.getGetterNameOrDefault();
String getterName = node.getGetterName();
if (getterName == null) { // GROOVY-11512
getterName = "get" + capitalize(name);
}
String setterName = node.getSetterNameOrDefault();

Statement getterBlock = node.getGetterBlock();
if (getterBlock == null) {
MethodNode getter = cNode.getGetterMethod(getterName);
if (getterBlock == null && !node.isPrivate()) {
MethodNode getter = cNode.getGetterMethod(getterName, !node.isStatic());
if (getter == null && isPrimitiveBoolean(node.getType())) {
getter = cNode.getGetterMethod("is" + capitalize(name));
getter = cNode.getGetterMethod("is" + capitalize(name), !node.isStatic());
}
if (!node.isPrivate() && methodNeedsReplacement(cNode, getter)) {
if (methodNeedsReplacement(cNode, getter)) {
getterBlock = stmt(fieldX(field));
}
}
Statement setterBlock = node.getSetterBlock();
if (setterBlock == null) {
// 2nd arg false below: though not usual, allow setter with non-void return type
MethodNode setter = cNode.getSetterMethod(setterName, false);
if (!node.isPrivate() && (propNodeModifiers & ACC_FINAL) == 0
&& methodNeedsReplacement(cNode, setter)) {
if (setterBlock == null && !node.isPrivate() && !node.isFinal()) {
MethodNode setter = cNode.getSetterMethod(setterName, /*void-only:*/false);
if (methodNeedsReplacement(cNode, setter)) {
setterBlock = assignS(fieldX(field), varX(name));
}
}

int methodModifiers = adjustPropertyModifiersForMethod(node); // GROOVY-3726

if (getterBlock != null) {
MethodNode getter = new MethodNode(getterName, propNodeModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
MethodNode getter = new MethodNode(getterName, methodModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
getter.setSynthetic(true);
// copyAnnotations(cNode, getter);
addGeneratedMethod(cNode, getter);

if (node.getGetterName() == null && getterName.startsWith("get") && (isPrimitiveBoolean(node.getType()) || isWrapperBoolean(node.getType()))) {
MethodNode secondGetter = new MethodNode("is" + capitalize(name), propNodeModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
if (methodNeedsReplacement(cNode, secondGetter)) {
secondGetter.setSynthetic(true);
addGeneratedMethod(cNode, secondGetter);
}
if (node.getGetterName() == null && isPrimitiveBoolean(node.getType())) {
getter = new MethodNode("is" + capitalize(name), methodModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
getter.setSynthetic(true);
// copyAnnotations(cNode, getter);
addGeneratedMethod(cNode, getter);
}
}
if (setterBlock != null) {
VariableExpression var = (VariableExpression) ((BinaryExpression) ((ExpressionStatement) setterBlock).getExpression()).getRightExpression();
Parameter setterParameter = new Parameter(node.getType(), name);
var.setAccessedVariable(setterParameter);

MethodNode setter = new MethodNode(setterName, propNodeModifiers, VOID_TYPE, new Parameter[]{setterParameter}, ClassNode.EMPTY_ARRAY, setterBlock);
MethodNode setter = new MethodNode(setterName, methodModifiers, VOID_TYPE, new Parameter[]{setterParameter}, ClassNode.EMPTY_ARRAY, setterBlock);
setter.setSynthetic(true);
// copyAnnotations(cNode, setter);
addGeneratedMethod(cNode, setter);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,88 @@ final class TraitASTTransformationTest {
trait Named {
String name
}
class Person implements Named {}
class Person implements Named {
}
def p = new Person(name:'Stromae')
assert p.getName() == 'Stromae'
assert p.name == 'Stromae'
'''
}

// GROOVY-11512
@Test
void testTraitWithProperty2() {
assertScript shell, '''
trait T {
boolean bee = true
}
class C implements T {
}
def c = new C()
assert c.bee == true
assert c.isBee() == true
assert c.getBee() == true
'''
}

@Test
void testTraitWithProperty3() {
assertScript shell, '''import static groovy.test.GroovyAssert.shouldFail
trait T {
boolean bee = true
boolean isBee() { bee }
}
class C implements T {
}
def c = new C()
assert c.bee == true
assert c.isBee() == true
shouldFail(MissingMethodException) {
c.getBee()
}
'''
}

@Test
void testTraitWithProperty4() {
assertScript shell, '''import static groovy.test.GroovyAssert.shouldFail
trait T {
boolean bee = true
boolean getBee() { bee }
}
class C implements T {
}
def c = new C()
assert c.bee == true
assert c.getBee() == true
shouldFail(MissingMethodException) {
c.isBee()
}
'''
}

@Test
void testTraitWithProperty5() {
assertScript shell, '''import static groovy.test.GroovyAssert.shouldFail
trait T {
Boolean bee = true
}
class C implements T {
}
def c = new C()
assert c.bee != null
assert c.getBee() != null
shouldFail(MissingMethodException) {
c.isBee()
}
'''
}

@Test
void testUpdatePropertyFromSelf() {
assertScript shell, '''
Expand Down

0 comments on commit 88c6336

Please sign in to comment.