Exemple #1
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 #2
0
static VALUE
method_inspect(VALUE method, SEL sel)
{
    rb_vm_method_t *data;
    VALUE str;
    const char *s;
    const char *sharp = "#";

    Data_Get_Struct(method, rb_vm_method_t, data);
    str = rb_str_buf_new2("#<");
    s = rb_obj_classname(method);
    rb_str_buf_cat2(str, s);
    rb_str_buf_cat2(str, ": ");

    rb_str_buf_cat2(str, rb_class2name(data->rclass));
    if (data->rclass != data->oclass) {
	rb_str_buf_cat2(str, "(");
	rb_str_buf_cat2(str, rb_class2name(data->oclass));
	rb_str_buf_cat2(str, ")");
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_buf_cat2(str, sel_getName(data->sel));
    rb_str_buf_cat2(str, ">");

    return str;
}
Exemple #3
0
static VALUE
rb_struct_set(VALUE obj, SEL sel, VALUE val)
{
    VALUE members, slot;
    long i;

    // foo=: -> foo
    char buf[100];
    const size_t s = strlcpy(buf, sel_getName(sel), sizeof buf);
    buf[s - 2] = '\0';
    ID field = rb_intern(buf);

    members = rb_struct_members(obj);
    rb_struct_modify(obj);
    for (i=0; i<RARRAY_LEN(members); i++) {
	slot = RARRAY_AT(members, i);
	if (SYM2ID(slot) == field) {
	    GC_WB(&RSTRUCT_PTR(obj)[i], val);
	    return val;
	}
    }
    rb_name_error(rb_frame_this_func(), "`%s' is not a struct member",
		  rb_id2name(field));
    return Qnil;		/* not reached */
}
Exemple #4
0
static VALUE
method_name(VALUE obj, SEL sel)
{
    rb_vm_method_t *data;
    Data_Get_Struct(obj, rb_vm_method_t, data);
    ID mid = rb_intern(sel_getName(data->sel));
    return ID2SYM(mid);
}
Exemple #5
0
VALUE
rb_sel_to_sym(SEL sel)
{
    if (sel == 0) {
	return Qnil;
    }
    return ID2SYM(rb_intern(sel_getName(sel)));
}
Exemple #6
0
VALUE
rb_f_notimplement(VALUE rcv, SEL sel)
{
    rb_raise(rb_eNotImpError,
	    "%s() function is unimplemented on this machine",
	    sel_getName(sel));
    return Qnil; // never reached
}
Exemple #7
0
static VALUE
inspect_enumerator(VALUE obj, VALUE dummy, int recur)
{
    struct enumerator *e;
    const char *cname;
    VALUE eobj, str;
    int tainted, untrusted;

    Data_Get_Struct(obj, struct enumerator, e);

    cname = rb_obj_classname(obj);

    if (!e || e->obj == Qundef) {
	return rb_sprintf("#<%s: uninitialized>", cname);
    }

    if (recur) {
	str = rb_sprintf("#<%s: ...>", cname);
	OBJ_TAINT(str);
	return str;
    }

    eobj = e->obj;

    tainted   = OBJ_TAINTED(eobj);
    untrusted = OBJ_UNTRUSTED(eobj);

    /* (1..100).each_cons(2) => "#<Enumerator: 1..100:each_cons(2)>" */
    str = rb_sprintf("#<%s: ", cname);
    rb_str_concat(str, rb_inspect(eobj));
    rb_str_buf_cat2(str, ":");
    rb_str_buf_cat2(str, sel_getName(e->sel));

    if (e->args) {
	long   argc = RARRAY_LEN(e->args);
	VALUE *argv = (VALUE*)RARRAY_PTR(e->args);

	rb_str_buf_cat2(str, "(");

	while (argc--) {
	    VALUE arg = *argv++;

	    rb_str_concat(str, rb_inspect(arg));
	    rb_str_buf_cat2(str, argc > 0 ? ", " : ")");

	    if (OBJ_TAINTED(arg)) tainted = TRUE;
	    if (OBJ_UNTRUSTED(arg)) untrusted = TRUE;
	}
    }

    rb_str_buf_cat2(str, ">");

    if (tainted) OBJ_TAINT(str);
    if (untrusted) OBJ_UNTRUST(str);
    return str;
}
Exemple #8
0
/* __attribute__((noinline)) */
__attribute__((always_inline)) struct objc_slot *
objc_msg_lookup_default(id *receiver, SEL selector, id sender)
{
	Class class = objc_object_get_class_inline((*receiver));
	struct objc_slot *result = objc_dtable_lookup(class->dtable, selector);
	if (UNLIKELY(NULL == result)) {
		dtable_t dtable = dtable_for_class(class);
		/* Install the dtable if it hasn't already been initialized. */
		if (dtable == uninstalled_dtable){
			objc_debug_log("Sending initialize to receiver %p, selector %s[%d]\n",
						   *receiver, sel_getName(selector), (unsigned)selector);
			objc_send_initialize(*receiver);
			dtable = dtable_for_class(class);
			result = objc_dtable_lookup(dtable, selector);
		}else{
Exemple #9
0
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
	CHECK_ARG(cls);
	CHECK_ARG(name);
	CHECK_ARG(imp);
	CHECK_ARG(types);
	const char *methodName = sel_getName(name);
	struct objc_method_list *methods;
	for (methods=cls->methods; methods!=NULL ; methods=methods->next)
	{
		for (int i=0 ; i<methods->count ; i++)
		{
			Method method = &methods->methods[i];
			if (strcmp(sel_getName(method->selector), methodName) == 0)
			{
				return NO;
			}
		}
	}

	methods = malloc(sizeof(struct objc_method_list) + sizeof(struct objc_method));
	methods->next = cls->methods;
	cls->methods = methods;

	methods->count = 1;
	methods->methods[0].selector = sel_registerTypedName_np(methodName, types);
	methods->methods[0].types = strdup(types);
	methods->methods[0].imp = imp;

	if (objc_test_class_flag(cls, objc_class_flag_resolved))
	{
		add_method_list_to_class(cls, methods);
	}

	return YES;
}
void printMethodsForClass(Class cls)
{
  unsigned int totalCount;
  Method *methods __attribute__((cleanup(free_methods))) = class_copyMethodList(cls, &totalCount);
  
  printf("=> List Methods For Class: %s (%u)\n", class_getName(cls), totalCount);
  for (unsigned int i = 0; i<totalCount; i++) {
    Method method = methods[i];
    SEL sel = method_getName(method);
    IMP imp = method_getImplementation(method);
    printf("    method<%p>: %s \n", imp, sel_getName(sel));
  }
  printf("\n");
  
}
Exemple #11
0
void protocol_addMethodDescription(Protocol *aProtocol,
                                   SEL name,
                                   const char *types,
                                   BOOL isRequiredMethod,
                                   BOOL isInstanceMethod)
{
	if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
	if (nil != aProtocol->isa) { return; }
	Protocol2 *proto = (Protocol2*)aProtocol;
	struct objc_method_description_list **listPtr;
	if (isInstanceMethod)
	{
		if (isRequiredMethod)
		{
			listPtr = &proto->instance_methods;
		}
		else
		{
			listPtr = &proto->optional_instance_methods;
		}
	}
	else
	{
		if (isRequiredMethod)
		{
			listPtr = &proto->class_methods;
		}
		else
		{
			listPtr = &proto->optional_class_methods;
		}
	}
	if (NULL == *listPtr)
	{
		*listPtr = calloc(1, sizeof(struct objc_method_description_list) + sizeof(struct objc_method_description));
		(*listPtr)->count = 1;
	}
	else
	{
		(*listPtr)->count++;
		*listPtr = realloc(*listPtr, sizeof(struct objc_method_description_list) +
				sizeof(struct objc_method_description) * (*listPtr)->count);
	}
	struct objc_method_description_list *list = *listPtr;
	int index = list->count-1;
	list->methods[index].name = sel_getName(name);
	list->methods[index].types= types;
}
Exemple #12
0
static VALUE
method_missing(VALUE obj, SEL sel, rb_vm_block_t *block, int argc,
	const VALUE *argv, rb_vm_method_missing_reason_t call_status)
{
    if (sel == selAlloc) {
        rb_raise(rb_eTypeError, "allocator undefined for %s",
		RSTRING_PTR(rb_inspect(obj)));
    }

    GET_VM()->set_method_missing_reason(call_status);

    VALUE *new_argv = (VALUE *)xmalloc_ptrs(sizeof(VALUE) * (argc + 1));

    char buf[100];
    int n = snprintf(buf, sizeof buf, "%s", sel_getName(sel));
    if (buf[n - 1] == ':') {
	// Let's see if there are more colons making this a real selector.
	bool multiple_colons = false;
	for (int i = 0; i < (n - 1); i++) {
	    if (buf[i] == ':') {
		multiple_colons = true;
		break;
	    }
	}
	if (!multiple_colons) {
	    // Not a typical multiple argument selector. So as this is
	    // probably a typical ruby method name, chop off the colon.
	    buf[n - 1] = '\0';
	}
    }
    new_argv[0] = ID2SYM(rb_intern(buf));
    MEMCPY(&new_argv[1], argv, VALUE, argc);

    // In case the missing selector _is_ method_missing: OR the object does
    // not respond to method_missing: (this can happen for NSProxy-based
    // objects), directly trigger the exception.
    Class k = (Class)CLASS_OF(obj);
    if (sel == selMethodMissing
	    || class_getInstanceMethod(k, selMethodMissing) == NULL) {
	rb_vm_method_missing(obj, argc + 1, new_argv);
	return Qnil; // never reached
    }
    else {
	return rb_vm_call2(block, obj, (VALUE)k, selMethodMissing, argc + 1,
		new_argv);
    }
}
Exemple #13
0
static void
rb_export_method(VALUE klass, ID name, ID noex)
{
    rb_vm_method_node_t *node;
    SEL sel;

    if (klass == rb_cObject) {
	rb_secure(4);
    }

    if (!rb_vm_lookup_method2((Class)klass, name, &sel, NULL, &node)) {
	if (TYPE(klass) != T_MODULE
		|| !rb_vm_lookup_method2((Class)rb_cObject, name, &sel, NULL,
		    &node)) {
	    rb_print_undef(klass, name, 0);
	}
    }

    if (node == NULL) {
	rb_raise(rb_eRuntimeError,
		"can't change visibility of non Ruby method `%s'",
		sel_getName(sel));
    }

    long flags = (node->flags & ~VM_METHOD_PRIVATE) & ~VM_METHOD_PROTECTED;
    switch (noex) {
	case NOEX_PRIVATE:
	    flags |= VM_METHOD_PRIVATE;
	    break;

	case NOEX_PROTECTED:
	    flags |= VM_METHOD_PROTECTED;
	    break;

	default:
	    break;
    }

    if (node->flags != flags) {
	if (node->klass == (Class)klass) {
	    node->flags = flags;
	}
	else {
	    rb_vm_define_method2((Class)klass, sel, node, flags, false);
	}
    }
}
Exemple #14
0
PRIMITIVE VALUE
vm_dispatch(VALUE top, VALUE self, void *sel, void *block, unsigned char opt,
	int argc, VALUE *argv)
{
    if (opt & DISPATCH_SUPER) {
	if (sel == 0) {
	    rb_raise(rb_eNoMethodError, "super called outside of method");
	}
    }

    VALUE buf[100];
    if (opt & DISPATCH_SPLAT) {
	if (argc == 1 && !SPECIAL_CONST_P(argv[1])
		&& *(VALUE *)argv[1] == rb_cRubyArray) {
	    argc = RARY(argv[1])->len;
	    argv = rary_ptr(argv[1]);
	}
	else {
	    VALUE *new_argv = buf;
	    vm_resolve_args(&new_argv, 100, &argc, argv);
	    argv = new_argv;
	}
	if (argc == 0) {
	    const char *selname = sel_getName((SEL)sel);
	    const size_t selnamelen = strlen(selname);
	    if (selname[selnamelen - 1] == ':') {
		// Because
		//   def foo; end; foo(*[])
		// creates foo but dispatches foo:.
		char buf[100];
		strncpy(buf, selname, sizeof buf);
		buf[selnamelen - 1] = '\0';
		sel = sel_registerName(buf);
	    }
	}
    }

    void *vm = rb_vm_current_vm();
    VALUE klass = vm_class_of(self);
    return rb_vm_call0(vm, top, self, (Class)klass, (SEL)sel,
	    (rb_vm_block_t *)block, opt, argc, argv);
}
Exemple #15
0
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
	if (Nil == cls) { return (IMP)0; }
	SEL sel = sel_registerTypedName_np(sel_getName(name), types);
	Method method = class_getInstanceMethodNonrecursive(cls, sel);
	if (method == NULL)
	{
		class_addMethod(cls, sel, imp, types);
		return NULL;
	}
	IMP old = (IMP)method->imp;
	method->imp = imp;

	if (objc_test_class_flag(cls, objc_class_flag_resolved))
	{
		objc_update_dtable_for_class(cls);
	}

	return old;
}
Exemple #16
0
static void
_objc_unload_IMPs_in_class(Class cl, void *kernel_module){
	if (cl->methods == NULL){
		return;
	}
	
	objc_debug_log("Unloading IMPs in class %s\n", class_getName(cl));
	
	for (objc_method_list *list = cl->methods; list != NULL; list = list->next){
		objc_debug_log("\t [%p]\n", list);
		for (int i = 0; i < list->size; ++i){
			Method m = &list->list[i];
			if (module_contains_IMP(kernel_module, m->implementation)){
				objc_debug_log("\t\t [%02i] - %s\n", i, sel_getName(m->selector));
				IMP old_imp = m->implementation;
				m->implementation = objc_unloaded_module_method;
				
				/* Since the selector name and types can actually be also
				 * allocated in the module, we better update the strings
				 * as well.
				 */
				m->selector_name = "__objc_unloaded_method";
				m->selector_types = sizeof(void*) == 4 ? "v8@0:4" : "v16@0:8";
				
				/* Update the dtable! */
				SparseArray *arr = (SparseArray*)cl->dtable;
				if (arr != NULL && arr != uninstalled_dtable){
					struct objc_slot *slot = SparseArrayLookup(arr, m->selector);
					if (slot != NULL && slot->implementation == old_imp){
						slot->implementation = m->implementation;
						
						slot->types = m->selector_types;
						
						++slot->version;
					}
				}
			}
		}
	}
}
Exemple #17
0
static void
fill_ocache(struct mcache *cache, VALUE self, Class klass, IMP imp, SEL sel,
	    Method method, int argc)
{
    cache->flag = MCACHE_OCALL;
    cache->sel = sel;
    cache->klass = klass;
    cache->as.ocall.imp = imp;
    cache->as.ocall.argc = argc;
    cache->as.ocall.bs_method = GET_CORE()->find_bs_method(klass, sel);

    char types[200];
    if (!rb_objc_get_types(self, klass, sel, method, cache->as.ocall.bs_method,
		types, sizeof types)) {
	printf("cannot get encoding types for %c[%s %s]\n",
		class_isMetaClass(klass) ? '+' : '-',
		class_getName(klass),
		sel_getName(sel));
	abort();
    }
    bool variadic = false;
    if (cache->as.ocall.bs_method != NULL
	    && cache->as.ocall.bs_method->variadic && method != NULL) {
	// TODO honor printf_format
	const int real_argc = rb_method_getNumberOfArguments(method) - 2;
	if (real_argc < argc) {
	    const size_t s = strlen(types);
	    assert(s + argc - real_argc < sizeof types);
	    for (int i = real_argc; i < argc; i++) {
		strlcat(types, "@", sizeof types);
	    }
	    argc = real_argc;
	}
	variadic = true;
    }
    cache->as.ocall.stub = (rb_vm_objc_stub_t *)GET_CORE()->gen_stub(types,
	    variadic, argc, true);
}
Exemple #18
0
struct objc_method_description protocol_getMethodDescription(Protocol *p,
  SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
{
  struct objc_method_description output = {NULL, NULL};

  if (p != NULL)
    {
      struct objc_method_description_list *methods;
      const char *name = sel_getName(aSel);
      int i;

      if (isInstanceMethod)
	{
          methods = p->instance_methods;
	}
      else
	{
	  methods = p->class_methods;
	}

      if (methods != NULL)
	{
	  for (i = 0; i < methods->count; i++)
	    {
	      if (!strcmp((char*)methods->list[i].name, name))
		{
		  output = methods->list[i];
		  // HACK: the name field of the objc_method_description struct 
		  // is supposed to be a selector, but testing reveals it is a string
		  output.name = sel_registerName((const char *) output.name);
		  break;
		}
	    }
	}
    }
  return output;
}
Exemple #19
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);
}
Exemple #20
0
void msg_debug_super(struct objc_super *super, SEL op)
{

	msg_debug(object_getClassName(super->receiver), sel_getName(op));
}
Exemple #21
0
static Method
rb_vm_super_lookup(Class klass, SEL sel, Class *super_class_p)
{
    // Locate the current method implementation.
    Class self_class = klass;
    Method method = class_getInstanceMethod(self_class, sel);
    if (method == NULL) {
	// The given selector does not exist, let's go through
	// #method_missing...
	*super_class_p = NULL;
	return NULL; 
    }
    IMP self_imp = method_getImplementation(method);

    // Iterate over ancestors, locate the current class and return the
    // super method, if it exists.
    VALUE ary = rb_mod_ancestors_nocopy((VALUE)klass);
    const int count = RARRAY_LEN(ary);
    bool klass_located = false;
#if ROXOR_VM_DEBUG
    printf("locating super method %s of class %s (%p) in ancestor chain: ", 
	    sel_getName(sel), rb_class2name((VALUE)klass), klass);
    for (int i = 0; i < count; i++) {
	VALUE sk = RARRAY_AT(ary, i);
	printf("%s (%p) ", rb_class2name(sk), (void *)sk);
    }
    printf("\n");
#endif
try_again:
    for (int i = 0; i < count; i++) {
        if (!klass_located && RARRAY_AT(ary, i) == (VALUE)self_class) {
            klass_located = true;
        }
        if (klass_located) {
            if (i < count - 1) {
                VALUE k = RARRAY_AT(ary, i + 1);
#if ROXOR_VM_DEBUG
		printf("looking in %s\n", rb_class2name((VALUE)k));
#endif

		Method method = class_getInstanceMethod((Class)k, sel);
		if (method == NULL) {
		    continue;
		}

		IMP imp = method_getImplementation(method);
		if (imp == self_imp || UNAVAILABLE_IMP(imp)) {
		    continue;
		}

		VALUE super = RCLASS_SUPER(k);
		if (super != 0 && class_getInstanceMethod((Class)super,
			    sel) == method) {
		    continue;
		}

#if ROXOR_VM_DEBUG
		printf("returning method %p of class %s (#%d)\n",
			method, rb_class2name(k), i + 1);
#endif

		*super_class_p = (Class)k;
		return method;
            }
        }
    }
    if (!klass_located) {
	// Could not locate the receiver's class in the ancestors list.
	// It probably means that the receiver has been extended somehow.
	// We therefore assume that the super method will be in the direct
	// superclass.
	klass_located = true;
	goto try_again;
    }

    *super_class_p = NULL;
    return NULL;
}
Exemple #22
0
bool
RoxorCore::respond_to(VALUE obj, VALUE klass, SEL sel, bool priv,
	bool check_override)
{
    if (klass == Qnil) {
	klass = CLASS_OF(obj);
    }
    else {
	assert(!check_override);
    }

    IMP imp = NULL;
    const bool overriden = check_override
	? ((imp = class_getMethodImplementation((Class)klass, selRespondTo))
		!= basic_respond_to_imp)
	: false;

    if (!overriden) {
	lock();
	const long key = respond_to_key((Class)klass, sel);
	std::map<long, int>::iterator iter = respond_to_cache.find(key);
	int iter_cached = (iter != respond_to_cache.end());
	unlock();
	int status;
	if (iter_cached) {
	    status = iter->second;
	}
	else {
	    Method m = class_getInstanceMethod((Class)klass, sel);
	    if (m == NULL) {
		const char *selname = sel_getName(sel);
		sel = helper_sel(selname, strlen(selname));
		if (sel != NULL) {
		    m = class_getInstanceMethod((Class)klass, sel);
		}
	    }

	    IMP imp = method_getImplementation(m);
	    if (UNAVAILABLE_IMP(imp) || imp == (IMP)rb_f_notimplement) {
		status = RESPOND_TO_NOT_EXIST;
	    }
	    else {
		rb_vm_method_node_t *node = method_node_get(m);
		if (node != NULL && (node->flags & VM_METHOD_PRIVATE)) {
		    status = RESPOND_TO_PRIVATE;
		}
		else {
		    status = RESPOND_TO_PUBLIC;
		}
	    }
	    lock();
	    respond_to_cache[key] = status;
	    unlock();
	}
	return status == RESPOND_TO_PUBLIC
	    || (priv && status == RESPOND_TO_PRIVATE);
    }
    else {
	if (imp == NULL || imp == _objc_msgForward) {
	    // The class does not respond to respond_to?:, it's probably
	    // NSProxy-based.
	    return false;
	}
	VALUE args[2];
	int n = 0;
	args[n++] = ID2SYM(rb_intern(sel_getName(sel)));
	if (priv) {
	    rb_vm_method_node_t *node = method_node_get(imp);
	    if (node != NULL
		    && (2 < node->arity.min
			|| (node->arity.max != -1 && 2 > node->arity.max))) {
		// Do nothing, custom respond_to? method incompatible arity.
	    }
	    else {
		args[n++] = Qtrue;
	    }
	}
	return rb_vm_call(obj, selRespondTo, n, args) == Qtrue;
    }
}
Exemple #23
0
void msg_debug_super_fixup(struct objc_super *super, struct fixup *fixup)
{
	msg_debug(object_getClassName(super->receiver), sel_getName(fixup->sel));
}
Exemple #24
0
PRIMITIVE VALUE
vm_sel_to_rval(void *sel)
{
    return sel == 0 ? Qnil : ID2SYM(rb_intern(sel_getName((SEL)sel)));
}
Exemple #25
0
BOOL sel_isMapped(SEL selector) {
    return (sel_getName(selector) != NULL) ? YES : NO;
}
Exemple #26
0
void msg_debug_fixup(id self, struct fixup *fixup)
{
	msg_debug(object_getClassName(self), sel_getName(fixup->sel));
}
Exemple #27
0
{
	objc_debug_log("Destroying dispatch tables.\n");
	
	objc_rw_lock_destroy(&initialize_lock);
	SparseArrayDestroy(&uninstalled_dtable);
	
	slot_pool_free();
}

static BOOL installMethodInDtable(Class class,
                                  Class owner,
                                  SparseArray *dtable,
                                  struct objc_method *method,
                                  BOOL replaceExisting)
{
	objc_debug_log("Installing method %s into dtable of class %s\n", sel_getName(method->selector), class_getName(class));
	
	objc_assert(uninstalled_dtable != dtable, "");
	SEL sel_id = method->selector;
	struct objc_slot *slot = SparseArrayLookup(dtable, sel_id);
	if (NULL != slot)
	{
		// If this method is the one already installed, pretend to install it again.
		if (slot->implementation == method->implementation) { return NO; }

		// If the existing slot is for this class, we can just replace the
		// implementation.  We don't need to bump the version; this operation
		// updates cached slots, it doesn't invalidate them.  
		if (slot->owner == owner)
		{
			// Don't replace methods if we're not meant to (if they're from
Exemple #28
0
void msg_debug_regular(id self, SEL op)
{
	msg_debug(object_getClassName(self), sel_getName(op));
}