Artifact 15533381aeeef40972c6f4065a01b2f5cb750290b7759aa8fc906711c0aae498:
- File
src/runtime/lookup.m
— part of check-in
[ede088a30d]
at
2019-04-14 13:35:38
on branch trunk
— runtime: Match Apple's +[initialize] behavior
This now calls +[initialize] several times on the same class if it is
unimplemented in one or more of the classes in the chain, to match the
behavior of the Apple runtime. While the behavior before was safer, it
can lead to testing against the ObjFW runtime and everything working,
but then failing when trying to use it with the Apple runtime. (user: js, size: 4838) [annotate] [blame] [check-ins using]
/* * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, * 2018, 2019 * Jonathan Schleifer <js@heap.zone> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #include <stdio.h> #include <stdlib.h> #import "ObjFW_RT.h" #import "private.h" #import "macros.h" static IMP forwardHandler = (IMP)0; static IMP stretForwardHandler = (IMP)0; static IMP commonMethodNotFound(id object, SEL selector, IMP (*lookup)(id, SEL), IMP forward) { /* * object might be a dummy object (see class_getMethodImplementation), * so don't access object directly unless it's a class! */ bool isClass = object_getClass(object)->info & OBJC_CLASS_INFO_METACLASS; if (!(object_getClass(object)->info & OBJC_CLASS_INFO_INITIALIZED)) { Class class = (isClass ? (Class)object : object_getClass(object)); objc_initialize_class(class); if (!(class->info & OBJC_CLASS_INFO_SETUP)) OBJC_ERROR("Could not dispatch message for incomplete " "class %s!", class_getName(class)); /* * We don't need to handle the case that super was called. * The reason for this is that a call to super is not possible * before a message to the class has been sent and it thus has * been initialized together with its superclasses. */ return lookup(object, selector); } /* Try resolveClassMethod: / resolveInstanceMethod: */ if (class_isMetaClass(object_getClass(object))) { Class class = object_getClass(object); if (class_respondsToSelector(class, @selector(resolveClassMethod:)) && [object resolveClassMethod: selector]) { if (!class_respondsToSelector(class, selector)) OBJC_ERROR("+[%s resolveClassMethod: %s] " "returned true without adding the method!", class_getName(object), sel_getName(selector)); return lookup(object, selector); } } else { Class class = object_getClass(object); Class metaclass = object_getClass(class); if (class_respondsToSelector(metaclass, @selector(resolveInstanceMethod:)) && [class resolveInstanceMethod: selector]) { if (!class_respondsToSelector(class, selector)) OBJC_ERROR("+[%s resolveInstanceMethod: %s] " "returned true without adding the method!", class_getName(object_getClass(object)), sel_getName(selector)); return lookup(object, selector); } } if (forward != (IMP)0) return forward; OBJC_ERROR("Selector %c[%s] is not implemented for class %s!", (isClass ? '+' : '-'), sel_getName(selector), object_getClassName(object)); } IMP objc_method_not_found(id object, SEL selector) { return commonMethodNotFound(object, selector, objc_msg_lookup, forwardHandler); } IMP objc_method_not_found_stret(id object, SEL selector) { return commonMethodNotFound(object, selector, objc_msg_lookup_stret, stretForwardHandler); } void objc_setForwardHandler(IMP forward, IMP stretForward) { forwardHandler = forward; stretForwardHandler = stretForward; } bool class_respondsToSelector(Class class, SEL selector) { if (class == Nil) return false; return (objc_dtable_get(class->DTable, (uint32_t)selector->UID) != (IMP)0); } #ifndef OF_ASM_LOOKUP static id nilMethod(id self, SEL _cmd) { return nil; } static OF_INLINE IMP commonLookup(id object, SEL selector, IMP (*notFound)(id, SEL)) { IMP imp; if (object == nil) return (IMP)nilMethod; imp = objc_dtable_get(object_getClass(object)->DTable, (uint32_t)selector->UID); if (imp == (IMP)0) return notFound(object, selector); return imp; } IMP objc_msg_lookup(id object, SEL selector) { return commonLookup(object, selector, objc_method_not_found); } IMP objc_msg_lookup_stret(id object, SEL selector) { return commonLookup(object, selector, objc_method_not_found_stret); } static OF_INLINE IMP commonSuperLookup(struct objc_super *super, SEL selector, IMP (*notFound)(id, SEL)) { IMP imp; if (super->self == nil) return (IMP)nilMethod; imp = objc_dtable_get(super->class->DTable, (uint32_t)selector->UID); if (imp == (IMP)0) return notFound(super->self, selector); return imp; } IMP objc_msg_lookup_super(struct objc_super *super, SEL selector) { return commonSuperLookup(super, selector, objc_method_not_found); } IMP objc_msg_lookup_super_stret(struct objc_super *super, SEL selector) { return commonSuperLookup(super, selector, objc_method_not_found_stret); } #endif