/* * 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 #import #import #import #import #import #import template inline std::unique_ptr> adoptSystem(U value) { return std::unique_ptr>(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 worklist; HashSet 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(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(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(protocol_copyPropertyList(protocol, &count)); for (unsigned i = 0; i < count; ++i) callback(properties[i]); } template 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::ResultType parseObjCType(const char*& position) { ASSERT(*position); switch (*position++) { case 'c': return DelegateType::template typeInteger(); case 'i': return DelegateType::template typeInteger(); case 's': return DelegateType::template typeInteger(); case 'l': return DelegateType::template typeInteger(); case 'q': return DelegateType::template typeDouble(); case 'C': return DelegateType::template typeInteger(); case 'I': return DelegateType::template typeInteger(); case 'S': return DelegateType::template typeInteger(); case 'L': return DelegateType::template typeInteger(); case 'Q': return DelegateType::template typeDouble(); case 'f': return DelegateType::template typeDouble(); case 'd': return DelegateType::template typeDouble(); 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 *); }