1/* 2 * Copyright (c) 2006-2008, The RubyCocoa Project. 3 * Copyright (c) 2001-2006, FUJIMOTO Hisakuni. 4 * All Rights Reserved. 5 * 6 * RubyCocoa is free software, covered under either the Ruby's license or the 7 * LGPL. See the COPYRIGHT file for more information. 8 */ 9 10#import "mdl_osxobjc.h" 11#import "osx_ruby.h" 12#import <Foundation/Foundation.h> 13#import "RubyCocoa.h" 14#import "Version.h" 15#import "RBThreadSwitcher.h" 16#import "RBObject.h" 17#import "RBClassUtils.h" 18#import "ocdata_conv.h" 19#import <mach-o/dyld.h> 20#import <string.h> 21#import "BridgeSupport.h" 22#import <objc/objc-runtime.h> 23#import "cls_objcid.h" 24#import "objc_compat.h" 25#import "OverrideMixin.h" 26#import "internal_macros.h" 27 28#define OSX_MODULE_NAME "OSX" 29 30static VALUE _cOCObject = Qnil; 31ID _relaxed_syntax_ID; 32 33static VALUE init_module_OSX() 34{ 35 VALUE module; 36 RB_ID id_osx = rb_intern(OSX_MODULE_NAME); 37 38 if (rb_const_defined(rb_cObject, id_osx)) 39 module = rb_const_get(rb_cObject, id_osx); 40 else 41 module = rb_define_module(OSX_MODULE_NAME); 42 return module; 43} 44 45static VALUE init_cls_OCObject(VALUE mOSX) 46{ 47 VALUE kObjcID; 48 VALUE kOCObject; 49 VALUE mOCObjWrapper; 50 51 kObjcID = rb_const_get(mOSX, rb_intern("ObjcID")); 52 kOCObject = rb_define_class_under(mOSX, "OCObject", kObjcID); 53 mOCObjWrapper = rb_const_get(mOSX, rb_intern("OCObjWrapper")); 54 rb_include_module(kOCObject, mOCObjWrapper); 55 56 return kOCObject; 57} 58 59// def OSX.objc_proxy_class_new (kls, kls_name) 60// ex1. OSX.objc_proxy_class_new (AA::BB::AppController, "AppController") 61static VALUE 62osx_mf_objc_proxy_class_new(VALUE mdl, VALUE kls, VALUE kls_name) 63{ 64 kls_name = rb_obj_as_string(kls_name); 65 RBObjcClassNew(kls, StringValuePtr(kls_name), [RBObject class]); 66 return Qnil; 67} 68 69// def OSX.objc_derived_class_new (kls, kls_name, super_name) 70// ex1. OSX.objc_derived_class_new (AA::BB::CustomView, "CustomView", "NSView") 71static VALUE 72osx_mf_objc_derived_class_new(VALUE mdl, VALUE kls, VALUE kls_name, VALUE super_name) 73{ 74 Class super_class; 75 Class new_cls = nil; 76 77 kls_name = rb_obj_as_string(kls_name); 78 super_name = rb_obj_as_string(super_name); 79 super_class = objc_getClass(StringValuePtr(super_name)); 80 if (super_class) 81 new_cls = RBObjcDerivedClassNew(kls, StringValuePtr(kls_name), super_class); 82 83 if (new_cls) 84 return ocobj_s_new(new_cls); 85 return Qnil; 86} 87 88// def OSX.objc_class_method_add (kls, method_name) 89// ex1. OSX.objc_class_method_add (AA::BB::CustomView, "drawRect:") 90static VALUE 91osx_mf_objc_class_method_add(VALUE mdl, VALUE kls, VALUE method_name, VALUE class_method, VALUE types) 92{ 93 Class a_class; 94 SEL a_sel; 95 const char *kls_name; 96 BOOL direct_override; 97 98 method_name = rb_obj_as_string(method_name); 99 a_sel = sel_registerName(StringValuePtr(method_name)); 100 if (a_sel == NULL) 101 return Qnil; 102 kls_name = rb_class2name(kls); 103 if (strncmp(kls_name, "OSX::", 5) == 0 104 && (a_class = objc_lookUpClass(kls_name + 5)) != NULL 105 && !is_objc_derived_class(kls)) { 106 // override in the current class 107 direct_override = YES; 108 } 109 else { 110 // override in the super class 111 a_class = RBObjcClassFromRubyClass(kls); 112 direct_override = NO; 113 } 114 if (a_class != NULL) { 115 id rcv; 116 117 rcv = RTEST(class_method) ? a_class->isa : a_class; 118 if (NIL_P(types)) 119 ovmix_register_ruby_method(rcv, a_sel, direct_override); 120 else 121 [rcv addRubyMethod:a_sel withType:StringValuePtr(types)]; 122 } 123 return Qnil; 124} 125 126static VALUE 127osx_mf_ruby_thread_switcher_start(int argc, VALUE* argv, VALUE mdl) 128{ 129 volatile VALUE arg_interval, arg_wait; 130 double interval, wait; 131 132 rb_scan_args(argc, argv, "02", &arg_interval, &arg_wait); 133 134 if (arg_interval == Qnil) { 135 [RBThreadSwitcher start]; 136 } 137 else { 138 Check_Type(arg_interval, T_FLOAT); 139 interval = RFLOAT(arg_interval)->value; 140 141 if (arg_wait == Qnil) { 142 [RBThreadSwitcher start: interval]; 143 } 144 else { 145 Check_Type(arg_wait, T_FLOAT); 146 wait = RFLOAT(arg_wait)->value; 147 [RBThreadSwitcher start: interval wait: wait]; 148 } 149 } 150 return Qnil; 151} 152 153static VALUE 154osx_mf_ruby_thread_switcher_stop(VALUE mdl) 155{ 156 [RBThreadSwitcher stop]; 157 return Qnil; 158} 159 160static VALUE 161ns_autorelease_pool(VALUE mdl) 162{ 163 id pool = [[NSAutoreleasePool alloc] init]; 164 rb_yield(Qnil); 165 [pool release]; 166 return Qnil; 167} 168 169static void 170thread_switcher_start() 171{ 172 [RBThreadSwitcher start]; 173} 174 175/******************/ 176 177VALUE 178rb_osx_class_const (const char* name) 179{ 180 VALUE mOSX; 181 VALUE constant; 182 ID name_id; 183 184 if (strlen(name) == 0) 185 return Qnil; 186 187 mOSX = osx_s_module(); 188 if (NIL_P(mOSX)) 189 return Qnil; 190 191 name_id = rb_intern(name); 192 if (!rb_is_const_id(name_id)) { 193 // If the class name can't be a constant, let's use the superclass name. 194 Class klass = objc_getClass(name); 195 if (klass != NULL) { 196 Class superklass = class_getSuperclass(klass); 197 if (superklass != NULL) 198 return rb_osx_class_const(class_getName(superklass)); 199 } 200 201 return Qnil; 202 } 203 204 // Get the class constant, triggering an import if necessary. 205 // Don't import the class if we are called within NSClassFromString, just return the constant 206 // if it exists (otherwise it would cause an infinite loop). 207 if (rb_const_defined(mOSX, name_id)) { 208 constant = rb_const_get(mOSX, name_id); 209 } 210 else if (current_function == NULL || strcmp(current_function->name, "NSClassFromString") != 0) { 211 constant = rb_funcall(mOSX, rb_intern("ns_import"), 1, rb_str_new2(name)); 212 } 213 else { 214 constant = Qnil; 215 } 216 217 return constant; 218} 219 220VALUE 221ocobj_s_class (void) 222{ 223 return _cOCObject; 224} 225 226VALUE 227rb_cls_ocobj (const char* name) 228{ 229 VALUE cls = rb_osx_class_const(name); 230 if (cls == Qnil) 231 cls = _cOCObject; 232 return cls; 233} 234 235static id 236rb_obj_ocid(VALUE rcv) 237{ 238 VALUE val = rb_funcall(rcv, rb_intern("__ocid__"), 0); 239 return NUM2OCID(val); 240} 241 242static VALUE 243osx_mf_objc_symbol_to_obj(VALUE mdl, VALUE const_name, VALUE const_type) 244{ 245 rb_raise(rb_eRuntimeError, "#objc_symbol_to_obj has been obsoleted"); 246 return Qnil; 247} 248 249/***/ 250 251VALUE 252osx_s_module() 253{ 254 RB_ID rid; 255 256 rid = rb_intern("OSX"); 257 if (! rb_const_defined(rb_cObject, rid)) 258 return rb_define_module("OSX"); 259 return rb_const_get(rb_cObject, rid); 260} 261 262VALUE 263ocobj_s_new_with_class_name(id ocid, const char *cls_name) 264{ 265 // Try to determine from the metadata if a given NSCFType object cannot be promoted to a better class. 266 if (strcmp(cls_name, "NSCFType") == 0) { 267 struct bsCFType *bs_cf_type; 268 269 bs_cf_type = find_bs_cf_type_by_type_id(CFGetTypeID((CFTypeRef)ocid)); 270 if (bs_cf_type != NULL) 271 cls_name = bs_cf_type->bridged_class_name; 272 } 273 274 return objcid_new_with_ocid(rb_cls_ocobj(cls_name), ocid); 275} 276 277VALUE 278ocobj_s_new(id ocid) 279{ 280 return ocobj_s_new_with_class_name(ocid, object_getClassName(ocid)); 281} 282 283id 284rbobj_get_ocid (VALUE obj) 285{ 286 RB_ID mtd; 287 288 if (rb_obj_is_kind_of(obj, objid_s_class()) == Qtrue) 289 return OBJCID_ID(obj); 290 291 mtd = rb_intern("__ocid__"); 292 if (rb_respond_to(obj, mtd)) 293 return rb_obj_ocid(obj); 294 295#if 0 296 if (rb_respond_to(obj, rb_intern("to_nsobj"))) { 297 VALUE nso = rb_funcall(obj, rb_intern("to_nsobj"), 0); 298 return rb_obj_ocid(nso); 299 } 300#endif 301 302 return nil; 303} 304 305VALUE 306ocid_get_rbobj (id ocid) 307{ 308 VALUE result = Qnil; 309 310 @try { 311 if (!IS_UNDOPROXY(ocid) 312 && (([ocid isProxy] && [ocid isRBObject]) 313 || [ocid respondsToSelector:@selector(__rbobj__)])) 314 result = [ocid __rbobj__]; 315 } 316 @catch (id exception) {} 317 318 return result; 319} 320 321// FIXME: this is a silly hack. 322 323struct RB_METHOD { 324 VALUE klass, rklass; 325 // ... 326}; 327 328static VALUE 329osx_mf_rebind_umethod(VALUE rcv, VALUE klass, VALUE umethod) 330{ 331 struct RB_METHOD *data; 332 333 Data_Get_Struct(umethod, struct RB_METHOD, data); 334 data->rklass = klass; 335 336 return Qnil; 337} 338 339static VALUE 340osx_rbobj_to_nsobj (VALUE rcv, VALUE obj) 341{ 342 id ocid, pool; 343 VALUE val; 344 345 pool = [[NSAutoreleasePool alloc] init]; 346 if (!rbobj_to_nsobj(obj, &ocid) || ocid == nil) { 347 [pool release]; 348 return Qnil; 349 } 350 351 val = ocid_to_rbobj(Qnil, ocid); 352 [ocid retain]; 353 OBJCID_DATA_PTR(val)->retained = YES; 354 OBJCID_DATA_PTR(val)->can_be_released = YES; 355 356 [pool release]; 357 358 return val; 359} 360 361NSThread *rubycocoaThread; 362NSRunLoop *rubycocoaRunLoop; 363 364/******************/ 365 366void initialize_mdl_osxobjc() 367{ 368 char* framework_resources_path(); 369 VALUE mOSX; 370 371 mOSX = init_module_OSX(); 372 init_cls_ObjcPtr(mOSX); 373 init_cls_ObjcID(mOSX); 374 init_mdl_OCObjWrapper(mOSX); 375 _cOCObject = init_cls_OCObject(mOSX); 376 377 _relaxed_syntax_ID = rb_intern("@relaxed_syntax"); 378 rb_ivar_set(mOSX, _relaxed_syntax_ID, Qtrue); 379 380 rb_define_module_function(mOSX, "objc_proxy_class_new", 381 osx_mf_objc_proxy_class_new, 2); 382 rb_define_module_function(mOSX, "objc_derived_class_new", 383 osx_mf_objc_derived_class_new, 3); 384 rb_define_module_function(mOSX, "objc_class_method_add", 385 osx_mf_objc_class_method_add, 4); 386 387 rb_define_module_function(mOSX, "ruby_thread_switcher_start", 388 osx_mf_ruby_thread_switcher_start, -1); 389 rb_define_module_function(mOSX, "ruby_thread_switcher_stop", 390 osx_mf_ruby_thread_switcher_stop, 0); 391 392 rb_define_module_function(mOSX, "ns_autorelease_pool", 393 ns_autorelease_pool, 0); 394 395 rb_define_const(mOSX, "RUBYCOCOA_VERSION", 396 rb_obj_freeze(rb_str_new2(RUBYCOCOA_VERSION))); 397 rb_define_const(mOSX, "RUBYCOCOA_RELEASE_DATE", 398 rb_obj_freeze(rb_str_new2(RUBYCOCOA_RELEASE_DATE))); 399 rb_define_const(mOSX, "RUBYCOCOA_SVN_REVISION", 400 rb_obj_freeze(rb_str_new2(RUBYCOCOA_SVN_REVISION))); 401#if __LP64__ 402 rb_define_const(mOSX, "RUBYCOCOA_BUILD_LP64", Qtrue); 403#else 404 rb_define_const(mOSX, "RUBYCOCOA_BUILD_LP64", Qfalse); 405#endif 406 407 char *p = framework_resources_path(); 408 rb_define_const(mOSX, "RUBYCOCOA_RESOURCES_PATH", 409 rb_obj_freeze(rb_str_new2(p))); 410 free(p); 411 412 rb_define_const(mOSX, "RUBYCOCOA_SIGN_PATHS", rb_ary_new()); 413 rb_define_const(mOSX, "RUBYCOCOA_FRAMEWORK_PATHS", rb_ary_new()); 414 415 rb_define_module_function(mOSX, "objc_symbol_to_obj", osx_mf_objc_symbol_to_obj, 2); 416 417 rb_define_module_function(mOSX, "__rebind_umethod__", osx_mf_rebind_umethod, 2); 418 419 rb_define_module_function(mOSX, "rbobj_to_nsobj", osx_rbobj_to_nsobj, 1); 420 421 thread_switcher_start(); 422 423 initialize_bridge_support(mOSX); 424 425 rubycocoaThread = [NSThread currentThread]; 426 rubycocoaRunLoop = [NSRunLoop currentRunLoop]; 427} 428