Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial port of WinObjC's KVO implementation to GNUstep #420

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
b6c52b9
GSAtomic: Add prefix to macro definitions
hmelder Jun 4, 2024
28c985a
NSKVOSupport: Import
hmelder Jun 4, 2024
36d0966
NSKVOSupport: Add test cases
hmelder Jun 4, 2024
d009453
NSKVOSwizzling: Ugly C Rewrite
hmelder Jun 4, 2024
43bc756
NSKeyValueObserving: Use old implementation as fallback
hmelder Jun 4, 2024
366e24b
NSKeyValueObserving: Rename TypeEncodingCases header
hmelder Jun 4, 2024
e24b308
NSKVOSupport: Fix new objects not being added to NSKeyValueChangeNew …
hmelder Jun 11, 2024
479fe58
NSKeyValueMutableSet: Fix will and didChange notifications for set op…
hmelder Jun 11, 2024
b06b5a0
NSKeyValueMutableSet: Document Accessor Search Patterns
hmelder Jun 11, 2024
b360183
NSKVOSupport: Add toMany test
hmelder Jun 11, 2024
a25ac65
NSKeyValueCoding: Change notifications when changing value via setVal…
hmelder Jun 11, 2024
d957cb8
NSKVOSupport: Add more tests
hmelder Jun 11, 2024
39caa6c
NSKVOSupport: Do not wrap block in try/finally to avoid crash in windows
hmelder Jun 11, 2024
1f22e2a
NSKVOSwizzling: use _alloca on Windows
hmelder Jun 11, 2024
6428ff8
NSKVOSupport: Do not autorelease newWithObservee:
hmelder Jun 11, 2024
f88eec6
NSKVOSupport: Do not leak Observee and TestFacade objects
hmelder Jun 11, 2024
4b2d288
Update license headers
hmelder Jun 11, 2024
06558d4
NSKeyValueMutableSet: Remove NSLog
hmelder Jun 11, 2024
81ead76
NSKVO: Update copyright headers
hmelder Jun 11, 2024
093659a
NSKVOSwizzling: Do not mix decl and code
hmelder Jun 11, 2024
d2baf7b
Improve runtime detection in makefile
hmelder Jun 11, 2024
48680c8
NSKVOSupport: Remove commented-out code
hmelder Jun 11, 2024
41deed9
Add file extension of source file in GNUMakefile
hmelder Jun 11, 2024
bf4989d
NSKVOSupport: Remove @status comments
hmelder Jun 18, 2024
47c40fd
NSKVOSupport: Implement private notify method
hmelder Jun 22, 2024
710159d
NSUserDefaults: KVO Support and fix macOS incompatibilities
hmelder Jun 22, 2024
d0c3175
NSKeyValueObserving: Set old to null if nil
hmelder Jun 23, 2024
94aca48
NSKeyValueObserving: Remove cached new value
hmelder Jun 23, 2024
b27baea
NSMethodSignature: Add signature cache
hmelder Jun 25, 2024
435c441
Fix build error with some compilers: initializer element is not a com…
triplef Jun 27, 2024
2020146
Fix some of the non-portable code
rfm Aug 5, 2024
7003909
tests not working for build with olf compiler yet.
rfm Aug 5, 2024
a37d9f9
NSKVOSupport: Remove ObjC2 features and mark tests failing on GCC as …
hmelder Sep 23, 2024
bf40916
Call class method instead of private _keyPathsForValuesAffectingValue…
hmelder Oct 14, 2024
14a3af1
Move _keyPathsForValuesAffectingValueForKey body into class method an…
hmelder Oct 14, 2024
e6c9108
Remove @synchronize
hmelder Oct 14, 2024
ef9781f
NSUserDefaults: Change notification should contain old value from oth…
hmelder Oct 15, 2024
4143b53
NSUserDefaults: Fetch new value from all domains
hmelder Oct 24, 2024
95f6740
NSKVOInternal: Fixup filename in header
hmelder Oct 24, 2024
38b3f2b
NSUserDefaults: Go through search list instead of only one domain in …
hmelder Oct 25, 2024
2c0d86b
Making indentation a bit less worse
hmelder Oct 25, 2024
ba0ec1d
Add NSUserDefaults KVO tests
hmelder Oct 25, 2024
41e9c98
NSKVOSupport: NSUserDefaults test small fixes
hmelder Oct 25, 2024
2223972
Add autoreleasepool
hmelder Oct 25, 2024
d3d1529
NSUserDefaults: Only emit change notifications if value changed
hmelder Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Source/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ NSJSONSerialization.m \
NSKeyedArchiver.m \
NSKeyedUnarchiver.m \
NSKeyValueCoding.m \
NSKeyValueObserving.m \
NSLengthFormatter.m \
NSLinguisticTagger.m \
NSLocale.m \
Expand Down Expand Up @@ -359,6 +358,19 @@ NSZone.m \
externs.m \
objc-load.m

# We have two implementations for Key Value Observing.
# One highly-optimised one that depends on libobjc2
# and the original implementation.
ifeq ($(OBJC_RUNTIME_LIB), ng)
BASE_MFILES += \
NSKVOSupport.m \
NSKVOSwizzling.m
else
BASE_MFILES += \
NSKeyValueObserving.m
endif


ifneq ($(GNUSTEP_TARGET_OS), mingw32)
ifneq ($(GNUSTEP_TARGET_OS), mingw64)
ifneq ($(GNUSTEP_TARGET_OS), windows)
Expand Down
22 changes: 11 additions & 11 deletions Source/GSAtomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
* Use native C11 atomic operations. _Atomic() should be defined by the
* compiler.
*/
#define atomic_load_explicit(object, order) \
#define gs_atomic_load_explicit(object, order) \
__c11_atomic_load(object, order)
#define atomic_store_explicit(object, desired, order) \
#define gs_atomic_store_explicit(object, desired, order) \
__c11_atomic_store(object, desired, order)

#else
Expand All @@ -33,15 +33,15 @@
#define _Atomic(T) struct { T volatile __val; }
#if __has_builtin(__sync_swap)
/* Clang provides a full-barrier atomic exchange - use it if available. */
#define atomic_exchange_explicit(object, desired, order) \
#define gs_atomic_exchange_explicit(object, desired, order) \
((void)(order), __sync_swap(&(object)->__val, desired))
#else
/*
* __sync_lock_test_and_set() is only an acquire barrier in theory (although in
* practice it is usually a full barrier) so we need an explicit barrier before
* it.
*/
#define atomic_exchange_explicit(object, desired, order) \
#define gs_atomic_exchange_explicit(object, desired, order) \
__extension__ ({ \
__typeof__(object) __o = (object); \
__typeof__(desired) __d = (desired); \
Expand All @@ -50,10 +50,10 @@ __extension__ ({ \
__sync_lock_test_and_set(&(__o)->__val, __d); \
})
#endif
#define atomic_load_explicit(object, order) \
#define gs_atomic_load_explicit(object, order) \
((void)(order), __sync_fetch_and_add(&(object)->__val, 0))
#define atomic_store_explicit(object, desired, order) \
((void)atomic_exchange_explicit(object, desired, order))
#define gs_atomic_store_explicit(object, desired, order) \
((void)gs_atomic_exchange_explicit(object, desired, order))

#endif

Expand All @@ -64,9 +64,9 @@ __extension__ ({ \
/*
* Convenience functions.
*/
#define atomic_load(object) \
atomic_load_explicit(object, __ATOMIC_SEQ_CST)
#define atomic_store(object, desired) \
atomic_store_explicit(object, desired, __ATOMIC_SEQ_CST)
#define gs_atomic_load(object) \
gs_atomic_load_explicit(object, __ATOMIC_SEQ_CST)
#define gs_atomic_store(object, desired) \
gs_atomic_store_explicit(object, desired, __ATOMIC_SEQ_CST)

#endif // _GSAtomic_h_
6 changes: 6 additions & 0 deletions Source/GSPThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,18 @@ typedef CONDITION_VARIABLE gs_cond_t;
#define GS_COND_BROADCAST(cond) WakeAllConditionVariable(&(cond))

/* Pthread-like locking primitives defined in NSLock.m */
#ifdef __cplusplus
extern "C" {
#endif
void gs_mutex_init(gs_mutex_t *l, gs_mutex_attr_t attr);
int gs_mutex_lock(gs_mutex_t *l);
int gs_mutex_trylock(gs_mutex_t *l);
int gs_mutex_unlock(gs_mutex_t *l);
int gs_cond_wait(gs_cond_t *cond, gs_mutex_t *mutex);
int gs_cond_timedwait(gs_cond_t *cond, gs_mutex_t *mutex, DWORD millisecs);
#ifdef __cplusplus
}
#endif

/*
* Threading primitives.
Expand Down
129 changes: 129 additions & 0 deletions Source/NSKVOInternal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
NSKVOInternal.h

Copyright (C) 2024 Free Software Foundation, Inc.

Written by: Hugo Melder <[email protected]>
Date: June 2024

Based on WinObjC KVO tests by Microsoft Corporation.

This file is part of GNUStep-base

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

If you are interested in a warranty or support for this source code,
contact Scott Christley <[email protected]> for more information.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
/**
Copyright (c) Microsoft. All rights reserved.

This code is licensed under the MIT License (MIT).

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/* This Key Value Observing Implementation is tied to libobjc2 */

#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSKeyValueObserving.h>
#import <Foundation/NSException.h>

#if defined(__OBJC2__)

#import "GSPThread.h"

#define NS_COLLECTION_THROW_ILLEGAL_KVO(keyPath) \
do \
{ \
[NSException \
raise:NSInvalidArgumentException \
format:@"-[%s %s] is not supported. Key path: %@", \
object_getClassName(self), sel_getName(_cmd), keyPath]; \
} while (false)

@class _NSKVOKeypathObserver;

@interface _NSKVOKeyObserver : NSObject
- (instancetype)initWithObject:(id)object
keypathObserver:(_NSKVOKeypathObserver *)keypathObserver
key:(NSString *)key
restOfKeypath:(NSString *)restOfKeypath
affectedObservers:(NSArray *)affectedObservers;
@property (nonatomic, retain) _NSKVOKeypathObserver *keypathObserver;
@property (nonatomic, retain) _NSKVOKeyObserver *restOfKeypathObserver;
@property (nonatomic, retain) NSArray *dependentObservers;
@property (nonatomic, assign) id object;
@property (nonatomic, copy) NSString *key;
@property (nonatomic, copy) NSString *restOfKeypath;
@property (nonatomic, retain) NSArray *affectedObservers;
@property (nonatomic, assign) BOOL root;
@property (nonatomic, readonly) BOOL isRemoved;
@end

@interface _NSKVOKeypathObserver : NSObject
- (instancetype)initWithObject:(id)object
observer:(id)observer
keyPath:(NSString *)keypath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
@property (nonatomic, assign) id object;
@property (nonatomic, assign) id observer;
@property (nonatomic, copy) NSString *keypath;
@property (nonatomic, assign) NSKeyValueObservingOptions options;
@property (nonatomic, assign) void *context;

@property (atomic, retain) NSMutableDictionary *pendingChange;
@end

@interface _NSKVOObservationInfo : NSObject
{
NSMutableDictionary<NSString *, NSMutableArray<_NSKVOKeyObserver *> *>
*_keyObserverMap;
NSInteger _dependencyDepth;
NSMutableSet<NSString *> *_existingDependentKeys;
gs_mutex_t _lock;
}
- (instancetype)init;
- (NSArray *)observersForKey:(NSString *)key;
- (void)addObserver:(_NSKVOKeyObserver *)observer;
@end

// From NSKVOSwizzling
void
_NSKVOEnsureKeyWillNotify(id object, NSString *key);

#endif

/* Implementation in NSKVOSupport.m for ObjC2 and NSKeyValueObserving
* respectively
*/
@interface
NSObject (NSKeyValueObservingPrivate)
- (void)_notifyObserversOfChangeForKey:(NSString *)key
oldValue:(id)oldValue
newValue:(id)newValue;
@end
Loading
Loading