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 <Foundation/NSBundle.h> 11#import <Foundation/NSAutoreleasePool.h> 12#import <Foundation/NSDictionary.h> 13#import "osx_ruby.h" 14#import "mdl_osxobjc.h" 15#import "ocdata_conv.h" 16#import "internal_macros.h" 17#import "objc_compat.h" 18 19/** module OSX::BundleSupport **/ 20static VALUE _mBundleSupport = Qnil; 21static NSMutableDictionary* gBundleMap; 22static const char* BUNDLE_MAP_NAME = "BUNDLE_MAP"; 23static const char* BUNDLE_STACK_NAME = "BUNDLE_STACK"; 24#define BUNDLE_STACK rb_const_get(_mBundleSupport, rb_intern(BUNDLE_STACK_NAME)) 25 26 27/** bundle_stack - stack for the current bundle and related 28 * parameter 29 * 30 * _push_bundle([ bundle, additional_param ]) 31 * _pop_bundle => [ bundle, additional_param ] or nil 32 * _current_bundle => [ bundle, additional_param ] or nil 33 * 34 * *NOTE* ocid_to_rbobj called at doing to pop/fetch a stack item, 35 * rather than at the push time, because it's not work at that time 36 * around the ns_import method. 37 **/ 38 39/* this function should be called from inside a NSAutoreleasePool */ 40static NSBundle* bundle_for(Class klass) 41{ 42 return (klass == nil) ? 43 [NSBundle mainBundle] : 44 [NSBundle bundleForClass: klass]; 45} 46 47static VALUE _make_stack_item(Class objc_class, id additional_param) 48{ 49 VALUE args = Qnil; 50 51 POOL_DO(pool) { 52 id bundle; 53 VALUE a0, a1; 54 55 bundle = bundle_for(objc_class); 56 a0 = OCID2NUM(bundle); 57 a1 = OCID2NUM(additional_param); 58 args = rb_ary_new3(2, a0, a1); 59 } END_POOL(pool); 60 return args; 61} 62 63static void _push_bundle(VALUE args) { (void) rb_ary_push(BUNDLE_STACK, args); } 64static void _pop_bundle() { (void) rb_ary_pop(BUNDLE_STACK); } 65 66static VALUE _current_bundle() 67{ 68 VALUE item; 69 id bundle_id, param_id; 70 VALUE bundle, param; 71 72 item = rb_funcall(BUNDLE_STACK, rb_intern("last"), 0); 73 if (! NIL_P(item)) { 74 bundle_id = NUM2OCID(rb_ary_entry(item, 0)); 75 param_id = NUM2OCID(rb_ary_entry(item, 1)); 76 bundle = ocid_to_rbobj(Qnil, bundle_id); 77 param = ocid_to_rbobj(Qnil, param_id); 78 return rb_ary_new3(2, bundle, param); 79 } 80 return Qnil; 81} 82 83static VALUE rb_current_bundle(VALUE mdl) { return _current_bundle(); } 84 85 86/** bundle_map - the mapping table of class to bundle **/ 87 88static id _ruby2ocid(VALUE obj) 89{ 90#if 1 91 id ocid; 92 return (rbobj_to_nsobj(obj, &ocid) == YES) ? ocid : nil; 93#else 94 return rbobj_get_ocid(obj); 95#endif 96} 97 98static id 99bundle_for_class(Class klass) 100{ 101 return [gBundleMap objectForKey:klass]; 102} 103 104static VALUE 105rb_bundle_for_class(VALUE mdl, VALUE objc_class) 106{ 107 return ocid_get_rbobj([gBundleMap objectForKey:_ruby2ocid(objc_class)]); 108} 109 110static VALUE 111rb_bind_class_with_current_bundle(VALUE mdl, VALUE objc_class) 112{ 113 VALUE stack_item; 114 stack_item = _current_bundle(); 115 if (! NIL_P(stack_item)) { 116 VALUE bundle = rb_ary_entry(stack_item, 0); 117 if (!gBundleMap) gBundleMap = [NSMutableDictionary new]; 118 [gBundleMap setObject:[NSNumber numberWithUnsignedLongLong:bundle] forKey:_ruby2ocid(objc_class)]; 119 return bundle; 120 } 121 return Qnil; 122} 123 124static VALUE my_load_clause(VALUE prog_name) 125{ 126 rb_require(StringValuePtr(prog_name)); 127 return Qnil; 128} 129 130static VALUE my_eval_clause(VALUE prog_source) 131{ 132 rb_eval_string(StringValuePtr(prog_source)); 133 return Qnil; 134} 135 136static VALUE my_rescue_clause(VALUE prog_name) 137{ 138 return ruby_errinfo; 139} 140 141/** 142 def load_ruby_program_for_class(path, objc_klass, additional_param) 143 _push_bundle(bundle for objc_class, additional_param) 144 require(prog_name) 145 nil 146 rescue Exception => err 147 err 148 ensure 149 _pop_bundle 150 end 151**/ 152 153VALUE 154load_ruby_program_for_class(const char* path, Class objc_class, id additional_param) 155{ 156 VALUE prog_name, stack_item, result; 157 158 prog_name = rb_str_new2(path); 159 stack_item = _make_stack_item(objc_class, additional_param); 160 _push_bundle(stack_item); 161 result = rb_rescue2( my_load_clause, prog_name, 162 my_rescue_clause, prog_name, rb_eException, (VALUE)0); 163 _pop_bundle(); 164 return result; 165} 166 167VALUE 168eval_ruby_program_for_class(const char* program, Class objc_class, id additional_param) 169{ 170 VALUE prog_source, stack_item, result; 171 172 prog_source = rb_str_new2(program); 173 stack_item = _make_stack_item(objc_class, additional_param); 174 _push_bundle(stack_item); 175 result = rb_rescue2( my_eval_clause, prog_source, 176 my_rescue_clause, Qnil, rb_eException, (VALUE)0); 177 _pop_bundle(); 178 return result; 179} 180 181 182/* replace NSBundle.bundleForClass */ 183static IMP original_bundleForClass = NULL; 184 185static id rubycocoa_bundleForClass(id rcv, SEL op, id klass) 186{ 187 id bundle = bundle_for_class(klass); 188 if (! bundle) 189 bundle = original_bundleForClass(rcv, op, klass); 190 return bundle; 191} 192 193static void setup_bundleForClass() 194{ 195 if (original_bundleForClass == NULL) { 196 Method method; 197 method = class_getClassMethod([NSBundle class], @selector(bundleForClass:)); 198 if (method) { 199 original_bundleForClass = method_getImplementation(method); 200 method_setImplementation(method, (IMP)rubycocoa_bundleForClass); 201 } 202 } 203} 204 205/** initialize primitive functions for module OSX::BundleSupport **/ 206void 207initialize_mdl_bundle_support() 208{ 209 if (NIL_P(_mBundleSupport)) { 210 _mBundleSupport = rb_define_module_under(osx_s_module(), "BundleSupport"); 211 212 rb_define_const(_mBundleSupport, BUNDLE_MAP_NAME, ocid_get_rbobj(gBundleMap)); 213 rb_define_const(_mBundleSupport, BUNDLE_STACK_NAME, rb_ary_new()); 214 215 rb_define_module_function(_mBundleSupport, 216 "bundle_for_class", 217 rb_bundle_for_class, 1); 218 219 rb_define_module_function(_mBundleSupport, 220 "bind_class_with_current_bundle", 221 rb_bind_class_with_current_bundle, 1); 222 223 rb_define_module_function(_mBundleSupport, 224 "_current_bundle", 225 rb_current_bundle, 0); 226 setup_bundleForClass(); 227 } 228} 229