Revision: 510 http://trac.macosforge.org/projects/ruby/changeset/510 Author: lsansonetti@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; } }
participants (1)
-
source_changes@macosforge.org