Skip to content

Commit

Permalink
implemented informal protocol method definition + some refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
lrz committed May 28, 2009
1 parent ff3a423 commit 65c5fb4
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 88 deletions.
15 changes: 5 additions & 10 deletions bridgesupport.cpp
Expand Up @@ -827,20 +827,15 @@ bs_parse_cb(bs_parser_t *parser, const char *path, bs_element_type_t type,

case BS_ELEMENT_INFORMAL_PROTOCOL_METHOD:
{
#if 0
bs_element_informal_protocol_method_t *bs_inf_prot_method =
(bs_element_informal_protocol_method_t *)value;
struct st_table *t = bs_inf_prot_method->class_method
? bs_inf_prot_cmethods
: bs_inf_prot_imethods;

st_insert(t, (st_data_t)bs_inf_prot_method->name,
(st_data_t)bs_inf_prot_method->type);
std::map<SEL, std::string> &map =
bs_inf_prot_method->class_method
? GET_VM()->bs_informal_protocol_cmethods
: GET_VM()->bs_informal_protocol_imethods;

free(bs_inf_prot_method->protocol_name);
free(bs_inf_prot_method);
do_not_free = true;
#endif
map[bs_inf_prot_method->name] = bs_inf_prot_method->type;
break;
}

Expand Down
28 changes: 19 additions & 9 deletions objc.h
@@ -1,3 +1,11 @@
/*
* MacRuby ObjC helpers.
*
* This file is covered by the Ruby license. See COPYING for more details.
*
* Copyright (C) 2007-2009, Apple Inc. All rights reserved.
*/

#ifndef __OBJC_H_
#define __OBJC_H_

Expand All @@ -12,16 +20,14 @@ struct rb_objc_method_sig {
unsigned int argc;
};

bs_element_method_t * rb_bs_find_method(Class klass, SEL sel);

bool rb_objc_get_types(VALUE recv, Class klass, SEL sel,
bool rb_objc_get_types(VALUE recv, Class klass, SEL sel, Method m,
bs_element_method_t *bs_method, char *buf, size_t buflen);

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);
struct rb_objc_method_sig *sig, bs_element_method_t *bs_method,
int argc, VALUE *argv);

void rb_objc_define_kvo_setter(VALUE klass, ID mid);
void rb_objc_change_ruby_method_signature(VALUE mod, ID mid, VALUE sig);
Expand All @@ -33,14 +39,16 @@ rb_objc_install_method(Class klass, SEL sel, IMP imp)

method = class_getInstanceMethod(klass, sel);
if (method == NULL) {
printf("method %s not found on class %p - aborting\n", sel_getName(sel), klass);
printf("method %s not found on class %p - aborting\n",
sel_getName(sel), klass);
abort();
}
assert(method != NULL);

method2 = class_getInstanceMethod((Class)RCLASS_SUPER(klass), sel);
if (method == method2) {
assert(class_addMethod(klass, sel, imp, method_getTypeEncoding(method)));
assert(class_addMethod(klass, sel, imp,
method_getTypeEncoding(method)));
}
else {
method_setImplementation(method, imp);
Expand Down Expand Up @@ -73,10 +81,12 @@ static inline bool
rb_objc_is_placeholder(id obj)
{
void *klass = *(void **)obj;
return klass == placeholder_String || klass == placeholder_Dictionary || klass == placeholder_Array;
return klass == placeholder_String || klass == placeholder_Dictionary
|| klass == placeholder_Array;
}

bool rb_objc_symbolize_address(void *addr, void **start, char *name, size_t name_len);
bool rb_objc_symbolize_address(void *addr, void **start, char *name,
size_t name_len);

static inline int
SubtypeUntil(const char *type, char end)
Expand Down
34 changes: 6 additions & 28 deletions objc.m
@@ -1,29 +1,9 @@
/*
* Copyright (c) 2008, Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
/*
* MacRuby ObjC helpers.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* This file is covered by the Ruby license. See COPYING for more details.
*
* Copyright (C) 2007-2009, Apple Inc. All rights reserved.
*/

#include <Foundation/Foundation.h>
Expand Down Expand Up @@ -67,14 +47,12 @@
}

bool
rb_objc_get_types(VALUE recv, Class klass, SEL sel,
rb_objc_get_types(VALUE recv, Class klass, SEL sel, Method method,
bs_element_method_t *bs_method, char *buf, size_t buflen)
{
Method method;
const char *type;
unsigned i;

method = class_getInstanceMethod(klass, sel);
if (method != NULL) {
if (bs_method == NULL) {
type = method_getTypeEncoding(method);
Expand Down
4 changes: 4 additions & 0 deletions spec/macruby/fixtures/method.bridgesupport
Expand Up @@ -21,4 +21,8 @@
<arg index='0' type='B'/>
</method>
</class>
<informal_protocol name='Foo'>
<method type="i@:i" selector="informalProtocolMethod1:"/>
<method type="B@:ii" selector="informalProtocolMethod2:withValue:"/>
</informal_protocol>
</signatures>
17 changes: 17 additions & 0 deletions spec/macruby/fixtures/method.m
@@ -1,5 +1,12 @@
#import <Foundation/Foundation.h>

@interface NSObject (TestInformalProtocolMethod)

- (int)informalProtocolMethod1:(int)arg;
- (BOOL)informalProtocolMethod2:(int)arg1 withValue:(int)arg2;

@end

@interface TestMethod : NSObject
{
id _foo;
Expand Down Expand Up @@ -665,6 +672,16 @@ - (BOOL)methodAcceptingNSRectPtr2:(NSRect *)ptr
return ptr == NULL;
}

+ (BOOL)testInformalProtocolMethod1:(id)o
{
return [o informalProtocolMethod1:41] == 42;
}

+ (BOOL)testInformalProtocolMethod2:(id)o
{
return [o informalProtocolMethod2:40 withValue:2];
}

@end

void
Expand Down
9 changes: 9 additions & 0 deletions spec/macruby/fixtures/method.rb
Expand Up @@ -58,3 +58,12 @@ def methodAcceptingNSRange(a); super; end
def methodAcceptingInt(a, float:a2, double:a3, short:a4, NSPoint:a5,
NSRect:a6, char:a7); super; end
end

class TestInformalProtocolMethod
def informalProtocolMethod1(x)
x + 1
end
def informalProtocolMethod2(x, withValue:x2)
x + x2 == 42
end
end
11 changes: 11 additions & 0 deletions spec/macruby/language/objc_method_spec.rb
Expand Up @@ -628,3 +628,14 @@ def o2.to_str; 'foo' end
TestMethodOverride.testMethodAcceptingComplexTypes(@o).should == 1
end
end

describe "A pure MacRuby method" do
before :each do
@o = TestInformalProtocolMethod.new
end

it "whose selector matches an informal protocol is defined on the Objective-C side with the correct type encoding" do
TestMethod.testInformalProtocolMethod1(@o).should == 1
TestMethod.testInformalProtocolMethod2(@o).should == 1
end
end
90 changes: 49 additions & 41 deletions vm.cpp
Expand Up @@ -1119,34 +1119,52 @@ rb_vm_find_class_ivar_slot(VALUE klass, ID name)
return -1;
}

static void
resolve_method(Class klass, SEL sel, Function *func, NODE *node, IMP imp,
Method m)
static inline void
resolve_method_type(char *buf, const size_t buflen, Class klass, Method m,
SEL sel, const unsigned int oc_arity)
{
const int oc_arity = rb_vm_node_arity(node).real + 3;

char types[100];
bs_element_method_t *bs_method = GET_VM()->find_bs_method(klass, sel);

if (m == NULL || !rb_objc_get_types(Qnil, klass, sel, bs_method, types,
sizeof types)) {
assert((unsigned int)oc_arity < sizeof(types));
types[0] = '@';
types[1] = '@';
types[2] = ':';
for (int i = 3; i < oc_arity; i++) {
types[i] = '@';
if (m == NULL
|| !rb_objc_get_types(Qnil, klass, sel, m, bs_method, buf, buflen)) {

std::map<SEL, std::string> &map = class_isMetaClass(klass)
? GET_VM()->bs_informal_protocol_cmethods
: GET_VM()->bs_informal_protocol_imethods;

std::map<SEL, std::string>::iterator iter = map.find(sel);
if (iter != map.end()) {
strncpy(buf, iter->second.c_str(), sizeof buf);
}
else {
assert(oc_arity < buflen);
buf[0] = '@';
buf[1] = '@';
buf[2] = ':';
for (unsigned int i = 3; i < oc_arity; i++) {
buf[i] = '@';
}
buf[oc_arity] = '\0';
}
types[oc_arity] = '\0';
}
else {
const int m_argc = method_getNumberOfArguments(m);
const unsigned int m_argc = method_getNumberOfArguments(m);
if (m_argc < oc_arity) {
for (int i = m_argc; i < oc_arity; i++) {
strcat(types, "@");
for (unsigned int i = m_argc; i < oc_arity; i++) {
strcat(buf, "@");
}
}
}
}

static void
resolve_method(Class klass, SEL sel, Function *func, NODE *node, IMP imp,
Method m)
{
const int oc_arity = rb_vm_node_arity(node).real + 3;

char types[100];
resolve_method_type(types, sizeof types, klass, m, sel, oc_arity);

std::map<Function *, IMP>::iterator iter =
GET_VM()->objc_to_ruby_stubs.find(func);
Expand Down Expand Up @@ -1607,22 +1625,10 @@ __rb_vm_define_method(Class klass, SEL sel, IMP imp,
IMP ruby_imp = node == NULL ? imp : node->ruby_imp;

define_method:
char *types;
Method method = class_getInstanceMethod(klass, sel);
if (method != NULL) {
types = (char *)method_getTypeEncoding(method);
}
else {
// TODO look at informal protocols list
types = (char *)alloca(oc_arity + 4);
types[0] = '@';
types[1] = '@';
types[2] = ':';
for (int i = 0; i < oc_arity; i++) {
types[3 + i] = '@';
}
types[3 + oc_arity] = '\0';
}

char types[100];
resolve_method_type(types, sizeof types, klass, method, sel, oc_arity);

GET_VM()->add_method(klass, sel, imp, ruby_imp, arity, flags, types);

Expand Down Expand Up @@ -1661,7 +1667,8 @@ rb_vm_define_method(Class klass, SEL sel, IMP imp, NODE *node, bool direct)

extern "C"
void
rb_vm_define_method2(Class klass, SEL sel, rb_vm_method_node_t *node, bool direct)
rb_vm_define_method2(Class klass, SEL sel, rb_vm_method_node_t *node,
bool direct)
{
assert(node != NULL);

Expand Down Expand Up @@ -2127,15 +2134,15 @@ fill_rcache(struct mcache *cache, Class klass, rb_vm_method_node_t *node)

static force_inline void
fill_ocache(struct mcache *cache, VALUE self, Class klass, IMP imp, SEL sel,
int argc)
Method method, int argc)
{
cache->flag = MCACHE_OCALL;
ocache.klass = klass;
ocache.imp = imp;
ocache.bs_method = GET_VM()->find_bs_method(klass, sel);

char types[200];
if (!rb_objc_get_types(self, klass, sel, ocache.bs_method,
if (!rb_objc_get_types(self, klass, sel, method, ocache.bs_method,
types, sizeof types)) {
printf("cannot get encoding types for %c[%s %s]\n",
class_isMetaClass(klass) ? '+' : '-',
Expand Down Expand Up @@ -2184,7 +2191,7 @@ __rb_vm_dispatch(struct mcache *cache, VALUE self, Class klass, SEL sel,
}
else {
// objc call
fill_ocache(cache, self, klass, imp, sel, argc);
fill_ocache(cache, self, klass, imp, sel, method, argc);
}
}
else {
Expand Down Expand Up @@ -2941,11 +2948,12 @@ rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope)
oklass = k;
}

Method method = class_getInstanceMethod((Class)klass, sel);
assert(method != NULL);

int arity;
if (node == NULL) {
Method m = class_getInstanceMethod((Class)klass, sel);
assert(m != NULL);
arity = method_getNumberOfArguments(m) - 2;
arity = method_getNumberOfArguments(method) - 2;
}
else {
arity = node->arity.min;
Expand All @@ -2967,7 +2975,7 @@ rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope)
// point to the method it was created from.
struct mcache *c = (struct mcache *)xmalloc(sizeof(struct mcache));
if (node == NULL) {
fill_ocache(c, obj, oklass, imp, sel, arity);
fill_ocache(c, obj, oklass, imp, sel, method, arity);
}
else {
rb_vm_method_node_t *node = GET_VM()->method_node_get(imp);
Expand Down
2 changes: 2 additions & 0 deletions vm.h
Expand Up @@ -519,6 +519,8 @@ class RoxorVM {
std::map<std::string, std::map<SEL, bs_element_method_t *> *>
bs_classes_class_methods, bs_classes_instance_methods;
std::map<std::string, bs_element_cftype_t *> bs_cftypes;
std::map<SEL, std::string> bs_informal_protocol_imethods,
bs_informal_protocol_cmethods;

bs_element_method_t *find_bs_method(Class klass, SEL sel);
rb_vm_bs_boxed_t *find_bs_boxed(std::string type);
Expand Down

0 comments on commit 65c5fb4

Please sign in to comment.