Exemple #1
0
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);
}
Exemple #2
0
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;
}
Exemple #3
0
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);
}
Exemple #4
0
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;
}
Exemple #5
0
VALUE
rb_class_super(VALUE klass)
{
    if (klass == 0) {
	return 0;
    }
    return (VALUE)class_getSuperclass((Class)klass);
}
Exemple #6
0
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;
}
Exemple #7
0
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;
  }
}
Exemple #9
0
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;
}
Exemple #10
0
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;
}
Exemple #11
0
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;
}
Exemple #12
0
/* '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;
}
Exemple #13
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;

    }
}
Exemple #14
0
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,
		    &current_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));
}