Differences From Artifact [15508d0871]:
- File
src/runtime/class.m
— part of check-in
[79fe29dbf4]
at
2019-06-17 00:38:44
on branch trunk
— Remove underscores from library & framework names
This is more compatible with the various conventions used on various
systems. (user: js, size: 21132) [annotate] [blame] [check-ins using]
To Artifact [132bb904b5]:
- File
src/runtime/class.m
— part of check-in
[3b3729a316]
at
2019-06-25 20:09:55
on branch trunk
— runtime: Don't use static selectors
Static selectors are initialized by a global constructor that might run
too late: If a class has a +[load] that triggers an +[initialize], this
could result in trying to call +[initialize] with the selector in
class.m still being uninitialized. When this happens, it results in a
weird looking crash in an entirely unrelated method. This is because
when uninitialized, the selector name has not been replaced with a
selector UID yet, meaning the pointer to the selector name is treated as
the selector UID. As the dispatch only considers the low 16 bits of the
selector UID for performance reasons, it will just take the low 16 bits
of the pointer to the selector name as the UID without any complaints.
This can then result in calling a random method on that class which then
crashes. (user: js, size: 21746) [annotate] [blame] [check-ins using]
︙ | ︙ | |||
130 131 132 133 134 135 136 | objc_global_mutex_unlock(); return class; } static void | | < < > > > | | > > > > > | | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | objc_global_mutex_unlock(); return class; } static void callSelector(Class class, SEL selector) { for (struct objc_method_list *methodList = class->isa->methodList; methodList != NULL; methodList = methodList->next) for (unsigned int i = 0; i < methodList->count; i++) if (sel_isEqual((SEL)&methodList->methods[i].selector, selector)) ((void (*)(id, SEL))methodList->methods[i] .implementation)(class, selector); } static bool hasLoad(Class class) { static SEL loadSel = NULL; if (loadSel == NULL) loadSel = sel_registerName("load"); for (struct objc_method_list *methodList = class->isa->methodList; methodList != NULL; methodList = methodList->next) for (size_t i = 0; i < methodList->count; i++) if (sel_isEqual((SEL)&methodList->methods[i].selector, loadSel)) return true; return false; } static void callLoad(Class class) { static SEL loadSel = NULL; if (loadSel == NULL) loadSel = sel_registerName("load"); if (class->info & OBJC_CLASS_INFO_LOADED) return; if (class->superclass != Nil) callLoad(class->superclass); callSelector(class, loadSel); class->info |= OBJC_CLASS_INFO_LOADED; } void objc_update_dtable(Class class) { |
︙ | ︙ | |||
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | class->info |= OBJC_CLASS_INFO_SETUP; class->isa->info |= OBJC_CLASS_INFO_SETUP; } static void initializeClass(Class class) { if (class->info & OBJC_CLASS_INFO_INITIALIZED) return; if (class->superclass) initializeClass(class->superclass); class->info |= OBJC_CLASS_INFO_DTABLE; class->isa->info |= OBJC_CLASS_INFO_DTABLE; objc_update_dtable(class); objc_update_dtable(class->isa); /* * Set it first to prevent calling it recursively due to message sends * in the initialize method */ class->info |= OBJC_CLASS_INFO_INITIALIZED; class->isa->info |= OBJC_CLASS_INFO_INITIALIZED; | > > > > > > > > > > | > | > | > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | class->info |= OBJC_CLASS_INFO_SETUP; class->isa->info |= OBJC_CLASS_INFO_SETUP; } static void initializeClass(Class class) { static SEL initializeSel = NULL; if (initializeSel == NULL) initializeSel = sel_registerName("initialize"); if (class->info & OBJC_CLASS_INFO_INITIALIZED) return; if (class->superclass) initializeClass(class->superclass); class->info |= OBJC_CLASS_INFO_DTABLE; class->isa->info |= OBJC_CLASS_INFO_DTABLE; objc_update_dtable(class); objc_update_dtable(class->isa); /* * Set it first to prevent calling it recursively due to message sends * in the initialize method */ class->info |= OBJC_CLASS_INFO_INITIALIZED; class->isa->info |= OBJC_CLASS_INFO_INITIALIZED; /* * +[initialize] might get called from some +[load], before the * constructors of this compilation module have been called, at which * point the selector would not be properly initialized. */ if (class_respondsToSelector(object_getClass(class), initializeSel)) { void (*initialize)(id, SEL) = (void (*)(id, SEL)) objc_msg_lookup(class, initializeSel); initialize(class, initializeSel); } } void objc_initialize_class(Class class) { if (class->info & OBJC_CLASS_INFO_INITIALIZED) return; |
︙ | ︙ | |||
888 889 890 891 892 893 894 895 896 897 898 | class->info &= ~OBJC_CLASS_INFO_SETUP; } void objc_unregister_class(Class class) { while (class->subclassList != NULL && class->subclassList[0] != Nil) objc_unregister_class(class->subclassList[0]); if (class->info & OBJC_CLASS_INFO_LOADED) | > > > > > | | 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 | class->info &= ~OBJC_CLASS_INFO_SETUP; } void objc_unregister_class(Class class) { static SEL unloadSel = NULL; if (unloadSel == NULL) unloadSel = sel_registerName("unload"); while (class->subclassList != NULL && class->subclassList[0] != Nil) objc_unregister_class(class->subclassList[0]); if (class->info & OBJC_CLASS_INFO_LOADED) callSelector(class, unloadSel); objc_hashtable_delete(classes, class->name); if (strcmp(class_getName(class), "Protocol") != 0) classesCount--; unregisterClass(class); |
︙ | ︙ |