NSPredicate fails to match constant values in IN clause
The following snippet:
NSPredicate *p = [NSPredicate predicateWithFormat:@"'a' in {'a'}"];
BOOL b = [p evaluateWithObject:self];
NSLog(@"b = %d", b);
logs "b = 1" on Mac OS X but "b = 0" on gnustep.
The following patch fixes this bug (and doesn't cause any of the existing NSPredicate tests to fail), but I suspect it may just be a bandaid for a specific case of a more general problem.
--- NSPredicate.m.orig 2022-01-29 14:18:23.249998976 +0000
+++ NSPredicate.m 2022-01-29 15:34:39.745337585 +0000
@@ -1049,6 +1049,8 @@
e = [rightResult objectEnumerator];
while ((value = [e nextObject]))
{
+ if ([value isKindOfClass:[GSConstantValueExpression class]])
+ value = [value constantValue];
if ([value isEqual: leftResult])
return YES;
}
I've never used NSPredicate, but looking through the code it seems to me the issue might be with the GSConstantValueExpression class used to represent the array. I wonder if its -expressionValueWithObject:context: method should, in the case of an array, return a new array formed by calling that method on each of its elements. What do you think?
Yes, the following patch does seem more general, fixes my use case, and doesn't break the existing NSPredicate tests:
--- NSPredicate.m.orig 2022-01-30 10:27:07.177865334 -0500
+++ NSPredicate.m 2022-01-30 10:36:17.211753217 -0500
@@ -1337,7 +1337,15 @@
- (id) expressionValueWithObject: (id)object
context: (NSMutableDictionary *)context
{
+ if ([_obj isKindOfClass:[NSArray class]]) {
+ NSMutableArray *tmp = [NSMutableArray arrayWithCapacity:[_obj count]];
+ for (id o in _obj) {
+ [tmp addObject:[o expressionValueWithObject:o context:context]];
+ }
+ return tmp;
+ } else {
return _obj;
+ }
}
I wrote/added a portable (not dependent on objc-2) version of that solution.
This fix doesn’t work when the array is given as an argument instead of inside the format string like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self IN %@", @[@"yes"]];
NSArray *filteredElements = [@[@"yes", @"no"] filteredArrayUsingPredicate:predicate];
NSLog(@"Elements: %@", filteredElements);
Running the above crashes in line 1351 here, because e will be an NSString ("yes") instead of an NSExpression:
https://github.com/gnustep/libs-base/blob/f3344628e5735c2782da2acd3aa3f2fced670177/Source/NSPredicate.m#L1348-L1354
Does anyone know how to fix this? I took a brief look but having a little trouble following the NSPredicate implementation.
Does anyone know how to fix this? I took a brief look but having a little trouble following the NSPredicate implementation.
Fixed in https://github.com/gnustep/libs-base/pull/273