123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- /*
- * 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>
- #include "DenseMapExtras.h"
- // 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)
- };
- spinlock_t AssociationsManagerLock;
- namespace objc {
- class ObjcAssociation {
- uintptr_t _policy;
- id _value;
- public:
- ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
- ObjcAssociation() : _policy(0), _value(nil) {}
- ObjcAssociation(const ObjcAssociation &other) = default;
- ObjcAssociation &operator=(const ObjcAssociation &other) = default;
- ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
- swap(other);
- }
- inline void swap(ObjcAssociation &other) {
- std::swap(_policy, other._policy);
- std::swap(_value, other._value);
- }
- inline uintptr_t policy() const { return _policy; }
- inline id value() const { return _value; }
- inline void acquireValue() {
- if (_value) {
- switch (_policy & 0xFF) {
- case OBJC_ASSOCIATION_SETTER_RETAIN:
- _value = objc_retain(_value);
- break;
- case OBJC_ASSOCIATION_SETTER_COPY:
- _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
- break;
- }
- }
- }
- inline void releaseHeldValue() {
- if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
- objc_release(_value);
- }
- }
- inline void retainReturnedValue() {
- if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
- objc_retain(_value);
- }
- }
- inline id autoreleaseReturnedValue() {
- if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
- return objc_autorelease(_value);
- }
- return _value;
- }
- };
- typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
- typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
- // class AssociationsManager manages a lock / hash table singleton pair.
- // Allocating an instance acquires the lock
- class AssociationsManager {
- using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
- static Storage _mapStorage;
- public:
- AssociationsManager() { AssociationsManagerLock.lock(); }
- ~AssociationsManager() { AssociationsManagerLock.unlock(); }
- AssociationsHashMap &get() {
- return _mapStorage.get();
- }
- static void init() {
- _mapStorage.init();
- }
- };
- AssociationsManager::Storage AssociationsManager::_mapStorage;
- } // namespace objc
- using namespace objc;
- void
- _objc_associations_init()
- {
- AssociationsManager::init();
- }
- id
- _object_get_associative_reference(id object, const void *key)
- {
- ObjcAssociation association{};
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.get());
- AssociationsHashMap::iterator i = associations.find((objc_object *)object);
- if (i != associations.end()) {
- ObjectAssociationMap &refs = i->second;
- ObjectAssociationMap::iterator j = refs.find(key);
- if (j != refs.end()) {
- association = j->second;
- association.retainReturnedValue();
- }
- }
- }
- return association.autoreleaseReturnedValue();
- }
- void
- _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
- {
- // This code used to work when nil was passed for object and key. Some code
- // probably relies on that to not crash. Check and handle it explicitly.
- // rdar://problem/44094390
- if (!object && !value) return;
- if (object->getIsa()->forbidsAssociatedObjects())
- _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
- DisguisedPtr<objc_object> disguised{(objc_object *)object};
- ObjcAssociation association{policy, value};
- // retain the new value (if any) outside the lock.
- association.acquireValue();
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.get());
- if (value) {
- auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
- if (refs_result.second) {
- /* it's the first association we make */
- object->setHasAssociatedObjects();
- }
- /* establish or replace the association */
- auto &refs = refs_result.first->second;
- auto result = refs.try_emplace(key, std::move(association));
- if (!result.second) {
- association.swap(result.first->second);
- }
- } else {
- auto refs_it = associations.find(disguised);
- if (refs_it != associations.end()) {
- auto &refs = refs_it->second;
- auto it = refs.find(key);
- if (it != refs.end()) {
- association.swap(it->second);
- refs.erase(it);
- if (refs.size() == 0) {
- associations.erase(refs_it);
- }
- }
- }
- }
- }
- // release the old value (outside of the lock).
- association.releaseHeldValue();
- }
- // Unlike setting/getting an associated reference,
- // this function is performance sensitive because of
- // raw isa objects (such as OS Objects) that can't track
- // whether they have associated objects.
- void
- _object_remove_assocations(id object)
- {
- ObjectAssociationMap refs{};
- {
- AssociationsManager manager;
- AssociationsHashMap &associations(manager.get());
- AssociationsHashMap::iterator i = associations.find((objc_object *)object);
- if (i != associations.end()) {
- refs.swap(i->second);
- associations.erase(i);
- }
- }
- // release everything (outside of the lock).
- for (auto &i: refs) {
- i.second.releaseHeldValue();
- }
- }
|