[macruby-changes] [510] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Aug 27 23:00:47 PDT 2008


Revision: 510
          http://trac.macosforge.org/projects/ruby/changeset/510
Author:   lsansonetti at apple.com
Date:     2008-08-27 23:00:47 -0700 (Wed, 27 Aug 2008)
Log Message:
-----------
objc dispatcher now faster and supports objects implementing -forwardInvocation:, fixed a memory crasher in boxe structure ivar setters

Modified Paths:
--------------
    MacRuby/trunk/include/ruby/intern.h
    MacRuby/trunk/objc.h
    MacRuby/trunk/objc.m
    MacRuby/trunk/vm_core.h
    MacRuby/trunk/vm_insnhelper.c

Modified: MacRuby/trunk/include/ruby/intern.h
===================================================================
--- MacRuby/trunk/include/ruby/intern.h	2008-08-28 05:59:22 UTC (rev 509)
+++ MacRuby/trunk/include/ruby/intern.h	2008-08-28 06:00:47 UTC (rev 510)
@@ -703,7 +703,6 @@
 long rb_objc_remove_flags(const void *obj);
 void rb_objc_methods(VALUE, Class);
 bool rb_objc_is_immutable(VALUE);
-VALUE rb_objc_call(VALUE, SEL, int, VALUE *);
 #endif
 /* version.c */
 void ruby_show_version(void);

Modified: MacRuby/trunk/objc.h
===================================================================
--- MacRuby/trunk/objc.h	2008-08-28 05:59:22 UTC (rev 509)
+++ MacRuby/trunk/objc.h	2008-08-28 06:00:47 UTC (rev 510)
@@ -1,6 +1,21 @@
 #ifndef __OBJC_H_
 #define __OBJC_H_
 
+#include "bs.h"
+
+struct rb_objc_method_sig {
+  const char *types;
+  unsigned int argc;
+};
+
+bool rb_objc_fill_sig(VALUE recv, Class klass, SEL sel, struct rb_objc_method_sig *sig, bs_element_method_t *bs_method);
+
+VALUE rb_objc_call(VALUE recv, SEL sel, int argc, VALUE *argv);
+
+VALUE rb_objc_call2(VALUE recv, VALUE klass, SEL sel, IMP imp, 
+	struct rb_objc_method_sig *sig, bs_element_method_t *bs_method, int argc, 
+	VALUE *argv);
+
 static inline void
 rb_objc_install_method(Class klass, SEL sel, IMP imp)
 {

Modified: MacRuby/trunk/objc.m
===================================================================
--- MacRuby/trunk/objc.m	2008-08-28 05:59:22 UTC (rev 509)
+++ MacRuby/trunk/objc.m	2008-08-28 06:00:47 UTC (rev 510)
@@ -1078,15 +1078,84 @@
     return type;
 }
 
+static inline int
+SubtypeUntil(const char *type, char end)
+{
+    int level = 0;
+    const char *head = type;
+
+    while (*type)
+    {
+	if (!*type || (!level && (*type == end)))
+	    return (int)(type - head);
+
+	switch (*type)
+	{
+	    case ']': case '}': case ')': level--; break;
+	    case '[': case '{': case '(': level += 1; break;
+	}
+
+	type += 1;
+    }
+
+    rb_bug ("Object: SubtypeUntil: end of type encountered prematurely\n");
+    return 0;
+}
+
+static inline const char *
+SkipStackSize(const char *type)
+{
+    while ((*type >= '0') && (*type <= '9'))
+	type += 1;
+    return type;
+}
+
+static inline const char *
+SkipFirstType(const char *type)
+{
+    while (1)
+    {
+        switch (*type++)
+        {
+            case 'O':   /* bycopy */
+            case 'n':   /* in */
+            case 'o':   /* out */
+            case 'N':   /* inout */
+            case 'r':   /* const */
+            case 'V':   /* oneway */
+            case '^':   /* pointers */
+                break;
+
+                /* arrays */
+            case '[':
+                return type + SubtypeUntil (type, ']') + 1;
+
+                /* structures */
+            case '{':
+                return type + SubtypeUntil (type, '}') + 1;
+
+                /* unions */
+            case '(':
+                return type + SubtypeUntil (type, ')') + 1;
+
+                /* basic types */
+            default:
+                return type;
+        }
+    }
+}
+
 VALUE
-rb_objc_call2(VALUE recv, VALUE klass, SEL sel, IMP imp, Method method, bs_element_method_t *bs_method, int argc, VALUE *argv)
+rb_objc_call2(VALUE recv, VALUE klass, SEL sel, IMP imp, 
+	      struct rb_objc_method_sig *sig, bs_element_method_t *bs_method, 
+	      int argc, VALUE *argv)
 {
     unsigned i, real_count, count;
     ffi_type *ffi_rettype, **ffi_argtypes;
     void *ffi_ret, **ffi_args;
     ffi_cif *cif;
     const char *type;
-    char buf[128];
+    char *rettype, buf[100];
     id ocrcv;
 
     /* XXX very special exceptions! */
@@ -1103,7 +1172,7 @@
 
     DLOG("OCALL", "%c[<%s %p> %s]", class_isMetaClass((Class)klass) ? '+' : '-', class_getName((Class)klass), (void *)ocrcv, (char *)sel);
 
-    count = method_getNumberOfArguments(method);
+    count = sig->argc;
     assert(count >= 2);
 
     real_count = count;
@@ -1119,8 +1188,7 @@
     }
 
     if (count == 2) {
-	method_getReturnType(method, buf, sizeof buf);
-	if (buf[0] == '@' || buf[0] == '#' || buf[0] == 'v') {
+	if (sig->types[0] == '@' || sig->types[0] == '#' || sig->types[0] == 'v') {
 	    /* Easy case! */
 	    @try {
 		if (klass == RCLASS_SUPER(*(Class *)ocrcv)) {
@@ -1136,8 +1204,10 @@
 	    @catch (id e) {
 		rb_objc_exc_raise(e);
 	    }
-	    if (buf[0] == '@' || buf[0] == '#') {
+	    if (sig->types[0] == '@' || sig->types[0] == '#') {
 		VALUE retval;
+		buf[0] = sig->types[0];
+		buf[1] = '\0';
 		rb_objc_ocval_to_rbval(&ffi_ret, buf, &retval);
 		return retval;
 	    }
@@ -1156,31 +1226,43 @@
     ffi_args[0] = &ocrcv;
     ffi_args[1] = &sel;
 
+    type = SkipFirstType(sig->types);
+    rettype = alloca(type - sig->types + 1);
+    strncpy(rettype, sig->types, type - sig->types);
+    rettype[type - sig->types] = '\0';
+    ffi_rettype = rb_objc_octype_to_ffitype(rettype);
+
+    type = SkipStackSize(type);
+    type = SkipFirstType(type); /* skip receiver */
+    type = SkipStackSize(type);
+    type = SkipFirstType(type); /* skip selector */
+
     for (i = 0; i < argc; i++) {
 	ffi_type *ffi_argtype;
+	const char *type2;
 
-	type = rb_objc_method_get_type(method, real_count, bs_method,
-	    i, buf, sizeof buf);
+	type = SkipStackSize(type);
+	type2 = SkipFirstType(type);
+	strncpy(buf, type, MIN(sizeof buf, type2 - type));
+	buf[MIN(sizeof buf, type2 - type)] = '\0';
 
-	ffi_argtypes[i + 2] = rb_objc_octype_to_ffitype(type);
+	ffi_argtypes[i + 2] = rb_objc_octype_to_ffitype(buf);
 	assert(ffi_argtypes[i + 2]->size > 0);
 	ffi_argtype = ffi_argtypes[i + 2];
 	ffi_args[i + 2] = (void *)alloca(ffi_argtype->size);
-	rb_objc_rval_to_ocval(argv[i], type, ffi_args[i + 2]);
+	rb_objc_rval_to_ocval(argv[i], buf, ffi_args[i + 2]);
+	
+	type = type2;
     }
 
     ffi_argtypes[count] = NULL;
     ffi_args[count] = NULL;
 
-    type = rb_objc_method_get_type(method, real_count, bs_method, 
-	-1, buf, sizeof buf);
-    ffi_rettype = rb_objc_octype_to_ffitype(type);
-
     cif = (ffi_cif *)alloca(sizeof(ffi_cif));
     if (ffi_prep_cif(cif, FFI_DEFAULT_ABI, count, ffi_rettype, 
 		ffi_argtypes) != FFI_OK) {
 	rb_fatal("can't prepare cif for objc method type `%s'",
-		method_getTypeEncoding(method));
+		sig->types);
     }
 
     if (ffi_rettype != &ffi_type_void) {
@@ -1199,7 +1281,7 @@
 
     if (ffi_rettype != &ffi_type_void) {
 	VALUE resp;
-	rb_objc_ocval_to_rbval(ffi_ret, type, &resp);
+	rb_objc_ocval_to_rbval(ffi_ret, rettype, &resp);
 	return resp;
     }
     else {
@@ -1212,13 +1294,102 @@
 {
     VALUE klass;
     Method method;
+    struct rb_objc_method_sig sig;
 
     klass = CLASS_OF(recv);
     method = class_getInstanceMethod((Class)klass, sel);
+    assert(rb_objc_fill_sig(recv, (Class)klass, sel, &sig, NULL));
 
-    return rb_objc_call2(recv, klass, sel, method_getImplementation(method), method, NULL, argc, argv);
+    return rb_objc_call2(recv, klass, sel, method_getImplementation(method), 
+	    &sig, NULL, argc, argv);
 }
 
+static inline const char *
+rb_get_bs_method_type(bs_element_method_t *bs_method, int arg)
+{
+    if (bs_method != NULL) {
+	if (arg == -1) {
+	    if (bs_method->retval != NULL)
+		return bs_method->retval->type;
+	}
+	else {
+	    int i;
+	    for (i = 0; i < bs_method->args_count; i++) {
+		if (bs_method->args[i].index == arg)
+		    return bs_method->args[i].type;
+	    }
+	}
+    }
+    return NULL;
+}
+
+bool
+rb_objc_fill_sig(VALUE recv, Class klass, SEL sel, struct rb_objc_method_sig *sig, bs_element_method_t *bs_method)
+{
+    Method method;
+    const char *type;
+    char buf[100];
+    unsigned i;
+
+    method = class_getInstanceMethod(klass, sel);
+    if (method != NULL) {
+	if (bs_method == NULL) {
+	    sig->types = method_getTypeEncoding(method);
+	    sig->argc = method_getNumberOfArguments(method);
+	}
+	else {
+	    char buf2[100];
+	    type = rb_get_bs_method_type(bs_method, -1);
+	    if (type != NULL) {
+		strlcpy(buf, type, sizeof buf);
+	    }
+	    else {
+		method_getReturnType(method, buf2, sizeof buf2);
+		strlcpy(buf, buf2, sizeof buf);
+	    }
+
+	    sig->argc = method_getNumberOfArguments(method);
+	    for (i = 0; i < sig->argc; i++) {
+		if (i >= 2 && (type = rb_get_bs_method_type(bs_method, i - 2)) != NULL) {
+		    strlcat(buf, type, sizeof buf);
+		}
+		else {
+		    method_getArgumentType(method, i, buf2, sizeof(buf2));
+		    strlcat(buf, buf2, sizeof buf);
+		}
+	    }
+
+	    sig->types = (char *)sel_registerName(buf); /* unify the string */
+	}
+	return true;
+    }
+    else if (!SPECIAL_CONST_P(recv)) {
+	NSMethodSignature *msig = [(id)recv methodSignatureForSelector:sel];
+	if (msig != NULL) {
+	    char buf[100];
+	    unsigned i;
+
+	    buf[0] = '\0';
+	    type = rb_get_bs_method_type(bs_method, -1);
+	    if (type == NULL)
+		type = [msig methodReturnType];
+	    strlcat(buf, type, sizeof buf);
+
+	    sig->argc = [msig numberOfArguments];
+	    for (i = 0; i < sig->argc; i++) {
+		if (i < 2 || (type = rb_get_bs_method_type(bs_method, i - 2)) == NULL) {
+		    type = [msig getArgumentTypeAtIndex:i];
+		}
+		strlcat(buf, type, sizeof buf);
+	    }
+
+	    sig->types = (char *)sel_registerName(buf); /* unify the string */
+	    return true;
+	}
+    }
+    return false;
+}
+
 void
 rb_objc_alias(VALUE klass, ID name, ID def)
 {
@@ -1896,12 +2067,14 @@
 	    (bs_element_struct_field_t *)&bs_struct->fields[i];
 
 	if (strcmp(ivar_id_str, bs_field->name) == 0) {
-	    VALUE *val;
+	    VALUE val, *pval;
 
-	    val = &((VALUE *)(data + bs_boxed->ffi_type->size))[i];
-	    if (*val == 0)
-		rb_objc_ocval_to_rbval(data + pos, bs_field->type, val);
-	    return *val;
+	    pval = &((VALUE *)(data + bs_boxed->ffi_type->size))[i];
+	    if (*pval == 0) {
+		pval = &val;
+		rb_objc_ocval_to_rbval(data + pos, bs_field->type, pval);
+	    }
+	    return *pval;
 	}
         pos += rb_objc_octype_to_ffitype(bs_field->type)->size;
     }
@@ -2568,22 +2741,6 @@
     }
 }
 
-static inline void
-rb_objc_install_method(Class klass, SEL sel, IMP imp)
-{
-    Method method = class_getInstanceMethod(klass, sel);
-    assert(method != NULL);
-    assert(class_addMethod(klass, sel, imp, method_getTypeEncoding(method)));
-}
-
-static inline void
-rb_objc_override_method(Class klass, SEL sel, IMP imp)
-{
-    Method method = class_getInstanceMethod(klass, sel);
-    assert(method != NULL);
-    method_setImplementation(method, imp);
-}
-
 static void
 rb_install_boxed_primitives(void)
 {

Modified: MacRuby/trunk/vm_core.h
===================================================================
--- MacRuby/trunk/vm_core.h	2008-08-28 05:59:22 UTC (rev 509)
+++ MacRuby/trunk/vm_core.h	2008-08-28 06:00:47 UTC (rev 510)
@@ -546,6 +546,7 @@
 
 #if WITH_OBJC
 # include "bs.h"
+# include "objc.h"
 struct rb_method_cache {
     unsigned int flags;
 #define RB_MCACHE_RCALL_FLAG		0x10
@@ -562,7 +563,7 @@
 	    SEL sel;
 	    VALUE klass;
 	    IMP imp;
-	    Method method;
+	    struct rb_objc_method_sig sig;
 	    bs_element_method_t *bs_method;
 	} ocall;
 	struct {

Modified: MacRuby/trunk/vm_insnhelper.c
===================================================================
--- MacRuby/trunk/vm_insnhelper.c	2008-08-28 05:59:22 UTC (rev 509)
+++ MacRuby/trunk/vm_insnhelper.c	2008-08-28 06:00:47 UTC (rev 510)
@@ -511,7 +511,6 @@
 }
 
 bs_element_method_t * rb_bs_find_method(Class klass, SEL sel);
-VALUE rb_objc_call2(VALUE recv, VALUE klass, SEL sel, IMP imp, Method method, bs_element_method_t *bs_method, int argc, VALUE *argv);
 
 static inline VALUE
 vm_call_method(rb_thread_t * const th, rb_control_frame_t * const cfp,
@@ -547,8 +546,12 @@
 			mcache->flags = RB_MCACHE_OCALL_FLAG;
 			mcache->as.ocall.klass = klass;
 			mcache->as.ocall.imp = imp;
-			mcache->as.ocall.method = class_getInstanceMethod((Class)klass, mcache->as.rcall.sel);
-			mcache->as.ocall.bs_method = rb_bs_find_method((Class)klass, mcache->as.rcall.sel);
+			mcache->as.ocall.bs_method = rb_bs_find_method(
+				(Class)klass, mcache->as.rcall.sel);
+			assert(rb_objc_fill_sig(recv, (Class)klass, 
+				    mcache->as.rcall.sel, 
+				    &mcache->as.ocall.sig,
+				    mcache->as.ocall.bs_method));
 			goto ocall_dispatch;
 		    }
 
@@ -632,7 +635,7 @@
 	    reg_cfp->sp -= num + 1;
 
 
-	    val = rb_objc_call2(recv, klass, mcache->as.rcall.sel, mcache->as.ocall.imp, mcache->as.ocall.method, mcache->as.ocall.bs_method, num, reg_cfp->sp + 1);
+	    val = rb_objc_call2(recv, klass, mcache->as.rcall.sel, mcache->as.ocall.imp, &mcache->as.ocall.sig, mcache->as.ocall.bs_method, num, reg_cfp->sp + 1);
 
 	    if (reg_cfp != th->cfp + 1)
 		rb_bug("cfp consistency error - send");
@@ -680,8 +683,11 @@
 	    mcache->as.ocall.sel = sel;
 	    mcache->as.ocall.klass = klass;
 	    mcache->as.ocall.imp = imp;
-	    mcache->as.ocall.method = class_getInstanceMethod((Class)klass, mcache->as.rcall.sel);
-	    mcache->as.ocall.bs_method = rb_bs_find_method((Class)klass, mcache->as.rcall.sel);
+	    mcache->as.ocall.bs_method = rb_bs_find_method((Class)klass, 
+		    mcache->as.rcall.sel);
+	    assert(rb_objc_fill_sig(recv, (Class)klass, mcache->as.rcall.sel, 
+			&mcache->as.ocall.sig,
+			mcache->as.ocall.bs_method));
 	    goto ocall_dispatch;
 	}
     }
@@ -809,38 +815,54 @@
 	    }
 	}
 	else if (mcache != NULL) {
-	    const char *p = (const char *)mcache->as.rcall.sel;
-	    size_t len = strlen(p);
-	    if (len >= 3) {
-		char buf[100];
-		SEL sel = 0;
-		if (isalpha(p[len - 3]) && p[len - 2] == '=' && p[len - 1] == ':') {
-		    /* foo=: -> setFoo: shortcut */
-		    snprintf(buf, sizeof buf, "set%s", p);
-		    buf[3] = toupper(buf[3]);
-		    buf[len + 1] = ':';
-		    buf[len + 2] = '\0';
-		    sel = sel_registerName(buf);
-		}
-		else if (isalpha(p[len - 2]) && p[len - 1] == '?') {
-		    /* foo?: -> isFoo: shortcut */
-		    snprintf(buf, sizeof buf, "is%s", p);
-		    buf[2] = toupper(buf[2]);
-		    buf[len + 1] = '\0';
-		    sel = sel_registerName(buf);
-		}
-		if (sel != 0) {
-		    Method method = class_getInstanceMethod((Class)klass, sel);
-		    if (method != NULL) {
-			IMP imp = method_getImplementation(method);
-			if (rb_objc_method_node3(imp) == NULL) {
-			    assert(class_addMethod((Class)klass, mcache->as.rcall.sel, imp,
+	    struct rb_objc_method_sig sig;
+	    if (rb_objc_fill_sig(recv, (Class)klass, mcache->as.rcall.sel, &sig, NULL)) {
+		/* the class probably implements forwardInvocation: */
+		mcache->flags = RB_MCACHE_OCALL_FLAG;
+		mcache->as.ocall.klass = klass;
+		mcache->as.ocall.imp = (IMP)objc_msgSend;
+		mcache->as.ocall.sig = sig;
+		mcache->as.ocall.bs_method = NULL;
+		goto ocall_dispatch;
+	    }
+	    else {
+		const char *p = (const char *)mcache->as.rcall.sel;
+		size_t len = strlen(p);
+		if (len >= 3) {
+		    char buf[100];
+		    SEL sel = 0;
+		    if (isalpha(p[len - 3]) && p[len - 2] == '=' && p[len - 1] == ':') {
+			/* foo=: -> setFoo: shortcut */
+			snprintf(buf, sizeof buf, "set%s", p);
+			buf[3] = toupper(buf[3]);
+			buf[len + 1] = ':';
+			buf[len + 2] = '\0';
+			sel = sel_registerName(buf);
+		    }
+		    else if (isalpha(p[len - 2]) && p[len - 1] == '?') {
+			/* foo?: -> isFoo: shortcut */
+			snprintf(buf, sizeof buf, "is%s", p);
+			buf[2] = toupper(buf[2]);
+			buf[len + 1] = '\0';
+			sel = sel_registerName(buf);
+		    }
+		    if (sel != 0) {
+			Method method = class_getInstanceMethod((Class)klass, sel);
+			IMP imp;
+			if (method != NULL
+			    && (imp = method_getImplementation(method)) != NULL
+			    && rb_objc_method_node3(imp) == NULL) {
+			    assert(class_addMethod((Class)klass, 
+					mcache->as.rcall.sel, imp,
 					method_getTypeEncoding(method)));
 			    mcache->flags = RB_MCACHE_OCALL_FLAG;
 			    mcache->as.ocall.klass = klass;
 			    mcache->as.ocall.imp = imp;
-			    mcache->as.ocall.method = class_getInstanceMethod((Class)klass, mcache->as.rcall.sel);
-			    mcache->as.ocall.bs_method = rb_bs_find_method((Class)klass, mcache->as.rcall.sel);
+			    mcache->as.ocall.sig.argc = method_getNumberOfArguments(method);
+			    mcache->as.ocall.sig.types = method_getTypeEncoding(method);
+			    mcache->as.ocall.bs_method = 
+				rb_bs_find_method((Class)klass, 
+					mcache->as.rcall.sel);
 			    goto ocall_dispatch;
 			}
 		    }
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/macruby-changes/attachments/20080827/2a9018bf/attachment-0001.html 


More information about the macruby-changes mailing list