Skip to content

Commit

Permalink
GROOVY-11372: STC: write property via extension method
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed May 10, 2024
1 parent 131992c commit ea129d3
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
Expand Down Expand Up @@ -1602,7 +1603,26 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
return true;
}

LinkedList<ClassNode> queue = new LinkedList<>();
// in case of a lookup on java.lang.Class, look for instance methods on Class
// as well; in case of static property access Class<Type> and Type are listed
boolean staticOnly = isClassClassNodeWrappingConcreteType(receiverType) ? false
: (receiver.getData() == null ? staticOnlyAccess : false);

List<MethodNode> setters = findSetters(wrapTypeIfNecessary(receiverType), setterName, /*voidOnly:*/false);
setters = allowStaticAccessToMember(setters, staticOnly);
// GROOVY-11319:
setters.removeIf(setter -> !hasAccessToMember(typeCheckingContext.getEnclosingClassNode(), setter.getDeclaringClass(), setter.getModifiers()));
// GROOVY-11372:
var loader = getSourceUnit().getClassLoader();
var dgmSet = (TreeSet<MethodNode>) findDGMMethodsForClassNode(loader, receiverType, setterName);
if (isPrimitiveType(receiverType)) findDGMMethodsForClassNode(loader, getWrapper(receiverType), setterName, dgmSet);
for (MethodNode method : dgmSet) {
if ((!staticOnly || method.isStatic()) && method.getParameters().length == 1) {
setters.add(method);
}
}

var queue = new LinkedList<ClassNode>();
queue.add(receiverType);
if (isPrimitiveType(receiverType)) {
queue.add(getWrapper(receiverType));
Expand All @@ -1618,11 +1638,6 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
Collections.addAll(queue, current.getInterfaces());
}

boolean staticOnly = (receiver.getData() == null ? staticOnlyAccess : false);
// in case of a lookup on java.lang.Class, look for instance methods on Class
// as well; in case of static property access Class<Type> and Type are listed
if (isClassClassNodeWrappingConcreteType(current)) staticOnly = false;

field = allowStaticAccessToMember(field, staticOnly);

// skip property/accessor checks for "x.@field"
Expand Down Expand Up @@ -1652,10 +1667,6 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
&& (!getter.isPublic() || (propertyName.matches("empty|class|metaClass") && !List.of(getTypeCheckingAnnotations()).contains(COMPILESTATIC_CLASSNODE)))))) {
getter = null;
}
List<MethodNode> setters = findSetters(current, setterName, /*voidOnly:*/false);
setters = allowStaticAccessToMember(setters, staticOnly);
// GROOVY-11319:
setters.removeIf(setter -> !hasAccessToMember(typeCheckingContext.getEnclosingClassNode(), setter.getDeclaringClass(), setter.getModifiers()));

if (readMode && getter != null && visitor != null) visitor.visitMethod(getter);

Expand Down Expand Up @@ -1709,9 +1720,10 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
}

// GROOVY-5568, GROOVY-9115, GROOVY-9123: the property may be defined by an extension
if (readMode) // GROOVY-11372
for (ClassNode dgmReceiver : isPrimitiveType(receiverType) ? new ClassNode[]{receiverType, getWrapper(receiverType)} : new ClassNode[]{receiverType}) {
Set<MethodNode> methods = findDGMMethodsForClassNode(getSourceUnit().getClassLoader(), dgmReceiver, getterName);
for (MethodNode method : findDGMMethodsForClassNode(getSourceUnit().getClassLoader(), dgmReceiver, isserName)) {
Set<MethodNode> methods = findDGMMethodsForClassNode(loader, dgmReceiver, getterName);
for (MethodNode method : findDGMMethodsForClassNode(loader, dgmReceiver, isserName)) {
if (isPrimitiveBoolean(method.getReturnType())) methods.add(method);
}
if (staticOnlyAccess && receiver.getData() == null && !isClassType(receiver.getType())) {
Expand All @@ -1732,7 +1744,7 @@ protected boolean existsProperty(final PropertyExpression pexp, final boolean re
}
ClassNode returnType = inferReturnTypeGenerics(dgmReceiver, getter, ArgumentListExpression.EMPTY_ARGUMENTS);
storeInferredTypeForPropertyExpression(pexp, returnType);
if (readMode) storeTargetMethod(pexp, getter);
storeTargetMethod(pexp, getter);
return true;
}
}
Expand Down
12 changes: 11 additions & 1 deletion src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
}

// GROOVY-5232
void testSetterForProperty() {
void testSetterForProperty1() {
assertScript '''
class Person {
String name
Expand All @@ -493,6 +493,16 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
'''
}

// GROOVY-11372
void testSetterForProperty2() {
assertScript '''
def baos = new ByteArrayOutputStream()
assert baos.size() == 0
baos.bytes= new byte[1]
assert baos.size() == 1
'''
}

// GROOVY-5443
void testFieldInitShouldPass() {
assertScript '''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,9 +908,9 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT

assertScript '''
def map = [:]
map.metaClass = null // TODO: GROOVY-6549 made this "put" (SC only)!
map.metaClass = null
assert map.metaClass != null
assert map.containsKey('metaClass')
assert !map.containsKey('metaClass')
'''
}

Expand Down

0 comments on commit ea129d3

Please sign in to comment.