| /* |
| * Copyright (C) 2013, 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import <memory> |
| #import <objc/Protocol.h> |
| #import <objc/runtime.h> |
| #import <wtf/HashSet.h> |
| #import <wtf/SystemFree.h> |
| #import <wtf/Vector.h> |
| #import <wtf/text/CString.h> |
| |
| template<typename T, typename U> |
| inline std::unique_ptr<T, WTF::SystemFree<T>> adoptSystem(U value) |
| { |
| return std::unique_ptr<T, WTF::SystemFree<T>>(value); |
| } |
| |
| inline bool protocolImplementsProtocol(Protocol *candidate, Protocol *target) |
| { |
| unsigned protocolProtocolsCount; |
| auto protocolProtocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(candidate, &protocolProtocolsCount)); |
| for (unsigned i = 0; i < protocolProtocolsCount; ++i) { |
| if (protocol_isEqual(protocolProtocols[i], target)) |
| return true; |
| } |
| return false; |
| } |
| |
| inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, void (^callback)(Protocol *, bool& stop)) |
| { |
| ASSERT(cls); |
| ASSERT(target); |
| |
| Vector<Protocol*> worklist; |
| HashSet<void*> visited; |
| |
| // Initially fill the worklist with the Class's protocols. |
| { |
| unsigned protocolsCount; |
| auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(class_copyProtocolList(cls, &protocolsCount)); |
| worklist.append(protocols.get(), protocolsCount); |
| } |
| |
| bool stop = false; |
| while (!worklist.isEmpty()) { |
| Protocol *protocol = worklist.last(); |
| worklist.removeLast(); |
| |
| // Are we encountering this Protocol for the first time? |
| if (!visited.add((__bridge void*)protocol).isNewEntry) |
| continue; |
| |
| // If it implements the protocol, make the callback. |
| if (protocolImplementsProtocol(protocol, target)) { |
| callback(protocol, stop); |
| if (stop) |
| break; |
| } |
| |
| // Add incorporated protocols to the worklist. |
| { |
| unsigned protocolsCount; |
| auto protocols = adoptSystem<__unsafe_unretained Protocol*[]>(protocol_copyProtocolList(protocol, &protocolsCount)); |
| worklist.append(protocols.get(), protocolsCount); |
| } |
| } |
| } |
| |
| inline void forEachMethodInClass(Class cls, void (^callback)(Method)) |
| { |
| unsigned count; |
| auto methods = adoptSystem<Method[]>(class_copyMethodList(cls, &count)); |
| for (unsigned i = 0; i < count; ++i) |
| callback(methods[i]); |
| } |
| |
| inline void forEachMethodInProtocol(Protocol *protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*)) |
| { |
| unsigned count; |
| auto methods = adoptSystem<objc_method_description[]>(protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count)); |
| for (unsigned i = 0; i < count; ++i) |
| callback(methods[i].name, methods[i].types); |
| } |
| |
| inline void forEachPropertyInProtocol(Protocol *protocol, void (^callback)(objc_property_t)) |
| { |
| unsigned count; |
| auto properties = adoptSystem<objc_property_t[]>(protocol_copyPropertyList(protocol, &count)); |
| for (unsigned i = 0; i < count; ++i) |
| callback(properties[i]); |
| } |
| |
| template<char open, char close> |
| void skipPair(const char*& position) |
| { |
| size_t count = 1; |
| do { |
| char c = *position++; |
| if (!c) |
| @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Malformed type encoding" userInfo:nil]; |
| if (c == open) |
| ++count; |
| else if (c == close) |
| --count; |
| } while (count); |
| } |
| |
| class StringRange { |
| WTF_MAKE_NONCOPYABLE(StringRange); |
| public: |
| StringRange(const char* begin, const char* end) |
| : m_string(begin, end - begin) |
| { } |
| operator const char*() const { return m_string.data(); } |
| const char* get() const { return m_string.data(); } |
| |
| private: |
| CString m_string; |
| }; |
| |
| class StructBuffer { |
| WTF_MAKE_NONCOPYABLE(StructBuffer); |
| public: |
| StructBuffer(const char* encodedType) |
| { |
| NSUInteger size, alignment; |
| NSGetSizeAndAlignment(encodedType, &size, &alignment); |
| m_buffer = fastAlignedMalloc(alignment, size); |
| } |
| |
| ~StructBuffer() { fastAlignedFree(m_buffer); } |
| operator void*() const { return m_buffer; } |
| |
| private: |
| void* m_buffer; |
| }; |
| |
| template<typename DelegateType> |
| typename DelegateType::ResultType parseObjCType(const char*& position) |
| { |
| ASSERT(*position); |
| |
| switch (*position++) { |
| case 'c': |
| return DelegateType::template typeInteger<char>(); |
| case 'i': |
| return DelegateType::template typeInteger<int>(); |
| case 's': |
| return DelegateType::template typeInteger<short>(); |
| case 'l': |
| return DelegateType::template typeInteger<long>(); |
| case 'q': |
| return DelegateType::template typeDouble<long long>(); |
| case 'C': |
| return DelegateType::template typeInteger<unsigned char>(); |
| case 'I': |
| return DelegateType::template typeInteger<unsigned>(); |
| case 'S': |
| return DelegateType::template typeInteger<unsigned short>(); |
| case 'L': |
| return DelegateType::template typeInteger<unsigned long>(); |
| case 'Q': |
| return DelegateType::template typeDouble<unsigned long long>(); |
| case 'f': |
| return DelegateType::template typeDouble<float>(); |
| case 'd': |
| return DelegateType::template typeDouble<double>(); |
| case 'B': |
| return DelegateType::typeBool(); |
| case 'v': |
| return DelegateType::typeVoid(); |
| |
| case '@': { // An object (whether statically typed or typed id) |
| if (position[0] == '?' && position[1] == '<') { |
| position += 2; |
| const char* begin = position; |
| skipPair<'<','>'>(position); |
| return DelegateType::typeBlock(begin, position - 1); |
| } |
| |
| if (*position == '"') { |
| const char* begin = position + 1; |
| const char* protocolPosition = strchr(begin, '<'); |
| const char* endOfType = strchr(begin, '"'); |
| position = endOfType + 1; |
| |
| // There's no protocol involved in this type, so just handle the class name. |
| if (!protocolPosition || protocolPosition > endOfType) |
| return DelegateType::typeOfClass(begin, endOfType); |
| // We skipped the class name and went straight to the protocol, so this is an id type. |
| if (begin == protocolPosition) |
| return DelegateType::typeId(); |
| // We have a class name with a protocol. For now, ignore the protocol. |
| return DelegateType::typeOfClass(begin, protocolPosition); |
| } |
| |
| return DelegateType::typeId(); |
| } |
| |
| case '{': { // {name=type...} A structure |
| const char* begin = position - 1; |
| skipPair<'{','}'>(position); |
| return DelegateType::typeStruct(begin, position); |
| } |
| |
| // NOT supporting C strings, arrays, pointers, unions, bitfields, function pointers. |
| case '*': // A character string (char *) |
| case '[': // [array type] An array |
| case '(': // (name=type...) A union |
| case 'b': // bnum A bit field of num bits |
| case '^': // ^type A pointer to type |
| case '?': // An unknown type (among other things, this code is used for function pointers) |
| // NOT supporting Objective-C Class, SEL |
| case '#': // A class object (Class) |
| case ':': // A method selector (SEL) |
| default: |
| return nil; |
| } |
| } |
| |
| extern "C" { |
| // Forward declare some Objective-C runtime internal methods that are not API. |
| const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod); |
| id objc_initWeak(id *, id); |
| void objc_destroyWeak(id *); |
| bool _Block_has_signature(void *); |
| const char * _Block_signature(void *); |
| } |