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); }
struct objc_ivar * class_getInstanceVariable (Class class_, const char *name) { if (class_ != Nil && name != NULL && ! CLS_IS_IN_CONSTRUCTION (class_)) { while (class_ != Nil) { struct objc_ivar_list *ivars = class_->ivars; if (ivars != NULL) { int i; for (i = 0; i < ivars->ivar_count; i++) { struct objc_ivar *ivar = &(ivars->ivar_list[i]); if (!strcmp (ivar->ivar_name, name)) return ivar; } } class_ = class_getSuperclass (class_); } } return NULL; }
Method class_getInstanceMethod(Class aClass, SEL aSelector) { CHECK_ARG(aClass); CHECK_ARG(aSelector); // If the class has a dtable installed, then we can use the fast path if (classHasInstalledDtable(aClass)) { // Do a dtable lookup to find out which class the method comes from. struct objc_slot *slot = objc_get_slot(aClass, aSelector); if (NULL == slot) { slot = objc_get_slot(aClass, sel_registerName(sel_getName(aSelector))); if (NULL == slot) { return NULL; } } // Now find the typed variant of the selector, with the correct types. aSelector = slot->selector; // Then do the slow lookup to find the method. return class_getInstanceMethodNonrecursive(slot->owner, aSelector); } Method m = class_getInstanceMethodNonrecursive(aClass, aSelector); if (NULL != m) { return m; } return class_getInstanceMethod(class_getSuperclass(aClass), aSelector); }
Ivar class_getInstanceVariable(Class cls, const char *name) { if (Nil != cls && NULL != name) { while (cls != Nil) { struct objc_ivar_list *ivarlist = cls->ivars; if (ivarlist != NULL) { int i; for (i = 0; i < ivarlist->ivar_count; i++) { Ivar ivar = &ivarlist->ivar_list[i]; if (strcmp(ivar->ivar_name, name) == 0) { return ivar; } } } cls = class_getSuperclass(cls); } } return NULL; }
VALUE rb_class_super(VALUE klass) { if (klass == 0) { return 0; } return (VALUE)class_getSuperclass((Class)klass); }
rb_vm_method_t * rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope) { SEL sel = 0; IMP imp = NULL; rb_vm_method_node_t *node = NULL; // TODO honor scope if (!rb_vm_lookup_method2((Class)klass, mid, &sel, &imp, &node)) { rb_print_undef(klass, mid, 0); } Class k, oklass = (Class)klass; while ((k = class_getSuperclass(oklass)) != NULL) { if (!rb_vm_lookup_method(k, sel, NULL, NULL)) { break; } oklass = k; } Method method = class_getInstanceMethod((Class)klass, sel); assert(method != NULL); int arity; rb_vm_method_node_t *new_node; if (node == NULL) { arity = rb_method_getNumberOfArguments(method) - 2; new_node = NULL; } else { arity = rb_vm_arity_n(node->arity); new_node = (rb_vm_method_node_t *)xmalloc(sizeof(rb_vm_method_node_t)); memcpy(new_node, node, sizeof(rb_vm_method_node_t)); } rb_vm_method_t *m = (rb_vm_method_t *)xmalloc(sizeof(rb_vm_method_t)); m->oclass = (VALUE)oklass; m->rclass = klass; GC_WB(&m->recv, obj); m->sel = sel; m->arity = arity; GC_WB(&m->node, new_node); // Let's allocate a static cache here, since a rb_vm_method_t must always // point to the method it was created from. struct mcache *c = (struct mcache *)xmalloc(sizeof(struct mcache)); if (new_node == NULL) { fill_ocache(c, obj, oklass, imp, sel, method, arity); } else { fill_rcache(c, oklass, sel, new_node); } GC_WB(&m->cache, c); return m; }
static void rb_objc_recorder(task_t task, void *context, unsigned type_mask, vm_range_t *ranges, unsigned range_count) { struct rb_objc_recorder_context *ctx; vm_range_t *r, *end; ctx = (struct rb_objc_recorder_context *)context; for (r = ranges, end = ranges + range_count; r < end; r++) { auto_memory_type_t type = auto_zone_get_layout_type(__auto_zone, (void *)r->address); if (type != AUTO_OBJECT_SCANNED && type != AUTO_OBJECT_UNSCANNED) { continue; } if (*(Class *)r->address == NULL) { continue; } if (ctx->class_of != 0) { Class c; bool ok = false; for (c = *(Class *)r->address; c != NULL; c = class_getSuperclass(c)) { if (c == (Class)ctx->class_of) { ok = true; break; } } if (!ok) { continue; } } switch (TYPE(r->address)) { case T_NONE: case T_NODE: continue; case T_ICLASS: case T_CLASS: case T_MODULE: rb_bug("object %p of type %d should not be recorded", (void *)r->address, TYPE(r->address)); case T_NATIVE: if (rb_objc_is_placeholder((void *)r->address)) { continue; } } rb_yield((VALUE)r->address); ctx->break_value = rb_vm_pop_broken_value(); ctx->count++; } }
/// @see http://www.sealiesoftware.com/blog/archive/2009/04/14/objc_explain_Classes_and_metaclasses.html XAOriginImpType originImplementationType(Class cls, SEL selector) { Class superclass; if (!class_respondsToSelector(cls, selector)) { return XAOriginImpTypeNotExists; } else if ((superclass = class_getSuperclass(cls)) && (class_getMethodImplementation(cls, selector) == class_getMethodImplementation(superclass, selector))) { return XAOriginImpTypeExistsInSuperclass; } else { return XAOriginImpTypeExists; } }
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; }
Method class_getInstanceMethod(Class aClass, SEL aSelector) { Method method = class_getInstanceMethodNonrecursive(aClass, aSelector); if (method == NULL) { // TODO: Check if this should be NULL or aClass Class superclass = class_getSuperclass(aClass); if (superclass == NULL) { return NULL; } return class_getInstanceMethod(superclass, aSelector); } return method; }
BOOL class_conformsToProtocol(Class cls, Protocol *protocol) { if (Nil == cls || NULL == protocol) { return NO; } for ( ; Nil != cls ; cls = class_getSuperclass(cls)) { for (struct objc_protocol_list *protocols = cls->protocols; protocols != NULL ; protocols = protocols->next) { for (int i=0 ; i<protocols->count ; i++) { Protocol *p1 = (Protocol*)protocols->list[i]; if (protocol_conformsToProtocol(p1, protocol)) { return YES; } } } } return NO; }
/* 'is_kind_of_exception_matcher' is our default exception matcher - it determines if the object 'exception' is of class 'catch_class', or of a subclass. */ static int is_kind_of_exception_matcher (Class catch_class, id exception) { /* NULL catch_class is catch-all (eg, @catch (id object)). */ if (catch_class == Nil) return 1; /* If exception is nil (eg, @throw nil;), then it can only be catched by a catch-all (eg, @catch (id object)). */ if (exception != nil) { Class c; for (c = exception->class_pointer; c != Nil; c = class_getSuperclass (c)) if (c == catch_class) return 1; } return 0; }
static void range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size) { const uint64_t end_addr = ptr_addr + ptr_size; range_contains_data_callback_info_t *info = (range_contains_data_callback_info_t *)baton; switch (info->type) { case eDataTypeAddress: // Check if the current malloc block contains an address specified by "info->addr" if (ptr_addr <= info->addr && info->addr < end_addr) { ++info->match_count; malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr, type }; g_matches.push_back(match, info->unique); } break; case eDataTypeContainsData: // Check if the current malloc block contains data specified in "info->data" { const uint32_t size = info->data.size; if (size < ptr_size) // Make sure this block can contain this data { uint8_t *ptr_data = NULL; if (task_peek (task, ptr_addr, ptr_size, (void **)&ptr_data) == KERN_SUCCESS) { const void *buffer = info->data.buffer; assert (ptr_data); const uint32_t align = info->data.align; for (uint64_t addr = ptr_addr; addr < end_addr && ((end_addr - addr) >= size); addr += align, ptr_data += align) { if (memcmp (buffer, ptr_data, size) == 0) { ++info->match_count; malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr, type }; g_matches.push_back(match, info->unique); } } } else { printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size); } } } break; case eDataTypeObjC: // Check if the current malloc block contains an objective C object // of any sort where the first pointer in the object is an OBJC class // pointer (an isa) { malloc_block_contents *block_contents = NULL; if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) { // We assume that g_objc_classes is up to date // that the class list was verified to have some classes in it // before calling this function const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); if (objc_class_idx != UINT32_MAX) { bool match = false; if (info->objc.match_isa == 0) { // Match any objective C object match = true; } else { // Only match exact isa values in the current class or // optionally in the super classes if (info->objc.match_isa == block_contents->isa) match = true; else if (info->objc.match_superclasses) { Class super = class_getSuperclass(block_contents->isa); while (super) { match = super == info->objc.match_isa; if (match) break; super = class_getSuperclass(super); } } } if (match) { //printf (" success\n"); ++info->match_count; malloc_match match = { (void *)ptr_addr, ptr_size, 0, type }; g_matches.push_back(match, info->unique); } else { //printf (" error: wrong class: %s\n", dl_info.dli_sname); } } else { //printf ("\terror: symbol not objc class: %s\n", dl_info.dli_sname); return; } } } break; case eDataTypeHeapInfo: // Check if the current malloc block contains an objective C object // of any sort where the first pointer in the object is an OBJC class // pointer (an isa) { malloc_block_contents *block_contents = NULL; if (task_peek (task, ptr_addr, sizeof(void *), (void **)&block_contents) == KERN_SUCCESS) { // We assume that g_objc_classes is up to date // that the class list was verified to have some classes in it // before calling this function const uint32_t objc_class_idx = g_objc_classes.FindClassIndex (block_contents->isa); if (objc_class_idx != UINT32_MAX) { // This is an objective C object g_objc_class_snapshot.AddInstance (objc_class_idx, ptr_size); } else { // Classify other heap info } } } break; } }
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 superclassForName(const char *className) { return class_getSuperclass(objc_lookUpClass(className)); }