[macruby-changes] [753] MacRuby/trunk/objc.m
source_changes at macosforge.org
source_changes at macosforge.org
Sun Nov 23 18:43:58 PST 2008
Revision: 753
http://trac.macosforge.org/projects/ruby/changeset/753
Author: lsansonetti at apple.com
Date: 2008-11-23 18:43:58 -0800 (Sun, 23 Nov 2008)
Log Message:
-----------
fix for #162
Modified Paths:
--------------
MacRuby/trunk/objc.m
Modified: MacRuby/trunk/objc.m
===================================================================
--- MacRuby/trunk/objc.m 2008-11-24 01:57:47 UTC (rev 752)
+++ MacRuby/trunk/objc.m 2008-11-24 02:43:58 UTC (rev 753)
@@ -1115,6 +1115,66 @@
}
}
+static inline void
+rb_method_setTypeEncoding(Method method, const char *types)
+{
+ char **types_p = ((void *)method + sizeof(SEL));
+ free(*types_p);
+ *types_p = strdup(types);
+}
+
+static void *rb_ruby_to_objc_closure(const char *octype, unsigned arity, NODE *node);
+
+static inline void
+rb_overwrite_method_signature(Class klass, SEL sel, const char *types, bool raise_if_error)
+{
+ Method method;
+ IMP imp;
+ NODE *node;
+
+ method = class_getInstanceMethod(klass, sel);
+ if (method == NULL) {
+ if (raise_if_error) {
+ rb_raise(rb_eArgError, "%c[%s %s] not found",
+ class_isMetaClass(klass) ? '+' : '-',
+ class_getName(klass),
+ sel_getName(sel));
+ }
+ return;
+ }
+
+ if (strcmp(method_getTypeEncoding(method), types) == 0) {
+ return;
+ }
+
+ imp = method_getImplementation(method);
+ node = rb_objc_method_node3(imp);
+ if (node == NULL) {
+ if (raise_if_error) {
+ rb_raise(rb_eArgError, "%c[%s %s] is a pure Objective-C method",
+ class_isMetaClass(klass) ? '+' : '-',
+ class_getName(klass),
+ sel_getName(sel));
+ }
+ else {
+ return;
+ }
+ }
+
+ DLOG("OCALL", "overwrite %c[%s %s] type encoding to %s",
+ class_isMetaClass(klass) ? '+' : '-',
+ class_getName(klass),
+ sel_getName(sel),
+ types);
+
+ /* re-generate the FFI closure with the right types */
+ imp = rb_ruby_to_objc_closure(types, method_getNumberOfArguments(method) - 2, node);
+ method_setImplementation(method, imp);
+
+ /* change the method signature */
+ rb_method_setTypeEncoding(method, types);
+}
+
VALUE
rb_objc_call2(VALUE recv, VALUE klass, SEL sel, IMP imp,
struct rb_objc_method_sig *sig, bs_element_method_t *bs_method,
@@ -1245,16 +1305,24 @@
type = SkipStackSize(type);
type = SkipFirstType(type); /* skip selector */
+ bs_element_arg_t *bs_args;
+
+ bs_args = bs_method == NULL ? NULL : bs_method->args;
+
for (i = 0; i < argc; i++) {
+ bs_element_arg_t *bs_arg;
ffi_type *ffi_argtype;
+ bs_arg = NULL;
+
if (*type == '\0') {
- if (bs_method->variadic) {
+ if (bs_method != NULL && bs_method->variadic) {
buf[0] = '@';
buf[1] = '\0';
}
else {
- rb_bug("incomplete method signature `%s' for argc %d", sig->types, argc);
+ rb_bug("incomplete method signature `%s' for argc %d",
+ sig->types, argc);
}
}
else {
@@ -1265,6 +1333,12 @@
strncpy(buf, type, MIN(sizeof buf, type2 - type));
buf[MIN(sizeof buf, type2 - type)] = '\0';
type = type2;
+
+ if (bs_args != NULL) {
+ while ((bs_arg = bs_args)->index < i) {
+ bs_args++;
+ }
+ }
}
ffi_argtypes[i + 2] = rb_objc_octype_to_ffitype(buf);
@@ -1272,6 +1346,35 @@
ffi_argtype = ffi_argtypes[i + 2];
ffi_args[i + 2] = (void *)alloca(ffi_argtype->size);
rb_objc_rval_to_ocval(argv[i], buf, ffi_args[i + 2]);
+
+ if (buf[0] == _C_SEL && bs_arg != NULL && bs_arg->sel_of_type != NULL) {
+ SEL arg_sel;
+ int j;
+
+ arg_sel = *(SEL *)ffi_args[i + 2];
+
+ /* XXX 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.
+ */
+
+ rb_overwrite_method_signature(*(Class *)ocrcv, arg_sel,
+ bs_arg->sel_of_type, false);
+
+ for (j = 0; j < argc; j++) {
+ if (j != i && !SPECIAL_CONST_P(argv[j])) {
+ rb_overwrite_method_signature(*(Class *)argv[j], arg_sel,
+ bs_arg->sel_of_type, false);
+ }
+ }
+ }
}
ffi_argtypes[count] = NULL;
@@ -1680,6 +1783,7 @@
/* XXX mmap() and mprotect() are 2 expensive calls, maybe we should try to
* mmap() and mprotect() a large memory page and reuse it for closures?
+ * XXX currently overwriting a closure leaks the previous one!
*/
if ((closure = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
@@ -1961,22 +2065,7 @@
{
SEL sel = sel_registerName(rb_id2name(rb_to_id(mid)));
char *types = StringValuePtr(sig);
- Class c = (Class)mod;
- Method m = class_getInstanceMethod(c, sel);
- if (m == NULL) {
- rb_raise(rb_eArgError,
- "invalid method %s for given class",
- (char *)sel);
- }
- IMP imp = method_getImplementation(m);
- if (rb_objc_method_node3(imp) == NULL) {
- rb_raise(rb_eArgError,
- "will not replace method signature for method %s because it is a pure Objective-C method",
- (char *)sel);
- }
- char **types_p = ((void *)m + sizeof(SEL));
- free(*types_p);
- *types_p = strdup(types);
+ rb_overwrite_method_signature((Class)mod, sel, types, true);
}
static inline bool
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20081123/e9d322bb/attachment-0001.html>
More information about the macruby-changes
mailing list