extern "C" __declspec(dllexport) void objc_exception_throw(void *exception) { int typeCount = 0; // Get count of all types in exception Class curType = object_getClass((id) exception); while (curType != nil) { typeCount++; curType = class_getSuperclass(curType); } typeCount++; // For id __ObjC_CatchableTypeArray *exceptTypes = (__ObjC_CatchableTypeArray *)_alloca(sizeof(__ObjC_CatchableTypeArray) + sizeof(__ObjC_CatchableType *) * typeCount); // Add exception type and all base types to throw information typeCount = 0; curType = object_getClass((id)exception); while (curType != nil) { exceptTypes->types[typeCount] = (__ObjC_CatchableType *)_alloca(sizeof(__ObjC_CatchableType)); memset(exceptTypes->types[typeCount], 0, sizeof(__ObjC_CatchableType)); exceptTypes->types[typeCount]->flags = 1; exceptTypes->types[typeCount]->mdisp = 0; exceptTypes->types[typeCount]->pdisp = -1; exceptTypes->types[typeCount]->vdisp = 0; exceptTypes->types[typeCount]->type = (const char *) alloca(32); memset((void *) exceptTypes->types[typeCount]->type, 0, 32); memcpy((char *)exceptTypes->types[typeCount]->type, class_getName(curType), strlen(class_getName(curType))); exceptTypes->types[typeCount]->size = 4; typeCount++; curType = class_getSuperclass(curType); } // Add id exceptTypes->types[typeCount] = (__ObjC_CatchableType *)_alloca(sizeof(__ObjC_CatchableType)); memset(exceptTypes->types[typeCount], 0, sizeof(__ObjC_CatchableType)); exceptTypes->types[typeCount]->flags = 1; exceptTypes->types[typeCount]->mdisp = 0; exceptTypes->types[typeCount]->pdisp = -1; exceptTypes->types[typeCount]->vdisp = 0; exceptTypes->types[typeCount]->type = (const char *)alloca(32); memset((void *)exceptTypes->types[typeCount]->type, 0, 32); exceptTypes->types[typeCount]->size = 4; typeCount++; exceptTypes->count = typeCount; _ThrowInfo ti = { 0, NULL, NULL, (_CatchableTypeArray *) exceptTypes }; _CxxThrowException(&exception, &ti); }
void objc_register_all_classes(struct objc_abi_symtab* symtab) { uint_fast32_t i; for (i = 0; i < symtab->cls_def_cnt; i++) { struct objc_abi_class* cls = (struct objc_abi_class*)symtab->defs[i]; register_class(cls); register_selectors(cls); register_selectors((struct objc_abi_class*)object_getClass((id)cls)); } for (i = 0; i < symtab->cls_def_cnt; i++) { Class cls = (Class)symtab->defs[i]; if (has_load(cls)) { setup_class(cls); if (cls->info & OBJC_CLASS_INFO_SETUP) call_load(cls); else { if (load_queue == NULL) load_queue = malloc(sizeof(Class)); else load_queue = realloc(load_queue, sizeof(Class) * (load_queue_cnt + 1)); if (load_queue == NULL) OBJC_ERROR( "Not enough memory for load " "queue!"); load_queue[load_queue_cnt++] = cls; } } else cls->info |= OBJC_CLASS_INFO_LOADED; } /* Process load queue */ for (i = 0; i < load_queue_cnt; i++) { setup_class(load_queue[i]); if (load_queue[i]->info & OBJC_CLASS_INFO_SETUP) { call_load(load_queue[i]); load_queue_cnt--; if (load_queue_cnt == 0) { free(load_queue); load_queue = NULL; continue; } load_queue[i] = load_queue[load_queue_cnt]; load_queue = realloc(load_queue, sizeof(Class) * load_queue_cnt); if (load_queue == NULL) OBJC_ERROR("Not enough memory for load queue!"); } } }
OBJCRT_EXPORT BOOL object_isMethodFromClass(id dwObj, SEL pSel, const char* fromClass) { Class pClass = object_getClass(dwObj); char* clsname = NULL; while (pClass != NULL) { clsname = (char*)pClass->name; struct objc_method_list* methodList = pClass->methodlist; while (methodList != NULL) { for (unsigned i = 0; i < methodList->count; i++) { if (methodList->methods[i].sel.uid == pSel->uid) { if (strcmp(clsname, fromClass) == 0) { return TRUE; } else { return FALSE; } } } methodList = methodList->next; } pClass = pClass->superclass; } return FALSE; }
void __darwin_objc_exception_throw(objc_object* object) { TRACE1(object); if (!object) { std::cerr << "NULL Objective-C exception thrown!\n"; abort(); } if (!m_lastBlock) { std::cerr << "Unhandled Objective-C exception: " << class_getName(object_getClass(object)) << '(' << object << ")\n"; abort(); } else { TryBlock* currentBlock = m_lastBlock; currentBlock->exceptionObject = object; m_lastBlock = m_lastBlock->previousBlock; longjmp(currentBlock->buffer, true); } }
id object_copy(id obj, size_t size) { Class cls = object_getClass(obj); id cpy = class_createInstance(cls, size - class_getInstanceSize(cls)); memcpy(((char*)cpy + sizeof(id)), ((char*)obj + sizeof(id)), size - sizeof(id)); return cpy; }
Ivar object_getInstanceVariable(id obj, const char *name, void **outValue) { Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); if (NULL != outValue) { *outValue = object_getIvar(obj, ivar); } return ivar; }
Class RegisterClass(const class_t* cls, intptr_t slide) { LOG << "Processing ObjC class " << cls->data()->className << std::endl; const class_t* meta = cls->isa; Class conv, super; auto itSuper = g_classPointers.find(cls->superclass); if (itSuper != g_classPointers.end()) super = itSuper->second; else super = reinterpret_cast<Class>(cls->superclass); LOG << "...superclass is @" << super << std::endl; assert(objc_getClass(cls->data()->className) == nullptr); conv = objc_allocateClassPair(super, cls->data()->className, 0); const class_ro_t* ro = cls->data(); const class_ro_t* roMeta = meta->data(); if (ro->baseMethods) ConvertMethodListGen(conv, ro->baseMethods); if (roMeta->baseMethods) ConvertMethodListGen(object_getClass(id(conv)), roMeta->baseMethods); if (ro->ivars) ConvertIvarList(conv, ro->ivars); if (ro->baseProtocols) AddClassProtocols(conv, ro->baseProtocols, slide); if (ro->baseProperties) { ConvertProperties(ro->baseProperties, [conv](const char* name, const objc_property_attribute_t* attr, unsigned int count) { class_addProperty(conv, name, attr, count); bug_gnustepFixPropertyCount(conv); }); } // conv->instance_size = ro->instSize; // conv->isa->instance_size = roMeta->instSize; objc_registerClassPair(conv); LOG << "ObjC class " << cls->data()->className << " now @" << conv << std::endl; g_classPointers[cls] = conv; g_classPointers[cls->isa] = object_getClass(id(conv)); return conv; }
Class RegisterClass(old_class* cls, bool hasExt) { LOG << "Processing old ObjC class " << cls->name << std::endl; const old_class* meta = cls->isa.cls; Class conv, super; auto & g_classPointers = getClassPointers(); /* old_class* psuper = cls->super_class.cls; auto itSuper = g_classPointers.find(psuper); // TODO: may not be needed, should always be a string if (itSuper != g_classPointers.end()) super = itSuper->second; else super = reinterpret_cast<Class>(psuper); */ super = (Class) objc_getClass(cls->super_class.name); LOG << "...with superclass @" << super << std::endl; conv = objc_allocateClassPair(super, cls->name, 0); if (cls->methodList) ConvertMethodListGen(conv, cls->methodList); if (meta->methodList) ConvertMethodListGen(object_getClass(id(conv)), meta->methodList); if (cls->ivars) ConvertIvarList(conv, cls->ivars); if (cls->protocols) AddClassProtocols(conv, cls->protocols); if (hasExt && cls->ext && cls->ext->propertyLists) { LOG << "Class has EXT and a property list/lists\n"; //if (cls->info & CLS_NO_PROPERTY_ARRAY) if (true) { ConvertProperties(cls->ext->propertyList, [conv](const char* name, const objc_property_attribute_t* attr, unsigned int count) { class_addProperty(conv, name, attr, count); bug_gnustepFixPropertyCount(conv); }); } else { for (size_t i = 0; cls->ext->propertyLists[i] != nullptr; i++) { const old_property_list* l = cls->ext->propertyLists[i]; ConvertProperties(l, [conv](const char* name, const objc_property_attribute_t* attr, unsigned int count) { class_addProperty(conv, name, attr, count); bug_gnustepFixPropertyCount(conv); }); } } } objc_registerClassPair(conv); g_classPointers[cls] = conv; g_classPointers[cls->name] = conv; LOG << "ObjC class " << cls->name << " @" << conv << std::endl; return conv; }
static void call_method(Class cls, const char* method) { struct objc_method_list* ml; SEL selector; unsigned int i; selector = sel_registerName(method); for (ml = object_getClass((id)cls)->methodlist; ml != NULL; ml = ml->next) for (i = 0; i < ml->count; i++) if (sel_isEqual((SEL)&ml->methods[i].sel, selector)) ((void (*)(id, SEL))ml->methods[i].imp)((id)cls, selector); }
void SwizzClassMethod(Class origClass, Class replaceClass, SEL origSel, SEL replaceSel) { Method origMethod = class_getClassMethod(origClass, origSel); Method newMethod = class_getClassMethod(replaceClass, replaceSel); origClass = object_getClass((id)origClass); if(class_addMethod(origClass, origSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) class_replaceMethod(origClass, replaceSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); else method_exchangeImplementations(origMethod, newMethod); }
// BAF: This seems to be some garbage collection magic. Not for us mortals to look at! __private_extern__ Boolean __CFRuntimeIsFreedObject(id anObject) { if (!anObject) return false; static Class freedClass = Nil; if (!freedClass) freedClass = _objc_getFreedObjectClass(); Class cls = object_getClass(anObject); if (cls == freedClass) return true; // in 64-bit, a future class has nil isa, and calling class_getName() on // such will crash so we do this test; zombie classes are not future classes if (objc_getClass((id)cls) == nil) return false; const char *cname = class_getName(cls); if (cname && 0 == strncmp(cname, "_NSZombie_", 10)) return true; return false; }
static void initialize_class(Class cls) { if (cls->info & OBJC_CLASS_INFO_INITIALIZED) return; if (cls->superclass) initialize_class(cls->superclass); cls->info |= OBJC_CLASS_INFO_DTABLE; object_getClass((id)cls)->info |= OBJC_CLASS_INFO_DTABLE; objc_update_dtable(cls); objc_update_dtable(object_getClass((id)cls)); /* * Set it first to prevent calling it recursively due to message sends * in the initialize method */ cls->info |= OBJC_CLASS_INFO_INITIALIZED; object_getClass((id)cls)->info |= OBJC_CLASS_INFO_INITIALIZED; call_method(cls, "initialize"); }
static BOOL has_load(Class cls) { struct objc_method_list* ml; SEL selector; unsigned int i; selector = sel_registerName("load"); for (ml = object_getClass((id)cls)->methodlist; ml != NULL; ml = ml->next) for (i = 0; i < ml->count; i++) if (sel_isEqual((SEL)&ml->methods[i].sel, selector)) return YES; return NO; }
int objc_exception_match(objc_class* cls, objc_object* object) { TRACE2(cls, object); objc_class* objClass = object_getClass(object); while (objClass) { if (objClass == cls) return true; else objClass = class_getSuperclass(objClass); } return false; }
void objc_free_all_classes(void) { uint32_t i; if (classes == NULL) return; for (i = 0; i <= classes->last_idx; i++) { if (classes->data[i] != NULL) { free_class((Class)classes->data[i]->obj); free_class(object_getClass((id)classes->data[i]->obj)); } } objc_hashtable_free(classes); classes = NULL; }
static void print_memory_object(task_t task, void *context, unsigned type_mask, vm_range_t *ranges, unsigned range_count) { const size_t min_size = *(size_t *)context; for (vm_range_t *r = ranges, *end = ranges + range_count; r < end; r++) { const size_t size = auto_zone_size(__auto_zone, (void *)r->address); if (size >= min_size) { printf("address %p size %ld rc %d layout type ", (void *)r->address, size, auto_zone_retain_count(__auto_zone, (void *)r->address)); switch (auto_zone_get_layout_type(__auto_zone, (void *)r->address)) { case AUTO_OBJECT: printf("object (class %s)\n", class_getName(object_getClass((void *)r->address))); break; default: printf("memory\n"); break; } } } }
Ivar object_setInstanceVariable(id obj, const char *name, void *value) { Ivar ivar = class_getInstanceVariable(object_getClass(obj), name); object_setIvar(obj, ivar, value); return ivar; }
VALUE rb_vm_dispatch(void *_vm, struct mcache *cache, VALUE top, VALUE self, Class klass, SEL sel, rb_vm_block_t *block, unsigned char opt, int argc, const VALUE *argv) { RoxorVM *vm = (RoxorVM *)_vm; #if ROXOR_VM_DEBUG bool cached = true; #endif bool cache_method = true; Class current_super_class = vm->get_current_super_class(); SEL current_super_sel = vm->get_current_super_sel(); if (opt & DISPATCH_SUPER) { // TODO goto recache; } if (cache->sel != sel || cache->klass != klass || cache->flag == 0) { recache: #if ROXOR_VM_DEBUG cached = false; #endif Method method; if (opt & DISPATCH_SUPER) { if (!sel_equal(klass, current_super_sel, sel)) { current_super_sel = sel; current_super_class = klass; } else { // Let's make sure the current_super_class is valid before // using it; we check this by verifying that it's a real // super class of the current class, as we may be calling // a super method of the same name but on a totally different // class hierarchy. Class k = klass; bool current_super_class_ok = false; while (k != NULL) { if (k == current_super_class) { current_super_class_ok = true; break; } k = class_getSuperclass(k); } if (!current_super_class_ok) { current_super_class = klass; } } method = rb_vm_super_lookup(current_super_class, sel, ¤t_super_class); } else { current_super_sel = 0; method = class_getInstanceMethod(klass, sel); } if (method != NULL) { recache2: IMP imp = method_getImplementation(method); if (UNAVAILABLE_IMP(imp)) { // Method was undefined. goto call_method_missing; } rb_vm_method_node_t *node = GET_CORE()->method_node_get(method); if (node != NULL) { // ruby call fill_rcache(cache, klass, sel, node); } else { // objc call fill_ocache(cache, self, klass, imp, sel, method, argc); } if (opt & DISPATCH_SUPER) { cache->flag |= MCACHE_SUPER; } } else { // Method is not found... #if !defined(MACRUBY_STATIC) // Force a method resolving, because the objc cache might be // wrong. if (rb_vm_resolve_method(klass, sel)) { goto recache; } #endif // Does the receiver implements -forwardInvocation:? if ((opt & DISPATCH_SUPER) == 0 && rb_objc_supports_forwarding(self, sel)) { //#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 // In earlier versions of the Objective-C runtime, there seems // to be a bug where class_getInstanceMethod isn't atomic, // and might return NULL while at the exact same time another // thread registers the related method. // As a work-around, we double-check if the method still does // not exist here. If he does, we can dispatch it properly. // note: OS X 10.7 also, this workaround is required. see #1476 method = class_getInstanceMethod(klass, sel); if (method != NULL) { goto recache2; } //#endif fill_ocache(cache, self, klass, (IMP)objc_msgSend, sel, NULL, argc); goto dispatch; } // Let's see if are not trying to call a Ruby method that accepts // a regular argument then an optional Hash argument, to be // compatible with the Ruby specification. const char *selname = (const char *)sel; size_t selname_len = strlen(selname); if (argc > 1) { const char *p = strchr(selname, ':'); if (p != NULL && p + 1 != '\0') { char *tmp = (char *)malloc(selname_len + 1); assert(tmp != NULL); strncpy(tmp, selname, p - selname + 1); tmp[p - selname + 1] = '\0'; sel = sel_registerName(tmp); VALUE h = rb_hash_new(); bool ok = true; p += 1; for (int i = 1; i < argc; i++) { const char *p2 = strchr(p, ':'); if (p2 == NULL) { ok = false; break; } strlcpy(tmp, p, selname_len); tmp[p2 - p] = '\0'; p = p2 + 1; rb_hash_aset(h, ID2SYM(rb_intern(tmp)), argv[i]); } free(tmp); tmp = NULL; if (ok) { argc = 2; ((VALUE *)argv)[1] = h; // bad, I know... Method m = class_getInstanceMethod(klass, sel); if (m != NULL) { method = m; cache_method = false; goto recache2; } } } } // Enable helpers for classes which are not RubyObject based. if ((RCLASS_VERSION(klass) & RCLASS_IS_OBJECT_SUBCLASS) != RCLASS_IS_OBJECT_SUBCLASS) { // Let's try to see if we are not given a helper selector. SEL new_sel = helper_sel(selname, selname_len); if (new_sel != NULL) { Method m = class_getInstanceMethod(klass, new_sel); if (m != NULL) { sel = new_sel; method = m; // We need to invert arguments because // #[]= and setObject:forKey: take arguments // in a reverse order if (new_sel == selSetObjectForKey && argc == 2) { VALUE swap = argv[0]; ((VALUE *)argv)[0] = argv[1]; ((VALUE *)argv)[1] = swap; cache_method = false; } goto recache2; } } } // Let's see if we are not trying to call a BridgeSupport function. if (selname[selname_len - 1] == ':') { selname_len--; } std::string name(selname, selname_len); bs_element_function_t *bs_func = GET_CORE()->find_bs_function(name); if (bs_func != NULL) { if ((unsigned)argc < bs_func->args_count || ((unsigned)argc > bs_func->args_count && bs_func->variadic == false)) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, bs_func->args_count); } std::string types; vm_gen_bs_func_types(argc, argv, bs_func, types); cache->flag = MCACHE_FCALL; cache->sel = sel; cache->klass = klass; cache->as.fcall.bs_function = bs_func; cache->as.fcall.imp = (IMP)dlsym(RTLD_DEFAULT, bs_func->name); assert(cache->as.fcall.imp != NULL); cache->as.fcall.stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub( types, bs_func->variadic, bs_func->args_count, false); } else { // Still nothing, then let's call #method_missing. goto call_method_missing; } } } dispatch: if (cache->flag & MCACHE_RCALL) { if (!cache_method) { cache->flag = 0; } #if ROXOR_VM_DEBUG printf("ruby dispatch %c[<%s %p> %s] (imp %p block %p argc %d opt %d cache %p cached %s)\n", class_isMetaClass(klass) ? '+' : '-', class_getName(klass), (void *)self, sel_getName(sel), cache->as.rcall.node->ruby_imp, block, argc, opt, cache, cached ? "true" : "false"); #endif bool block_already_current = vm->is_block_current(block); Class current_klass = vm->get_current_class(); if (!block_already_current) { vm->add_current_block(block); } vm->set_current_class(NULL); Class old_current_super_class = vm->get_current_super_class(); vm->set_current_super_class(current_super_class); SEL old_current_super_sel = vm->get_current_super_sel(); vm->set_current_super_sel(current_super_sel); const bool should_pop_broken_with = sel != selInitialize && sel != selInitialize2; struct Finally { bool block_already_current; Class current_class; Class current_super_class; SEL current_super_sel; bool should_pop_broken_with; RoxorVM *vm; Finally(bool _block_already_current, Class _current_class, Class _current_super_class, SEL _current_super_sel, bool _should_pop_broken_with, RoxorVM *_vm) { block_already_current = _block_already_current; current_class = _current_class; current_super_class = _current_super_class; current_super_sel = _current_super_sel; should_pop_broken_with = _should_pop_broken_with; vm = _vm; } ~Finally() { if (!block_already_current) { vm->pop_current_block(); } vm->set_current_class(current_class); if (should_pop_broken_with) { vm->pop_broken_with(); } vm->set_current_super_class(current_super_class); vm->set_current_super_sel(current_super_sel); vm->pop_current_binding(); } } finalizer(block_already_current, current_klass, old_current_super_class, old_current_super_sel, should_pop_broken_with, vm); // DTrace probe: method__entry if (MACRUBY_METHOD_ENTRY_ENABLED()) { char *class_name = (char *)rb_class2name((VALUE)klass); char *method_name = (char *)sel_getName(sel); char file[PATH_MAX]; unsigned long line = 0; GET_CORE()->symbolize_backtrace_entry(1, file, sizeof file, &line, NULL, 0); MACRUBY_METHOD_ENTRY(class_name, method_name, file, line); } VALUE v = ruby_dispatch(top, self, sel, cache->as.rcall.node, opt, argc, argv); // DTrace probe: method__return if (MACRUBY_METHOD_RETURN_ENABLED()) { char *class_name = (char *)rb_class2name((VALUE)klass); char *method_name = (char *)sel_getName(sel); char file[PATH_MAX]; unsigned long line = 0; GET_CORE()->symbolize_backtrace_entry(1, file, sizeof file, &line, NULL, 0); MACRUBY_METHOD_RETURN(class_name, method_name, file, line); } return v; } else if (cache->flag & MCACHE_OCALL) { if (cache->as.ocall.argc != argc) { goto recache; } if (!cache_method) { cache->flag = 0; } if (block != NULL) { rb_warn("passing a block to an Objective-C method - " \ "will be ignored"); } else if (sel == selNew) { if (self == rb_cNSMutableArray) { self = rb_cRubyArray; } } else if (sel == selClass) { // Because +[NSObject class] returns self. if (RCLASS_META(klass)) { return RCLASS_MODULE(self) ? rb_cModule : rb_cClass; } // Because the CF classes should be hidden, for Ruby compat. if (self == Qnil) { return rb_cNilClass; } if (self == Qtrue) { return rb_cTrueClass; } if (self == Qfalse) { return rb_cFalseClass; } return rb_class_real((VALUE)klass, true); } #if ROXOR_VM_DEBUG printf("objc dispatch %c[<%s %p> %s] imp=%p cache=%p argc=%d (cached=%s)\n", class_isMetaClass(klass) ? '+' : '-', class_getName(klass), (void *)self, sel_getName(sel), cache->as.ocall.imp, cache, argc, cached ? "true" : "false"); #endif id ocrcv = RB2OC(self); if (cache->as.ocall.bs_method != NULL) { Class ocklass = object_getClass(ocrcv); for (int i = 0; i < (int)cache->as.ocall.bs_method->args_count; i++) { bs_element_arg_t *arg = &cache->as.ocall.bs_method->args[i]; if (arg->sel_of_type != NULL) { // BridgeSupport tells us that this argument contains a // selector of the given type, but we don't have any // information regarding the target. RubyCocoa and the // other ObjC bridges do not really require it since they // use the NSObject message forwarding mechanism, but // MacRuby registers all methods in the runtime. // // Therefore, we apply here a naive heuristic by assuming // that either the receiver or one of the arguments of this // call is the future target. const int arg_i = arg->index; assert(arg_i >= 0 && arg_i < argc); if (argv[arg_i] != Qnil) { ID arg_selid = rb_to_id(argv[arg_i]); SEL arg_sel = sel_registerName(rb_id2name(arg_selid)); if (reinstall_method_maybe(ocklass, arg_sel, arg->sel_of_type)) { goto sel_target_found; } for (int j = 0; j < argc; j++) { if (j != arg_i && !SPECIAL_CONST_P(argv[j])) { if (reinstall_method_maybe(*(Class *)argv[j], arg_sel, arg->sel_of_type)) { goto sel_target_found; } } } } sel_target_found: // There can only be one sel_of_type argument. break; } } } return __rb_vm_objc_dispatch(cache->as.ocall.stub, cache->as.ocall.imp, ocrcv, sel, argc, argv); } else if (cache->flag & MCACHE_FCALL) { #if ROXOR_VM_DEBUG printf("C dispatch %s() imp=%p argc=%d (cached=%s)\n", cache->as.fcall.bs_function->name, cache->as.fcall.imp, argc, cached ? "true" : "false"); #endif return (*cache->as.fcall.stub)(cache->as.fcall.imp, argc, argv); } printf("method dispatch is b0rked\n"); abort(); call_method_missing: // Before calling method_missing, let's check if we are not in the following // cases: // // def foo; end; foo(42) // def foo(x); end; foo // // If yes, we need to raise an ArgumentError exception instead. const char *selname = sel_getName(sel); const size_t selname_len = strlen(selname); SEL new_sel = 0; if (argc > 0 && selname[selname_len - 1] == ':') { char buf[100]; assert(sizeof buf > selname_len - 1); strlcpy(buf, selname, sizeof buf); buf[selname_len - 1] = '\0'; new_sel = sel_registerName(buf); } else if (argc == 0) { char buf[100]; snprintf(buf, sizeof buf, "%s:", selname); new_sel = sel_registerName(buf); } if (new_sel != 0) { Method m = class_getInstanceMethod(klass, new_sel); if (m != NULL) { IMP mimp = method_getImplementation(m); if (!UNAVAILABLE_IMP(mimp)) { unsigned expected_argc; rb_vm_method_node_t *node = GET_CORE()->method_node_get(m); if (node != NULL) { expected_argc = node->arity.min; } else { expected_argc = rb_method_getNumberOfArguments(m); expected_argc -= 2; // removing receiver and selector } rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, expected_argc); } } } rb_vm_method_missing_reason_t status; if (opt & DISPATCH_VCALL) { status = METHOD_MISSING_VCALL; } else if (opt & DISPATCH_SUPER) { status = METHOD_MISSING_SUPER; } else { status = METHOD_MISSING_DEFAULT; } return method_missing((VALUE)self, sel, block, argc, argv, status); }
Class metaclassForName(const char *className) { return object_getClass((id)objc_lookUpClass(className)); }
Method class_getClassMethod(Class aClass, SEL aSelector) { return class_getInstanceMethod(object_getClass((id)aClass), aSelector); }
Ivar class_getClassVariable(Class cls, const char* name) { // Note: We don't have compiler support for cvars in ObjC return class_getInstanceVariable(object_getClass((id)cls), name); }
const char* object_getClassName(id obj) { return class_getName(object_getClass(obj)); }
const char *object_getClassName(id obj) { CHECK_ARG(obj); return class_getName(object_getClass(obj)); }
Class RegisterClass(const class_t* cls, intptr_t slide, uint32_t image_index) { if (nullptr == cls) { return nullptr; } auto & g_classPointers = getClassPointers(); auto itClass = g_classPointers.find(cls); if (itClass != g_classPointers.end()) { LOG << "Found existing class @" << itClass->second << std::endl; return itClass->second; } Class super = RegisterClass(cls->superclass, slide, image_index); LOG << "...superclass is @" << super << std::endl; if (nullptr != super) { LOG << "...superclass name " << (((uintptr_t)super != (uintptr_t)cls->superclass) ? cls->superclass->data()->className : class_getName(super)) << std::endl; LOG << "...super name " << class_getName(super) << std::endl; } if (nullptr == cls->data()) { // TODO: Is this a bad pointer filled in, or are we supposed to do something different with this? // This is from when we call RegisterClass recursively sometimes... std::cerr << "Error - Null class data at class @" << cls << std::endl; return nullptr; } if (nullptr == cls->data()->className) { // TODO: Is this a bad pointer filled in, or are we supposed to do something different with this? // This is from when we call RegisterClass recursively sometimes... std::cerr << "Error - Null class name at class @" << cls << std::endl; return nullptr; } LOG << "Processing ObjC class " << cls->data()->className << std::endl; Class sameClass = (Class)objc_getClass(cls->data()->className); if (nullptr != sameClass) { LOG << "Found a class with the same name @" << sameClass << std::endl; return sameClass; } LOG << "obj_allocateClassPair(" << super << ", " << cls->data()->className << ")" << std::endl; Class conv = objc_allocateClassPair(super, cls->data()->className, 0); if (nullptr == conv) { std::cerr << "Failed to allocate class " << cls->data()->className << std::endl; return nullptr; } Class meta = object_getClass(id(conv)); const class_ro_t* ro = cls->data(); if (ro->baseMethods) ConvertMethodListGen(conv, ro->baseMethods, image_index); if (g_classPointers.find(cls->isa) == g_classPointers.end()) { const class_ro_t* roMeta = cls->isa->data(); if (roMeta->baseMethods) ConvertMethodListGen(meta, roMeta->baseMethods, image_index); } if (ro->ivars) ConvertIvarList(conv, ro->ivars); if (ro->baseProtocols) AddClassProtocols(conv, ro->baseProtocols, slide); if (ro->baseProperties) { ConvertProperties(ro->baseProperties, [conv](const char* name, const objc_property_attribute_t* attr, unsigned int count) { class_addProperty(conv, name, attr, count); bug_gnustepFixPropertyCount(conv); }); } // conv->instance_size = ro->instSize; objc_registerClassPair(conv); LOG << "ObjC class " << cls->data()->className << " now @" << conv << std::endl; g_classPointers[cls] = conv; g_classPointers[conv] = conv; g_classPointers[meta] = meta; return conv; }
id UndefinedClassInitialize(id obj, SEL sel) { fprintf(stderr, "Undefined class %s used\n", class_getName(object_getClass(obj))); return obj; }
static void setup_class(Class cls) { const char* superclass; Class metaclass = object_getClass((id)cls); if (cls->info & OBJC_CLASS_INFO_SETUP) return; if ((superclass = ((struct objc_abi_class*)cls)->superclass) != NULL) { Class super = Nil; // Classes loaded from the image contain a superclass name instead of a superclass. Resolve it. if ((cls->info & OBJC_CLASS_INFO_CREATED_RUNTIME) == 0) { super = objc_classname_to_class(superclass); } else { super = (Class)superclass; } if (super == (Class)nil) return; setup_class(super); if (!(super->info & OBJC_CLASS_INFO_SETUP)) return; cls->superclass = super; metaclass->superclass = object_getClass((id)super); add_subclass(cls); add_subclass(metaclass); } else metaclass->superclass = cls; // Calculate size based on ivars int slide = 0; cls->instance_size = -(int)cls->instance_size; if (cls->superclass) { slide = cls->superclass->instance_size; } if (slide) { struct objc_ivar_list* list = (struct objc_ivar_list*)cls->ivars; if (list) { // If first ivar has a negative offset (???) .. increase slide by that amount if (list->count > 0) { if ((int)list->ivars[0].offset < 0) { printf("Warning: Class %s has first ivar %s with a negative offset of %d!\n", cls->name, list->ivars[0].name, list->ivars[0].offset); slide += -(int)list->ivars[0].offset; } } for (unsigned i = 0; i < list->count; i++) { struct objc_ivar* curIvar = &list->ivars[i]; curIvar->offset += slide; *((int**)cls->ivar_offsets)[i] += slide; assert(*((int**)cls->ivar_offsets)[i] == curIvar->offset); } } } cls->instance_size += slide; cls->info |= OBJC_CLASS_INFO_SETUP; metaclass->info |= OBJC_CLASS_INFO_SETUP; metaclass->instance_size = sizeof(struct objc_class); }
void* UndefMgr::generateNew(const char* name) { bool isClass = (strncmp(name, "OBJC_CLASS", 10) == 0); bool isMetaClass = (strncmp(name, "OBJC_METACLASS", 14) == 0); if (isClass || isMetaClass) { // Strip the OBJC_CLASS_$_ or OBJC_METACLASS_$_ name = &name[(isMetaClass) ? 17 : 13]; // Objective-C class or metaclass info static std::map<std::string, Class> undefClasses; Class classStub = nullptr; auto classIter = undefClasses.find(name); if (classIter != undefClasses.end()) { classStub = classIter->second; } else if (NULL == (classStub = (Class)objc_getClass(name))) { classStub = objc_allocateClassPair(nullptr, name, 0); if (isMetaClass) { // Put in a dummy initialize method that lets us know the class got used IMP imp = reinterpret_cast<IMP>(&UndefinedClassInitialize); SEL sel = sel_registerTypedName_np("initialize", NULL); class_addMethod(object_getClass(reinterpret_cast<id>(classStub)), sel, imp, NULL); } objc_registerClassPair(classStub); Darling::MachOMgr::instance()->registerNativeClass(object_getClass(reinterpret_cast<id>(classStub))); Darling::MachOMgr::instance()->registerNativeClass(classStub); fprintf(stderr, "Undef class for class %s at %p\n", class_getName(classStub), classStub); undefClasses[name] = classStub; } return (isClass) ? classStub : object_getClass(reinterpret_cast<id>(classStub)); } else if (strncmp(name, "OBJC_IVAR", 9) == 0) { // Objective-C ivar typedef struct { const char *name; const char *type; int offset; } objc_ivar; objc_ivar *ivarStub = new objc_ivar; ivarStub->name = &name[12]; ivarStub->type = ""; ivarStub->offset = 0; return ivarStub; } else { // Generate a function stub UndefinedFunction * uf = new UndefinedFunction(); uf->init(name); return uf->getPointer(); } }
OBJCRT_EXPORT void* object_getIndexedIvars(id obj) { return (void*)((uint8_t*)obj + OBJC_ID_PADDED(class_getInstanceSize(object_getClass(obj)))); }