123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- /*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- /*
- Implementation of the weak / associative references for non-GC mode.
- */
- #include "objc-private.h"
- #include <objc/message.h>
- #include <map>
- #if _LIBCPP_VERSION
- # include <unordered_map>
- #else
- # include <tr1/unordered_map>
- using namespace tr1;
- #endif
- // wrap all the murky C++ details in a namespace to get them out of the way.
- namespace objc_references_support {
- struct DisguisedPointerEqual {
- bool operator()(uintptr_t p1, uintptr_t p2) const {
- return p1 == p2;
- }
- };
-
- struct DisguisedPointerHash {
- uintptr_t operator()(uintptr_t k) const {
- // borrowed from CFSet.c
- #if __LP64__
- uintptr_t a = 0x4368726973746F70ULL;
- uintptr_t b = 0x686572204B616E65ULL;
- #else
- uintptr_t a = 0x4B616E65UL;
- uintptr_t b = 0x4B616E65UL;
- #endif
- uintptr_t c = 1;
- a += k;
- #if __LP64__
- a -= b; a -= c; a ^= (c >> 43);
- b -= c; b -= a; b ^= (a << 9);
- c -= a; c -= b; c ^= (b >> 8);
- a -= b; a -= c; a ^= (c >> 38);
- b -= c; b -= a; b ^= (a << 23);
- c -= a; c -= b; c ^= (b >> 5);
- a -= b; a -= c; a ^= (c >> 35);
- b -= c; b -= a; b ^= (a << 49);
- c -= a; c -= b; c ^= (b >> 11);
- a -= b; a -= c; a ^= (c >> 12);
- b -= c; b -= a; b ^= (a << 18);
- c -= a; c -= b; c ^= (b >> 22);
- #else
- a -= b; a -= c; a ^= (c >> 13);
- b -= c; b -= a; b ^= (a << 8);
- c -= a; c -= b; c ^= (b >> 13);
- a -= b; a -= c; a ^= (c >> 12);
- b -= c; b -= a; b ^= (a << 16);
- c -= a; c -= b; c ^= (b >> 5);
- a -= b; a -= c; a ^= (c >> 3);
- b -= c; b -= a; b ^= (a << 10);
- c -= a; c -= b; c ^= (b >> 15);
- #endif
- return c;
- }
- };
-
- struct ObjectPointerLess {
- bool operator()(const void *p1, const void *p2) const {
- return p1 < p2;
- }
- };
-
- struct ObjcPointerHash {
- uintptr_t operator()(void *p) const {
- return DisguisedPointerHash()(uintptr_t(p));
- }
- };
- // STL allocator that uses the runtime's internal allocator.
-
- template <typename T> struct ObjcAllocator {
- typedef T value_type;
- typedef value_type* pointer;
- typedef const value_type *const_pointer;
- typedef value_type& reference;
- typedef const value_type& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
- template <typename U> ObjcAllocator(const ObjcAllocator<U>&) {}
- ObjcAllocator() {}
- ObjcAllocator(const ObjcAllocator&) {}
- ~ObjcAllocator() {}
- pointer address(reference x) const { return &x; }
- const_pointer address(const_reference x) const {
- return x;
- }
- pointer allocate(size_type n, const_pointer = 0) {
- return static_cast<pointer>(::malloc(n * sizeof(T)));
- }
- void deallocate(pointer p, size_type) { ::free(p); }
- size_type max_size() const {
- return static_cast<size_type>(-1) / sizeof(T);
- }
- void construct(pointer p, const value_type& x) {
- new(p) value_type(x);
- }
- void destroy(pointer p) { p->~value_type(); }
- void operator=(const ObjcAllocator&);
- };
- template<> struct ObjcAllocator<void> {
- typedef void value_type;
- typedef void* pointer;
- typedef const void *const_pointer;
- template <typename U> struct rebind { typedef ObjcAllocator<U> other; };
- };
-
- typedef uintptr_t disguised_ptr_t;
- inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
- inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
-
- class ObjcAssociation {
- uintptr_t _policy;
- id _value;
- public:
- ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
- ObjcAssociation() : _policy(0), _value(nil) {}
- uintptr_t policy() const { return _policy; }
- id value() const { return _value; }
-
- bool hasValue() { return _value != nil; }
- };
- #if TARGET_OS_WIN32
- typedef hash_map<void *, ObjcAssociation> ObjectAssociationMap;
- typedef hash_map<disguised_ptr_t, ObjectAssociationMap *> AssociationsHashMap;
- #else
- typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
- class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
- public:
- void *operator new(size_t n) { return ::malloc(n); }
- void operator delete(void *ptr) { ::free(ptr); }
- };
- typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
- class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
- public:
- void *operator new(size_t n) { return ::malloc(n); }
- void operator delete(void *ptr) { ::free(ptr); }
- };
- #endif
- }
- using namespace objc_references_support;
- // class AssociationsManager manages a lock / hash table singleton pair.
- // Allocating an instance acquires the lock, and calling its assocations()
- // method lazily allocates the hash table.
- spinlock_t AssociationsManagerLock;
- class AssociationsManager {
- // associative references: object pointer -> PtrPtrHashMap.
- static AssociationsHashMap *_map;
- public:
- AssociationsManager() { AssociationsManagerLock.lock(); }
- ~AssociationsManager() { AssociationsManagerLock.unlock(); }
-
- AssociationsHashMap &associations() {
- if (_map == NULL)
- _map = new AssociationsHashMap();
- return *_map;
- }
- };
- AssociationsHashMap *AssociationsManager::_map = NULL;
- // expanded policy bits.
- enum {
- OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
- OBJC_ASSOCIATION_SETTER_RETAIN = 1,
- OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
- OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
- OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
- OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
- };
- id _object_get_associative_reference(id object, void *key) {
- id value = nil;
- uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.associations());
- disguised_ptr_t disguised_object = DISGUISE(object);
- AssociationsHashMap::iterator i = associations.find(disguised_object);
- if (i != associations.end()) {
- ObjectAssociationMap *refs = i->second;
- ObjectAssociationMap::iterator j = refs->find(key);
- if (j != refs->end()) {
- ObjcAssociation &entry = j->second;
- value = entry.value();
- policy = entry.policy();
- if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
- objc_retain(value);
- }
- }
- }
- }
- if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
- objc_autorelease(value);
- }
- return value;
- }
- static id acquireValue(id value, uintptr_t policy) {
- switch (policy & 0xFF) {
- case OBJC_ASSOCIATION_SETTER_RETAIN:
- return objc_retain(value);
- case OBJC_ASSOCIATION_SETTER_COPY:
- return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
- }
- return value;
- }
- static void releaseValue(id value, uintptr_t policy) {
- if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
- return objc_release(value);
- }
- }
- struct ReleaseValue {
- void operator() (ObjcAssociation &association) {
- releaseValue(association.value(), association.policy());
- }
- };
- void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
- // retain the new value (if any) outside the lock.
- ObjcAssociation old_association(0, nil);
- id new_value = value ? acquireValue(value, policy) : nil;
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.associations());
- disguised_ptr_t disguised_object = DISGUISE(object);
- if (new_value) {
- // break any existing association.
- AssociationsHashMap::iterator i = associations.find(disguised_object);
- if (i != associations.end()) {
- // secondary table exists
- ObjectAssociationMap *refs = i->second;
- ObjectAssociationMap::iterator j = refs->find(key);
- if (j != refs->end()) {
- old_association = j->second;
- j->second = ObjcAssociation(policy, new_value);
- } else {
- (*refs)[key] = ObjcAssociation(policy, new_value);
- }
- } else {
- // create the new association (first time).
- ObjectAssociationMap *refs = new ObjectAssociationMap;
- associations[disguised_object] = refs;
- (*refs)[key] = ObjcAssociation(policy, new_value);
- object->setHasAssociatedObjects();
- }
- } else {
- // setting the association to nil breaks the association.
- AssociationsHashMap::iterator i = associations.find(disguised_object);
- if (i != associations.end()) {
- ObjectAssociationMap *refs = i->second;
- ObjectAssociationMap::iterator j = refs->find(key);
- if (j != refs->end()) {
- old_association = j->second;
- refs->erase(j);
- }
- }
- }
- }
- // release the old value (outside of the lock).
- if (old_association.hasValue()) ReleaseValue()(old_association);
- }
- void _object_remove_assocations(id object) {
- vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.associations());
- if (associations.size() == 0) return;
- disguised_ptr_t disguised_object = DISGUISE(object);
- AssociationsHashMap::iterator i = associations.find(disguised_object);
- if (i != associations.end()) {
- // copy all of the associations that need to be removed.
- ObjectAssociationMap *refs = i->second;
- for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
- elements.push_back(j->second);
- }
- // remove the secondary table.
- delete refs;
- associations.erase(i);
- }
- }
- // the calls to releaseValue() happen outside of the lock.
- for_each(elements.begin(), elements.end(), ReleaseValue());
- }
|