Esempio n. 1
0
VALUE
rb_mod_init_copy(VALUE clone, SEL sel, VALUE orig)
{
    rb_obj_init_copy(clone, 0, orig);

    VALUE super;
    if (!RCLASS_RUBY(orig)) {
	super = orig;
	rb_warn("cloning class `%s' is not supported, creating a " \
		"subclass instead", rb_class2name(orig));
    }
    else {
	super = RCLASS_SUPER(orig);
    }
    RCLASS_SET_SUPER(clone, super);

    // Copy flags.
    unsigned long version_flag = RCLASS_IS_RUBY_CLASS;
    if ((RCLASS_VERSION(super) & RCLASS_IS_OBJECT_SUBCLASS)
	    == RCLASS_IS_OBJECT_SUBCLASS) {
	version_flag |= RCLASS_IS_OBJECT_SUBCLASS;
    }
    if (RCLASS_MODULE(orig)) {
	version_flag |= RCLASS_IS_MODULE;
    }
    RCLASS_SET_VERSION(clone, version_flag);
    if (!class_isMetaClass((Class)clone)) {
	// Clear type info.
	RCLASS_SET_VERSION(*(Class *)clone, RCLASS_VERSION(*(Class *)clone));
    }

    // Copy methods.
    rb_vm_copy_methods((Class)orig, (Class)clone);
    if (!class_isMetaClass((Class)orig)) {
	rb_vm_copy_methods(*(Class *)orig, *(Class *)clone);
    }

    // Copy ivars.
    CFMutableDictionaryRef orig_dict = rb_class_ivar_dict(orig);
    CFMutableDictionaryRef clone_dict;
    if (orig_dict != NULL) {
	clone_dict = CFDictionaryCreateMutableCopy(NULL, 0, orig_dict);
	rb_class_ivar_set_dict(clone, clone_dict);
	CFMakeCollectable(clone_dict);
    }
    else {
	clone_dict = rb_class_ivar_dict_or_create(clone);
    }

    // Remove the classpath & classid (name) so that they are not
    // copied over the new module / class.
    CFDictionaryRemoveValue(clone_dict, (const void *)id_classpath);
    CFDictionaryRemoveValue(clone_dict, (const void *)id_classid);
    return clone;
}
Esempio n. 2
0
File: class.c Progetto: MSch/MacRuby
void
rb_objc_class_sync_version(Class ocklass, Class ocsuper)
{
    const long super_version = RCLASS_VERSION(ocsuper);
    long klass_version = RCLASS_VERSION(ocklass);

    if (ocsuper == (Class)rb_cRubyObject
	|| (super_version & RCLASS_IS_OBJECT_SUBCLASS)
	    == RCLASS_IS_OBJECT_SUBCLASS) {
	klass_version |= RCLASS_IS_OBJECT_SUBCLASS;
    }

    RCLASS_SET_VERSION(ocklass, klass_version);
}
Esempio n. 3
0
File: class.c Progetto: MSch/MacRuby
VALUE
rb_singleton_class_clone(VALUE obj)
{
    VALUE klass = RBASIC(obj)->klass;
    if (!RCLASS_SINGLETON(klass)) {
	return klass;
    }

    // Create new singleton class.
    VALUE clone = rb_objc_create_class(NULL, RCLASS_SUPER(klass));

    // Copy ivars.
    CFMutableDictionaryRef ivar_dict = rb_class_ivar_dict(klass);
    if (ivar_dict != NULL) {
	CFMutableDictionaryRef cloned_ivar_dict =
	    CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)ivar_dict);
	rb_class_ivar_set_dict(clone, cloned_ivar_dict);
	CFMakeCollectable(cloned_ivar_dict);
    }

    // Copy methods.
    rb_vm_copy_methods((Class)klass, (Class)clone);	

    rb_singleton_class_attached(clone, obj);
    if (RCLASS_SUPER(clone) == rb_cRubyObject) {
	long v = RCLASS_VERSION(clone) ^ RCLASS_IS_OBJECT_SUBCLASS;
	RCLASS_SET_VERSION(clone, v);
    }
    RCLASS_SET_VERSION_FLAG(clone, RCLASS_IS_SINGLETON);
    return clone;
}
Esempio n. 4
0
File: class.c Progetto: MSch/MacRuby
void
rb_define_object_special_methods(VALUE klass)
{
    RCLASS_SET_VERSION(*(VALUE *)klass,
	    (RCLASS_VERSION(*(VALUE *)klass) | RCLASS_HAS_ROBJECT_ALLOC));

    rb_objc_define_method(*(VALUE *)klass, "new",
	    rb_class_new_instance_imp, -1);
    rb_objc_define_method(klass, "dup", rb_obj_dup, 0);
    rb_objc_define_private_method(klass, "initialize", rb_objc_init, -1);
    rb_objc_define_private_method(klass, "initialize_copy",
	    rb_obj_init_copy, 1);
    rb_objc_define_method(klass, "hash", rb_obj_id, 0);

    // To make sure singleton classes will be filtered.
    rb_objc_define_method(*(VALUE *)klass, "superclass", rb_obj_superclass, 0);
    rb_objc_define_method(klass, "class", rb_obj_class, 0);

    rb_objc_install_method(*(Class *)klass, selAllocWithZone,
	    (IMP)rb_obj_imp_allocWithZone);
    rb_objc_install_method((Class)klass, selIsEqual,
	    (IMP)rb_obj_imp_isEqual);
    rb_objc_install_method((Class)klass, selInit, (IMP)rb_obj_imp_init);
    rb_objc_install_method((Class)klass, selDescription,
	    (IMP)rb_obj_imp_description);

    // Create -copyWithZone:, since the method doesn't exist yet we need to
    // find the type encoding somewhere, here we check Symbol since it's
    // created very early. 
    Method m = class_getInstanceMethod((Class)rb_cSymbol, selCopyWithZone);
    assert(m != NULL);
    class_replaceMethod((Class)klass, selCopyWithZone, 
	    (IMP)rb_obj_imp_copyWithZone, method_getTypeEncoding(m));
}
Esempio n. 5
0
File: class.c Progetto: MSch/MacRuby
VALUE
rb_objc_create_class(const char *name, VALUE super)
{
    if (super == 0) {
	super = rb_cObject;
    }
    VALUE klass = rb_objc_alloc_class(name, super, T_CLASS, rb_cClass);
   
    if (super != rb_cNSObject && super != 0
	    && ((RCLASS_VERSION(*(VALUE *)super) & RCLASS_HAS_ROBJECT_ALLOC)
		== RCLASS_HAS_ROBJECT_ALLOC)) {
	RCLASS_SET_VERSION(*(VALUE *)klass,
		(RCLASS_VERSION(*(VALUE *)klass) | RCLASS_HAS_ROBJECT_ALLOC));
    }

    if (name != NULL && rb_class_tbl != NULL) {
	st_insert(rb_class_tbl, (st_data_t)rb_intern(name), (st_data_t)klass);
    }

    return klass;
}
Esempio n. 6
0
File: class.c Progetto: MSch/MacRuby
/* :nodoc: */
VALUE
rb_mod_init_copy(VALUE clone, SEL sel, VALUE orig)
{
    static ID classpath = 0;
    static ID classid = 0;

    rb_obj_init_copy(clone, 0, orig);
    {
	VALUE super;
	unsigned long version_flag;

	if (!RCLASS_RUBY(orig)) {
	    super = orig;
	    rb_warn("cloning class `%s' is not supported, creating a " \
		    "subclass instead", rb_class2name(orig));
	}
	else {
	    super = RCLASS_SUPER(orig);
	}
	RCLASS_SET_SUPER(clone, super);

	version_flag = RCLASS_IS_RUBY_CLASS;
	if ((RCLASS_VERSION(super) & RCLASS_IS_OBJECT_SUBCLASS) == RCLASS_IS_OBJECT_SUBCLASS) {
	    version_flag |= RCLASS_IS_OBJECT_SUBCLASS;
	}

	RCLASS_SET_VERSION(clone, version_flag);

	rb_vm_copy_methods((Class)orig, (Class)clone);
	CFMutableDictionaryRef ivar_dict = rb_class_ivar_dict(orig);
	if (ivar_dict != NULL) {
	    CFMutableDictionaryRef cloned_ivar_dict;

	    if (classpath == 0) {
		classpath = rb_intern("__classpath__");
	    }
	    if (classid == 0) {
		classid = rb_intern("__classid__");
	    }
	    cloned_ivar_dict = CFDictionaryCreateMutableCopy(NULL, 0,
		(CFDictionaryRef)ivar_dict);
	    // Remove the classpath & classid (name) so that they are not
	    // copied over the new module / class
	    CFDictionaryRemoveValue(cloned_ivar_dict, (const void *)classpath);
	    CFDictionaryRemoveValue(cloned_ivar_dict, (const void *)classid);
	    CFMakeCollectable(cloned_ivar_dict);
	    rb_class_ivar_set_dict(clone, cloned_ivar_dict);
	}
    }

    return clone;
}
Esempio n. 7
0
File: class.c Progetto: MSch/MacRuby
VALUE
rb_make_singleton_class(VALUE super)
{
    VALUE klass = rb_objc_create_class(NULL, super);
    long v = RCLASS_VERSION(klass);
    if (super == rb_cRubyObject) {
	v ^= RCLASS_IS_OBJECT_SUBCLASS;
    }
    v |= RCLASS_IS_RUBY_CLASS;
    v |= RCLASS_IS_SINGLETON;
    RCLASS_SET_VERSION(klass, v);
    return klass;
}
Esempio n. 8
0
static VALUE
specific_eval(int argc, VALUE *argv, VALUE klass, VALUE self)
{
    VALUE retval;

    // XXX: not exception-safe
    const long old_version = RCLASS_VERSION(klass);

    if (rb_block_given_p()) {
        if (argc > 0) {
            rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)",
		    argc);
        }
	rb_vm_set_current_scope(klass, SCOPE_PUBLIC);
        retval = rb_vm_yield_under(klass, self, 1, &self);
    }
    else {
	const char *file = "(eval)";
	int line = 1;

        if (argc == 0) {
            rb_raise(rb_eArgError, "block not supplied");
        }
	if (rb_safe_level() >= 4) {
	    StringValue(argv[0]);
	}
	else {
	    SafeStringValue(argv[0]);
	}
	if (argc > 3) {
	    const char *name = rb_id2name(rb_frame_callee());
	    rb_raise(rb_eArgError,
		    "wrong number of arguments: %s(src) or %s{..}",
		    name, name);
	}
	if (argc > 2) {
	    line = NUM2INT(argv[2]);
	}
	if (argc > 1) {
	    file = StringValuePtr(argv[1]);
	}
	rb_vm_set_current_scope(klass, SCOPE_PUBLIC);
	retval = eval_under(self, klass, argv[0], Qnil, file, line);
    }

    RCLASS_SET_VERSION(klass, old_version);

    return retval;
}
Esempio n. 9
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);
}
Esempio n. 10
0
File: class.c Progetto: MSch/MacRuby
void
rb_include_module2(VALUE klass, VALUE orig_klass, VALUE module, bool check,
	bool add_methods)
{
    if (check) {
	rb_frozen_class_p(klass);
	if (!OBJ_TAINTED(klass)) {
	    rb_secure(4);
	}
	Check_Type(module, T_MODULE);
    }

    // Register the module as included in the class.
    VALUE ary = rb_attr_get(klass, idIncludedModules);
    if (ary == Qnil) {
	ary = rb_ary_new();
	rb_ivar_set(klass, idIncludedModules, ary);
    }
    else {
	if (rb_ary_includes(ary, module)) {
	    return;
	}
    }
    rb_ary_insert(ary, 0, module);

    // Mark the module as included somewhere.
    const long v = RCLASS_VERSION(module) | RCLASS_IS_INCLUDED;
    RCLASS_SET_VERSION(module, v);

    // Register the class as included in the module.
    ary = rb_attr_get(module, idIncludedInClasses);
    if (ary == Qnil) {
	ary = rb_ary_new();
	rb_ivar_set(module, idIncludedInClasses, ary);
    }
    rb_ary_push(ary, klass);

    // Delete the ancestors array if it exists, since we just changed it.
    CFMutableDictionaryRef iv_dict = rb_class_ivar_dict(klass);
    if (iv_dict != NULL) {
	CFDictionaryRemoveValue(iv_dict, (const void *)idAncestors);
    }

    if (add_methods) {
	// Copy methods. If original class has the basic -initialize and if the
	// module has a customized -initialize, we must copy the customized
	// version to the original class too.
	rb_vm_copy_methods((Class)module, (Class)klass);

	// When including into the class Class, also copy the methods to the
	// singleton class of NSObject.
	if (klass == rb_cClass || klass == rb_cModule) {
            rb_vm_copy_methods((Class)module, *(Class *)rb_cNSObject);
	}

	if (orig_klass != 0 && orig_klass != klass) {
	    Method m = class_getInstanceMethod((Class)orig_klass,
		    selInitialize);
	    Method m2 = class_getInstanceMethod((Class)klass, selInitialize);
	    if (m != NULL && m2 != NULL
		&& method_getImplementation(m) == (IMP)rb_objc_init
		&& method_getImplementation(m2) != (IMP)rb_objc_init) {
		rb_vm_copy_method((Class)orig_klass, m2);
	    }
	}
    }
}