Revision: 1495 http://trac.macosforge.org/projects/ruby/changeset/1495 Author: lsansonetti@apple.com Date: 2009-04-27 19:23:44 -0700 (Mon, 27 Apr 2009) Log Message: ----------- implemented BridgeSupport constants, some work on Pointer, extracted the objc specs of method_spec.rb into method_objc_spec.rb, started working on pointer_spec.rb Modified Paths: -------------- MacRuby/branches/experimental/objc.m MacRuby/branches/experimental/roxor.cpp MacRuby/branches/experimental/spec/macruby/method_spec.rb Added Paths: ----------- MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb MacRuby/branches/experimental/spec/macruby/pointer_spec.rb Modified: MacRuby/branches/experimental/objc.m =================================================================== --- MacRuby/branches/experimental/objc.m 2009-04-27 02:04:56 UTC (rev 1494) +++ MacRuby/branches/experimental/objc.m 2009-04-28 02:23:44 UTC (rev 1495) @@ -75,7 +75,7 @@ static struct st_table *bs_inf_prot_imethods; static struct st_table *bs_cftypes; -VALUE rb_cPointer; +static VALUE rb_cPointer; struct RPointer { @@ -517,6 +517,7 @@ static void rb_objc_rval_to_ocval(VALUE, const char *, void **); +#if 0 static VALUE rb_pointer_new_with_type(VALUE recv, SEL sel, VALUE type) { @@ -572,6 +573,7 @@ return ret; } +#endif static bool rb_objc_rval_copy_boxed_data(VALUE rval, bs_element_boxed_t *bs_boxed, void *ocval) @@ -3264,11 +3266,13 @@ rb_install_boxed_primitives(); #endif +#if 0 rb_cPointer = rb_define_class("Pointer", rb_cObject); rb_undef_alloc_func(rb_cPointer); rb_objc_define_method(*(VALUE *)rb_cPointer, "new_with_type", rb_pointer_new_with_type, 1); rb_objc_define_method(rb_cPointer, "assign", rb_pointer_assign, 1); rb_objc_define_method(rb_cPointer, "[]", rb_pointer_aref, 1); +#endif rb_ivar_type = rb_intern("@__objc_type__"); Modified: MacRuby/branches/experimental/roxor.cpp =================================================================== --- MacRuby/branches/experimental/roxor.cpp 2009-04-27 02:04:56 UTC (rev 1494) +++ MacRuby/branches/experimental/roxor.cpp 2009-04-28 02:23:44 UTC (rev 1495) @@ -203,6 +203,8 @@ Function *compile_bs_struct_writer(rb_vm_bs_boxed_t *bs_boxed, int field); Function *compile_ffi_function(void *stub, void *imp, int argc); + Function *compile_to_rval_convertor(const char *type); + Function *compile_to_ocval_convertor(const char *type); private: const char *fname; @@ -477,6 +479,8 @@ std::map<Class, std::map<ID, int> *> ivar_slots; std::map<SEL, GlobalVariable *> redefined_ops_gvars; std::map<Class, struct rb_vm_outer *> outers; + std::map<std::string, void *> c_stubs, objc_stubs, + to_rval_convertors, to_ocval_convertors; public: static RoxorVM *current; @@ -502,8 +506,6 @@ std::map<VALUE, rb_vm_catch_t *> catch_jmp_bufs; std::vector<jmp_buf *> return_from_block_jmp_bufs; - std::map<std::string, void *> c_stubs, objc_stubs; - bs_parser_t *bs_parser; std::map<std::string, rb_vm_bs_boxed_t *> bs_boxed; std::map<std::string, bs_element_function_t *> bs_funcs; @@ -516,6 +518,16 @@ rb_vm_bs_boxed_t *find_bs_struct(std::string type); rb_vm_bs_boxed_t *find_bs_opaque(std::string type); + void *gen_stub(std::string types, int argc, bool is_objc); + void *gen_to_rval_convertor(std::string type); + void *gen_to_ocval_convertor(std::string type); + + void insert_stub(const char *types, void *stub, bool is_objc) { + std::map<std::string, void *> &m = + is_objc ? objc_stubs : c_stubs; + m.insert(std::make_pair(types, stub)); + } + #if ROXOR_ULTRA_LAZY_JIT std::map<SEL, std::map<Class, rb_vm_method_source_t *> *> method_sources; @@ -7083,26 +7095,100 @@ return rb_vm_call(obj, selMethodMissing, argc + 1, new_argv, false); } -static void * -vm_gen_stub(std::string types, int argc, bool is_objc) +inline void * +RoxorVM::gen_stub(std::string types, int argc, bool is_objc) { - std::map<std::string, void *> &stubs = - is_objc ? GET_VM()->objc_stubs : GET_VM()->c_stubs; + std::map<std::string, void *> &stubs = is_objc ? objc_stubs : c_stubs; std::map<std::string, void *>::iterator iter = stubs.find(types); if (iter != stubs.end()) { return iter->second; } - //printf("generating %s stub %s argc %d\n", is_objc ? "objc" : "c", types, argc); - Function *f = RoxorCompiler::shared->compile_stub(types.c_str(), argc, is_objc); - void *stub = (void *)GET_VM()->compile(f); + void *stub = (void *)compile(f); stubs.insert(std::make_pair(types, stub)); return stub; } +Function * +RoxorCompiler::compile_to_rval_convertor(const char *type) +{ + // VALUE foo(void *ocval); + Function *f = cast<Function>(module->getOrInsertFunction("", + Type::VoidTy, PtrTy, NULL)); + Function::arg_iterator arg = f->arg_begin(); + Value *ocval = arg++; + + bb = BasicBlock::Create("EntryBlock", f); + + const Type *llvm_type = convert_type(type); + ocval = new BitCastInst(ocval, PointerType::getUnqual(llvm_type), "", bb); + ocval = new LoadInst(ocval, "", bb); + + Value *rval = compile_conversion_to_ruby(type, llvm_type, ocval); + + ReturnInst::Create(rval, bb); + + return f; +} + +inline void * +RoxorVM::gen_to_rval_convertor(std::string type) +{ + std::map<std::string, void *>::iterator iter = + to_rval_convertors.find(type); + if (iter != to_rval_convertors.end()) { + return iter->second; + } + + Function *f = RoxorCompiler::shared->compile_to_rval_convertor( + type.c_str()); + void *convertor = (void *)compile(f); + to_rval_convertors.insert(std::make_pair(type, convertor)); + + return convertor; +} + +Function * +RoxorCompiler::compile_to_ocval_convertor(const char *type) +{ + // void foo(VALUE rval, void **ocval); + Function *f = cast<Function>(module->getOrInsertFunction("", + Type::VoidTy, RubyObjTy, PtrTy, NULL)); + Function::arg_iterator arg = f->arg_begin(); + Value *rval = arg++; + Value *ocval = arg++; + + bb = BasicBlock::Create("EntryBlock", f); + + const Type *llvm_type = convert_type(type); + ocval = new BitCastInst(ocval, PointerType::getUnqual(llvm_type), "", bb); + compile_conversion_to_c(type, rval, ocval); + + ReturnInst::Create(bb); + + return f; +} + +inline void * +RoxorVM::gen_to_ocval_convertor(std::string type) +{ + std::map<std::string, void *>::iterator iter = + to_ocval_convertors.find(type); + if (iter != to_ocval_convertors.end()) { + return iter->second; + } + + Function *f = RoxorCompiler::shared->compile_to_ocval_convertor( + type.c_str()); + void *convertor = (void *)compile(f); + to_ocval_convertors.insert(std::make_pair(type, convertor)); + + return convertor; +} + static inline void vm_gen_bs_func_types(bs_element_function_t *bs_func, std::string &types) { @@ -7196,8 +7282,8 @@ sel_getName(sel)); abort(); } - ocache.stub = (rb_vm_objc_stub_t *)vm_gen_stub(types, argc, - true); + ocache.stub = (rb_vm_objc_stub_t *)GET_VM()->gen_stub(types, + argc, true); } } else { @@ -7235,8 +7321,8 @@ fcache.bs_function = bs_func; fcache.imp = (IMP)dlsym(RTLD_DEFAULT, bs_func->name); assert(fcache.imp != NULL); - fcache.stub = (rb_vm_c_stub_t *)vm_gen_stub(types, argc, - false); + fcache.stub = (rb_vm_c_stub_t *)GET_VM()->gen_stub(types, + argc, false); } else { // Still nothing, then let's call #method_missing. @@ -8568,9 +8654,8 @@ bs_const->name); } - // TODO - //rb_objc_ocval_to_rval(sym, bs_const->type, &v); - v = INT2FIX(42); + void *convertor = GET_VM()->gen_to_rval_convertor(bs_const->type); + v = ((VALUE (*)(void *))convertor)(sym); CFMutableDictionaryRef iv_dict = rb_class_ivar_dict(rb_cObject); assert(iv_dict != NULL); @@ -8580,6 +8665,8 @@ return v; } +VALUE rb_cBoxed; + extern "C" void rb_vm_check_arity(int given, int requested) @@ -8983,8 +9070,61 @@ return bs_boxed->bs_type == BS_ELEMENT_OPAQUE ? Qtrue : Qfalse; } -VALUE rb_cBoxed; +VALUE rb_cPointer; +typedef struct { + VALUE type; + VALUE (*convert_to_rval)(void *); + void (*convert_to_ocval)(VALUE rval, void **); + void *val; +} rb_vm_pointer_t; + +static VALUE +rb_pointer_new(VALUE rcv, SEL sel, VALUE type) +{ + StringValuePtr(type); + + rb_vm_pointer_t *ptr = (rb_vm_pointer_t *)xmalloc(sizeof(rb_vm_pointer_t)); + GC_WB(&ptr->type, type); + + const char *type_str = RSTRING_PTR(type); + ptr->convert_to_rval = + (VALUE (*)(void *))GET_VM()->gen_to_rval_convertor(type_str); + ptr->convert_to_ocval = + (void (*)(VALUE, void **))GET_VM()->gen_to_ocval_convertor(type_str); + ptr->val = NULL; // TODO + + return Data_Wrap_Struct(rb_cPointer, NULL, NULL, ptr); +} + +static VALUE +rb_pointer_aref(VALUE rcv, SEL sel, VALUE idx) +{ + return Qnil; +} + +static VALUE +rb_pointer_asef(VALUE rcv, SEL sel, VALUE idx, VALUE val) +{ + return Qnil; +} + +static VALUE +rb_pointer_assign(VALUE rcv, SEL sel, VALUE val) +{ + return rb_pointer_asef(rcv, 0, FIX2INT(0), val); +} + +static VALUE +rb_pointer_type(VALUE rcv, SEL sel) +{ + rb_vm_pointer_t *ptr; + + Data_Get_Struct(rcv, rb_vm_pointer_t, ptr); + + return ptr->type; +} + extern "C" void Init_BridgeSupport(void) @@ -8996,6 +9136,20 @@ (void *)rb_boxed_is_opaque, 0); boxed_ivar_type = rb_intern("__octype__"); + rb_cPointer = rb_define_class("Pointer", rb_cObject); + rb_objc_define_method(*(VALUE *)rb_cPointer, "new", + (void *)rb_pointer_new, 1); + rb_objc_define_method(*(VALUE *)rb_cPointer, "new_with_type", + (void *)rb_pointer_new, 1); + rb_objc_define_method(rb_cPointer, "[]", + (void *)rb_pointer_aref, 1); + rb_objc_define_method(rb_cPointer, "[]=", + (void *)rb_pointer_asef, 2); + rb_objc_define_method(rb_cPointer, "assign", + (void *)rb_pointer_assign, 1); + rb_objc_define_method(rb_cPointer, "type", + (void *)rb_pointer_type, 0); + bs_const_magic_cookie = rb_str_new2("bs_const_magic_cookie"); rb_objc_retain((void *)bs_const_magic_cookie); } @@ -9475,7 +9629,8 @@ types.append(convert_ffi_type(RARRAY_AT(args, i))); } - rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)vm_gen_stub(types, argc, false); + rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_VM()->gen_stub(types, argc, + false); Function *f = RoxorCompiler::shared->compile_ffi_function((void *)stub, sym, argc); IMP imp = GET_VM()->compile(f); @@ -9507,8 +9662,8 @@ static void setup_builtin_stubs(void) { - GET_VM()->objc_stubs.insert(std::make_pair("@@:", (void *)builtin_ostub1)); - GET_VM()->objc_stubs.insert(std::make_pair("#@:", (void *)builtin_ostub1)); + GET_VM()->insert_stub("@@:", (void *)builtin_ostub1, true); + GET_VM()->insert_stub("#@:", (void *)builtin_ostub1, true); } #if ROXOR_ULTRA_LAZY_JIT Modified: MacRuby/branches/experimental/spec/macruby/method_spec.rb =================================================================== --- MacRuby/branches/experimental/spec/macruby/method_spec.rb 2009-04-27 02:04:56 UTC (rev 1494) +++ MacRuby/branches/experimental/spec/macruby/method_spec.rb 2009-04-28 02:23:44 UTC (rev 1495) @@ -82,375 +82,6 @@ def @o.isFoo; end @o.should_not have_method(:'foo?') end -end -framework 'Foundation' - -fixture_source = File.dirname(__FILE__) + '/fixtures/method.m' -fixture_ext = '/tmp/method.bundle' -if !File.exist?(fixture_ext) or File.mtime(fixture_source) > File.mtime(fixture_ext) -=begin - # #system is currently broken - unless system("/usr/bin/gcc #{fixture_source} -o #{fixture_ext} -g -framework Foundation -dynamiclib -fobjc-gc -arch i386 -arch x86_64 -arch ppc") - $stderr.puts "cannot compile fixture source file `#{fixture_source}' - aborting" - exit 1 - end -=end - `/usr/bin/gcc #{fixture_source} -o #{fixture_ext} -g -framework Foundation -dynamiclib -fobjc-gc -arch i386 -arch x86_64 -arch ppc` + # TODO add overloading specs end -require '/tmp/method' -load_bridge_support_file File.dirname(__FILE__) + '/fixtures/method.bridgesupport' - -describe "A pure Objective-C method" do - before :each do - @o = TestMethod.new - end - - it "can be called with #foo= if it matches the #setFoo pattern" do - o = [] - - # TODO Stopped here: This should be sufficient, but doesn't work. - # o.should have_method(:'setArray', true) - # o.should have_method(:'array=', true) - - o.respond_to?(:'setArray').should == true - o.respond_to?(:'array=').should == true - o.array = [1, 2, 3] - o.should == [1, 2, 3] - end - - it "can be called with #foo? if it matches the #isFoo pattern" do - o = NSBundle.mainBundle - o.respond_to?(:'isLoaded').should == true - o.respond_to?(:'loaded?').should == true - o.loaded?.should == true - end - - it "is only exposed in #methods if the second argument is true" do - o = Object.new - o.methods.include?(:'performSelector').should == false - o.methods(true).include?(:'performSelector').should == false - o.methods(false).include?(:'performSelector').should == false - o.methods(true, true).include?(:'performSelector').should == true - o.methods(false, true).include?(:'performSelector').should == true - end - - it "can be called on an immediate object" do - 123.self.should == 123 - true.self.should == true - false.self.should == false - nil.self.should == nil - end - - it "returning void returns nil in Ruby" do - @o.methodReturningVoid.should == nil - end - - it "returning nil returns nil in Ruby" do - @o.methodReturningNil.should == nil - end - - it "returning self returns the same receiver object" do - @o.methodReturningSelf.should == @o - @o.methodReturningSelf.object_id == @o.object_id - end - - it "returning kCFBooleanTrue returns true in Ruby" do - @o.methodReturningCFTrue.should == true - @o.methodReturningCFTrue.class.should == TrueClass - end - - it "returning kCFBooleanFalse returns false in Ruby" do - @o.methodReturningCFFalse.should == false - @o.methodReturningCFFalse.class.should == FalseClass - end - - it "returning kCFNull returns nil in Ruby" do - @o.methodReturningCFNull.should == nil - @o.methodReturningCFNull.class.should == NilClass - end - - it "returning YES returns true in Ruby" do - @o.methodReturningYES.should == true - end - - it "returning NO returns true in Ruby" do - @o.methodReturningNO.should == false - end - - it "returning 'char' or 'unsigned char' returns a Fixnum in Ruby" do - @o.methodReturningChar.should == 42 - @o.methodReturningChar2.should == -42 - @o.methodReturningUnsignedChar.should == 42 - end - - it "returning 'short' or 'unsigned short' returns a Fixnum in Ruby" do - @o.methodReturningShort.should == 42 - @o.methodReturningShort2.should == -42 - @o.methodReturningUnsignedShort.should == 42 - end - - it "returning 'int' or 'unsigned int' returns a Fixnum in Ruby" do - @o.methodReturningInt.should == 42 - @o.methodReturningInt2.should == -42 - @o.methodReturningUnsignedInt.should == 42 - end - - it "returning 'long' or 'unsigned long' returns a Fixnum if possible in Ruby" do - @o.methodReturningLong.should == 42 - @o.methodReturningLong2.should == -42 - @o.methodReturningUnsignedLong.should == 42 - end - - it "returning 'long' or 'unsigned long' returns a Bignum if it cannot fix in a Fixnum in Ruby" do - @o.methodReturningLong3.should == - (RUBY_ARCH == 'x86_64' ? 4611686018427387904 : 1073741824) - @o.methodReturningLong3.class.should == Bignum - @o.methodReturningLong4.should == - (RUBY_ARCH == 'x86_64' ? -4611686018427387905 : -1073741825) - @o.methodReturningLong4.class.should == Bignum - @o.methodReturningUnsignedLong2.should == - (RUBY_ARCH == 'x86_64' ? 4611686018427387904 : 1073741824) - @o.methodReturningUnsignedLong2.class.should == Bignum - end - - it "returning 'float' returns a Float in Ruby" do - @o.methodReturningFloat.should be_close(3.1415, 0.0001) - @o.methodReturningFloat.class.should == Float - end - - it "returning 'double' returns a Float in Ruby" do - @o.methodReturningDouble.should be_close(3.1415, 0.0001) - @o.methodReturningDouble.class.should == Float - end - - it "returning 'SEL' returns a Symbol or nil in Ruby" do - @o.methodReturningSEL.class.should == Symbol - @o.methodReturningSEL.should == :'foo:with:with:' - @o.methodReturningSEL2.class.should == NilClass - @o.methodReturningSEL2.should == nil - end - - it "returning 'char *' returns a String or nil in Ruby" do - @o.methodReturningCharPtr.class.should == String - @o.methodReturningCharPtr.should == 'foo' - @o.methodReturningCharPtr2.class.should == NilClass - @o.methodReturningCharPtr2.should == nil - end - - it "returning 'NSPoint' returns an NSPoint boxed object in Ruby" do - b = @o.methodReturningNSPoint - b.class.should == NSPoint - b.x.class.should == Float - b.x.should == 1.0 - b.y.class.should == Float - b.y.should == 2.0 - end - - it "returning 'NSSize' returns an NSSize boxed object in Ruby" do - b = @o.methodReturningNSSize - b.class.should == NSSize - b.width.class.should == Float - b.width.should == 3.0 - b.height.class.should == Float - b.height.should == 4.0 - end - - it "returning 'NSRect' returns an NSRect boxed object in Ruby" do - b = @o.methodReturningNSRect - b.class.should == NSRect - b.origin.class.should == NSPoint - b.origin.x.should == 1.0 - b.origin.y.should == 2.0 - b.size.class.should == NSSize - b.size.width.should == 3.0 - b.size.height.should == 4.0 - end - - it "returning 'NSRange' returns an NSRange boxed object in Ruby" do - b = @o.methodReturningNSRange - b.class.should == NSRange - b.location.class.should == Fixnum - b.location.should == 0 - b.length.class.should == Fixnum - b.length.should == 42 - end - - it "accepting the receiver as 'id' should receive the exact same object" do - @o.methodAcceptingSelf(@o).should == 1 - end - - it "accepting the receiver's class as 'id' should receive the exact same object" do - @o.methodAcceptingSelfClass(@o.class).should == 1 - end - - it "accepting 'nil' as 'id' should receive Objective-C's nil" do - @o.methodAcceptingNil(nil).should == 1 - end - - it "accepting 'true' as 'id; should receive CF's kCFBooleanTrue" do - @o.methodAcceptingTrue(true).should == 1 - end - - it "accepting 'false' as 'id' should receive CF's kCFBooleanFalse" do - @o.methodAcceptingFalse(false).should == 1 - end - - it "accepting a Fixnum as 'id' should receive a Fixnum boxed object" do - @o.methodAcceptingFixnum(42).should == 1 - end - - it "accepting nil or false as 'BOOL' should receive NO, any other object should receive YES" do - @o.methodAcceptingFalseBOOL(nil).should == 1 - @o.methodAcceptingFalseBOOL(false).should == 1 - - @o.methodAcceptingTrueBOOL(true).should == 1 - @o.methodAcceptingTrueBOOL(0).should == 1 - @o.methodAcceptingTrueBOOL(123).should == 1 - @o.methodAcceptingTrueBOOL('foo').should == 1 - @o.methodAcceptingTrueBOOL(Object.new).should == 1 - end - - it "accepting a Fixnum-compatible object as 'char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', 'long', 'unsigned long' should receive the converted data, or raise an exception" do - @o.methodAcceptingChar(42).should == 1 - @o.methodAcceptingUnsignedChar(42).should == 1 - @o.methodAcceptingShort(42).should == 1 - @o.methodAcceptingUnsignedShort(42).should == 1 - @o.methodAcceptingInt(42).should == 1 - @o.methodAcceptingUnsignedInt(42).should == 1 - @o.methodAcceptingLong(42).should == 1 - @o.methodAcceptingUnsignedLong(42).should == 1 - - @o.methodAcceptingChar(42.0).should == 1 - @o.methodAcceptingUnsignedChar(42.0).should == 1 - @o.methodAcceptingShort(42.0).should == 1 - @o.methodAcceptingUnsignedShort(42.0).should == 1 - @o.methodAcceptingInt(42.0).should == 1 - @o.methodAcceptingUnsignedInt(42.0).should == 1 - @o.methodAcceptingLong(42.0).should == 1 - @o.methodAcceptingUnsignedLong(42.0).should == 1 - - o2 = Object.new - def o2.to_i; 42; end - - @o.methodAcceptingChar(o2).should == 1 - @o.methodAcceptingUnsignedChar(o2).should == 1 - @o.methodAcceptingShort(o2).should == 1 - @o.methodAcceptingUnsignedShort(o2).should == 1 - @o.methodAcceptingInt(o2).should == 1 - @o.methodAcceptingUnsignedInt(o2).should == 1 - @o.methodAcceptingLong(o2).should == 1 - @o.methodAcceptingUnsignedLong(o2).should == 1 - - lambda { @o.methodAcceptingChar(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedChar(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingShort(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedShort(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingInt(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedInt(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingLong(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedLong(nil) }.should raise_error(TypeError) - - lambda { @o.methodAcceptingChar(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedChar(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingShort(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedShort(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingInt(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedInt(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingLong(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingUnsignedLong(Object.new) }.should raise_error(TypeError) - end - - it "accepting a one-character string as 'char' or 'unsigned char' should receive the first character" do - @o.methodAcceptingChar('*').should == 1 - @o.methodAcceptingUnsignedChar('*').should == 1 - end - - it "accepting a String, Symbol or nil as 'SEL' should receive the appropriate selector" do - @o.methodAcceptingSEL(:'foo:with:with:').should == 1 - @o.methodAcceptingSEL('foo:with:with:').should == 1 - @o.methodAcceptingSEL2(nil).should == 1 - - lambda { @o.methodAcceptingSEL(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingSEL(Object.new) }.should raise_error(TypeError) - end - - it "accepting a Float-compatible object as 'float' or 'double' should receive the appropriate data" do - @o.methodAcceptingFloat(3.1415).should == 1 - @o.methodAcceptingDouble(3.1415).should == 1 - - o2 = Object.new - def o2.to_f; 3.1415; end - - @o.methodAcceptingFloat(o2).should == 1 - @o.methodAcceptingDouble(o2).should == 1 - - lambda { @o.methodAcceptingFloat(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingDouble(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingFloat(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingDouble(Object.new) }.should raise_error(TypeError) - end - - it "accepting a String-compatible object as 'char *' should receive the appropriate data" do - @o.methodAcceptingCharPtr('foo').should == 1 - - o2 = Object.new - def o2.to_str; 'foo' end - - @o.methodAcceptingCharPtr(o2).should == 1 - - lambda { @o.methodAcceptingCharPtr(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingCharPtr([]) }.should raise_error(TypeError) - lambda { @o.methodAcceptingCharPtr(Object.new) }.should raise_error(TypeError) - - @o.methodAcceptingCharPtr2(nil).should == 1 - end - - it "accepting an NSPoint, NSSize, NSRange or NSRect object as 'NSPoint', 'NSSize', 'NSRange' or 'NSRect' should receive the C structure" do - p = @o.methodReturningNSPoint - @o.methodAcceptingNSPoint(p).should == 1 - p = @o.methodReturningNSSize - @o.methodAcceptingNSSize(p).should == 1 - p = @o.methodReturningNSRect - @o.methodAcceptingNSRect(p).should == 1 - p = @o.methodReturningNSRange - @o.methodAcceptingNSRange(p).should == 1 - - lambda { @o.methodAcceptingNSPoint(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSPoint(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSPoint(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSPoint(@o.methodReturningNSSize) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSSize(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSSize(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSSize(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSSize(@o.methodReturningNSPoint) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRect(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRect(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRect(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRect(@o.methodReturningNSPoint) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRange(nil) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRange(123) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRange(Object.new) }.should raise_error(TypeError) - lambda { @o.methodAcceptingNSRange(@o.methodReturningNSPoint) }.should raise_error(TypeError) - end - - it "accepting an Array of valid objects as a structure type should receive the C structure" do - @o.methodAcceptingNSPoint([1, 2]).should == 1 - @o.methodAcceptingNSSize([3, 4]).should == 1 - @o.methodAcceptingNSRect([[1, 2], [3, 4]]).should == 1 - @o.methodAcceptingNSRect([1, 2, 3, 4]).should == 1 - @o.methodAcceptingNSRange([0, 42]).should == 1 - - lambda { @o.methodAcceptingNSPoint([1]) }.should raise_error(ArgumentError) - lambda { @o.methodAcceptingNSPoint([1, 2, 3]) }.should raise_error(ArgumentError) - lambda { @o.methodAcceptingNSRect([1, 2, 3]) }.should raise_error(ArgumentError) - lambda { @o.methodAcceptingNSRect([1, 2, 3, 4, 5]) }.should raise_error(ArgumentError) - lambda { @o.methodAcceptingNSRect([[1, 2], [3]]) }.should raise_error(ArgumentError) - lambda { @o.methodAcceptingNSRect([[1, 2], [3, 4, 5]]) }.should raise_error(ArgumentError) - end - - it "accepting various C types should receive these types as expected" do - @o.methodAcceptingInt(42, float:42, double:42, short:42, NSPoint:[42, 42], - NSRect:[42, 42, 42, 42], char:42).should == 1 - end -end Added: MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb =================================================================== --- MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb (rev 0) +++ MacRuby/branches/experimental/spec/macruby/objc_method_spec.rb 2009-04-28 02:23:44 UTC (rev 1495) @@ -0,0 +1,372 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +framework 'Foundation' + +fixture_source = File.dirname(__FILE__) + '/fixtures/method.m' +fixture_ext = '/tmp/method.bundle' +if !File.exist?(fixture_ext) or File.mtime(fixture_source) > File.mtime(fixture_ext) +=begin + # #system is currently broken + unless system("/usr/bin/gcc #{fixture_source} -o #{fixture_ext} -g -framework Foundation -dynamiclib -fobjc-gc -arch i386 -arch x86_64 -arch ppc") + $stderr.puts "cannot compile fixture source file `#{fixture_source}' - aborting" + exit 1 + end +=end + `/usr/bin/gcc #{fixture_source} -o #{fixture_ext} -g -framework Foundation -dynamiclib -fobjc-gc -arch i386 -arch x86_64 -arch ppc` +end +require '/tmp/method' +load_bridge_support_file File.dirname(__FILE__) + '/fixtures/method.bridgesupport' + +describe "A pure Objective-C method" do + before :each do + @o = TestMethod.new + end + + it "can be called with #foo= if it matches the #setFoo pattern" do + o = [] + + # TODO Stopped here: This should be sufficient, but doesn't work. + # o.should have_method(:'setArray', true) + # o.should have_method(:'array=', true) + + o.respond_to?(:'setArray').should == true + o.respond_to?(:'array=').should == true + o.array = [1, 2, 3] + o.should == [1, 2, 3] + end + + it "can be called with #foo? if it matches the #isFoo pattern" do + o = NSBundle.mainBundle + o.respond_to?(:'isLoaded').should == true + o.respond_to?(:'loaded?').should == true + o.loaded?.should == true + end + + it "is only exposed in #methods if the second argument is true" do + o = Object.new + o.methods.include?(:'performSelector').should == false + o.methods(true).include?(:'performSelector').should == false + o.methods(false).include?(:'performSelector').should == false + o.methods(true, true).include?(:'performSelector').should == true + o.methods(false, true).include?(:'performSelector').should == true + end + + it "can be called on an immediate object" do + 123.self.should == 123 + true.self.should == true + false.self.should == false + nil.self.should == nil + end + + it "returning void returns nil in Ruby" do + @o.methodReturningVoid.should == nil + end + + it "returning nil returns nil in Ruby" do + @o.methodReturningNil.should == nil + end + + it "returning self returns the same receiver object" do + @o.methodReturningSelf.should == @o + @o.methodReturningSelf.object_id == @o.object_id + end + + it "returning kCFBooleanTrue returns true in Ruby" do + @o.methodReturningCFTrue.should == true + @o.methodReturningCFTrue.class.should == TrueClass + end + + it "returning kCFBooleanFalse returns false in Ruby" do + @o.methodReturningCFFalse.should == false + @o.methodReturningCFFalse.class.should == FalseClass + end + + it "returning kCFNull returns nil in Ruby" do + @o.methodReturningCFNull.should == nil + @o.methodReturningCFNull.class.should == NilClass + end + + it "returning YES returns true in Ruby" do + @o.methodReturningYES.should == true + end + + it "returning NO returns true in Ruby" do + @o.methodReturningNO.should == false + end + + it "returning 'char' or 'unsigned char' returns a Fixnum in Ruby" do + @o.methodReturningChar.should == 42 + @o.methodReturningChar2.should == -42 + @o.methodReturningUnsignedChar.should == 42 + end + + it "returning 'short' or 'unsigned short' returns a Fixnum in Ruby" do + @o.methodReturningShort.should == 42 + @o.methodReturningShort2.should == -42 + @o.methodReturningUnsignedShort.should == 42 + end + + it "returning 'int' or 'unsigned int' returns a Fixnum in Ruby" do + @o.methodReturningInt.should == 42 + @o.methodReturningInt2.should == -42 + @o.methodReturningUnsignedInt.should == 42 + end + + it "returning 'long' or 'unsigned long' returns a Fixnum if possible in Ruby" do + @o.methodReturningLong.should == 42 + @o.methodReturningLong2.should == -42 + @o.methodReturningUnsignedLong.should == 42 + end + + it "returning 'long' or 'unsigned long' returns a Bignum if it cannot fix in a Fixnum in Ruby" do + @o.methodReturningLong3.should == + (RUBY_ARCH == 'x86_64' ? 4611686018427387904 : 1073741824) + @o.methodReturningLong3.class.should == Bignum + @o.methodReturningLong4.should == + (RUBY_ARCH == 'x86_64' ? -4611686018427387905 : -1073741825) + @o.methodReturningLong4.class.should == Bignum + @o.methodReturningUnsignedLong2.should == + (RUBY_ARCH == 'x86_64' ? 4611686018427387904 : 1073741824) + @o.methodReturningUnsignedLong2.class.should == Bignum + end + + it "returning 'float' returns a Float in Ruby" do + @o.methodReturningFloat.should be_close(3.1415, 0.0001) + @o.methodReturningFloat.class.should == Float + end + + it "returning 'double' returns a Float in Ruby" do + @o.methodReturningDouble.should be_close(3.1415, 0.0001) + @o.methodReturningDouble.class.should == Float + end + + it "returning 'SEL' returns a Symbol or nil in Ruby" do + @o.methodReturningSEL.class.should == Symbol + @o.methodReturningSEL.should == :'foo:with:with:' + @o.methodReturningSEL2.class.should == NilClass + @o.methodReturningSEL2.should == nil + end + + it "returning 'char *' returns a String or nil in Ruby" do + @o.methodReturningCharPtr.class.should == String + @o.methodReturningCharPtr.should == 'foo' + @o.methodReturningCharPtr2.class.should == NilClass + @o.methodReturningCharPtr2.should == nil + end + + it "returning 'NSPoint' returns an NSPoint boxed object in Ruby" do + b = @o.methodReturningNSPoint + b.class.should == NSPoint + b.x.class.should == Float + b.x.should == 1.0 + b.y.class.should == Float + b.y.should == 2.0 + end + + it "returning 'NSSize' returns an NSSize boxed object in Ruby" do + b = @o.methodReturningNSSize + b.class.should == NSSize + b.width.class.should == Float + b.width.should == 3.0 + b.height.class.should == Float + b.height.should == 4.0 + end + + it "returning 'NSRect' returns an NSRect boxed object in Ruby" do + b = @o.methodReturningNSRect + b.class.should == NSRect + b.origin.class.should == NSPoint + b.origin.x.should == 1.0 + b.origin.y.should == 2.0 + b.size.class.should == NSSize + b.size.width.should == 3.0 + b.size.height.should == 4.0 + end + + it "returning 'NSRange' returns an NSRange boxed object in Ruby" do + b = @o.methodReturningNSRange + b.class.should == NSRange + b.location.class.should == Fixnum + b.location.should == 0 + b.length.class.should == Fixnum + b.length.should == 42 + end + + it "accepting the receiver as 'id' should receive the exact same object" do + @o.methodAcceptingSelf(@o).should == 1 + end + + it "accepting the receiver's class as 'id' should receive the exact same object" do + @o.methodAcceptingSelfClass(@o.class).should == 1 + end + + it "accepting 'nil' as 'id' should receive Objective-C's nil" do + @o.methodAcceptingNil(nil).should == 1 + end + + it "accepting 'true' as 'id; should receive CF's kCFBooleanTrue" do + @o.methodAcceptingTrue(true).should == 1 + end + + it "accepting 'false' as 'id' should receive CF's kCFBooleanFalse" do + @o.methodAcceptingFalse(false).should == 1 + end + + it "accepting a Fixnum as 'id' should receive a Fixnum boxed object" do + @o.methodAcceptingFixnum(42).should == 1 + end + + it "accepting nil or false as 'BOOL' should receive NO, any other object should receive YES" do + @o.methodAcceptingFalseBOOL(nil).should == 1 + @o.methodAcceptingFalseBOOL(false).should == 1 + + @o.methodAcceptingTrueBOOL(true).should == 1 + @o.methodAcceptingTrueBOOL(0).should == 1 + @o.methodAcceptingTrueBOOL(123).should == 1 + @o.methodAcceptingTrueBOOL('foo').should == 1 + @o.methodAcceptingTrueBOOL(Object.new).should == 1 + end + + it "accepting a Fixnum-compatible object as 'char', 'unsigned char', 'short', 'unsigned short', 'int', 'unsigned int', 'long', 'unsigned long' should receive the converted data, or raise an exception" do + @o.methodAcceptingChar(42).should == 1 + @o.methodAcceptingUnsignedChar(42).should == 1 + @o.methodAcceptingShort(42).should == 1 + @o.methodAcceptingUnsignedShort(42).should == 1 + @o.methodAcceptingInt(42).should == 1 + @o.methodAcceptingUnsignedInt(42).should == 1 + @o.methodAcceptingLong(42).should == 1 + @o.methodAcceptingUnsignedLong(42).should == 1 + + @o.methodAcceptingChar(42.0).should == 1 + @o.methodAcceptingUnsignedChar(42.0).should == 1 + @o.methodAcceptingShort(42.0).should == 1 + @o.methodAcceptingUnsignedShort(42.0).should == 1 + @o.methodAcceptingInt(42.0).should == 1 + @o.methodAcceptingUnsignedInt(42.0).should == 1 + @o.methodAcceptingLong(42.0).should == 1 + @o.methodAcceptingUnsignedLong(42.0).should == 1 + + o2 = Object.new + def o2.to_i; 42; end + + @o.methodAcceptingChar(o2).should == 1 + @o.methodAcceptingUnsignedChar(o2).should == 1 + @o.methodAcceptingShort(o2).should == 1 + @o.methodAcceptingUnsignedShort(o2).should == 1 + @o.methodAcceptingInt(o2).should == 1 + @o.methodAcceptingUnsignedInt(o2).should == 1 + @o.methodAcceptingLong(o2).should == 1 + @o.methodAcceptingUnsignedLong(o2).should == 1 + + lambda { @o.methodAcceptingChar(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedChar(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingShort(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedShort(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingInt(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedInt(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingLong(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedLong(nil) }.should raise_error(TypeError) + + lambda { @o.methodAcceptingChar(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedChar(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingShort(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedShort(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingInt(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedInt(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingLong(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingUnsignedLong(Object.new) }.should raise_error(TypeError) + end + + it "accepting a one-character string as 'char' or 'unsigned char' should receive the first character" do + @o.methodAcceptingChar('*').should == 1 + @o.methodAcceptingUnsignedChar('*').should == 1 + end + + it "accepting a String, Symbol or nil as 'SEL' should receive the appropriate selector" do + @o.methodAcceptingSEL(:'foo:with:with:').should == 1 + @o.methodAcceptingSEL('foo:with:with:').should == 1 + @o.methodAcceptingSEL2(nil).should == 1 + + lambda { @o.methodAcceptingSEL(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingSEL(Object.new) }.should raise_error(TypeError) + end + + it "accepting a Float-compatible object as 'float' or 'double' should receive the appropriate data" do + @o.methodAcceptingFloat(3.1415).should == 1 + @o.methodAcceptingDouble(3.1415).should == 1 + + o2 = Object.new + def o2.to_f; 3.1415; end + + @o.methodAcceptingFloat(o2).should == 1 + @o.methodAcceptingDouble(o2).should == 1 + + lambda { @o.methodAcceptingFloat(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingDouble(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingFloat(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingDouble(Object.new) }.should raise_error(TypeError) + end + + it "accepting a String-compatible object as 'char *' should receive the appropriate data" do + @o.methodAcceptingCharPtr('foo').should == 1 + + o2 = Object.new + def o2.to_str; 'foo' end + + @o.methodAcceptingCharPtr(o2).should == 1 + + lambda { @o.methodAcceptingCharPtr(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingCharPtr([]) }.should raise_error(TypeError) + lambda { @o.methodAcceptingCharPtr(Object.new) }.should raise_error(TypeError) + + @o.methodAcceptingCharPtr2(nil).should == 1 + end + + it "accepting an NSPoint, NSSize, NSRange or NSRect object as 'NSPoint', 'NSSize', 'NSRange' or 'NSRect' should receive the C structure" do + p = @o.methodReturningNSPoint + @o.methodAcceptingNSPoint(p).should == 1 + p = @o.methodReturningNSSize + @o.methodAcceptingNSSize(p).should == 1 + p = @o.methodReturningNSRect + @o.methodAcceptingNSRect(p).should == 1 + p = @o.methodReturningNSRange + @o.methodAcceptingNSRange(p).should == 1 + + lambda { @o.methodAcceptingNSPoint(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSPoint(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSPoint(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSPoint(@o.methodReturningNSSize) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSSize(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSSize(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSSize(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSSize(@o.methodReturningNSPoint) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRect(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRect(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRect(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRect(@o.methodReturningNSPoint) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRange(nil) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRange(123) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRange(Object.new) }.should raise_error(TypeError) + lambda { @o.methodAcceptingNSRange(@o.methodReturningNSPoint) }.should raise_error(TypeError) + end + + it "accepting an Array of valid objects as a structure type should receive the C structure" do + @o.methodAcceptingNSPoint([1, 2]).should == 1 + @o.methodAcceptingNSSize([3, 4]).should == 1 + @o.methodAcceptingNSRect([[1, 2], [3, 4]]).should == 1 + @o.methodAcceptingNSRect([1, 2, 3, 4]).should == 1 + @o.methodAcceptingNSRange([0, 42]).should == 1 + + lambda { @o.methodAcceptingNSPoint([1]) }.should raise_error(ArgumentError) + lambda { @o.methodAcceptingNSPoint([1, 2, 3]) }.should raise_error(ArgumentError) + lambda { @o.methodAcceptingNSRect([1, 2, 3]) }.should raise_error(ArgumentError) + lambda { @o.methodAcceptingNSRect([1, 2, 3, 4, 5]) }.should raise_error(ArgumentError) + lambda { @o.methodAcceptingNSRect([[1, 2], [3]]) }.should raise_error(ArgumentError) + lambda { @o.methodAcceptingNSRect([[1, 2], [3, 4, 5]]) }.should raise_error(ArgumentError) + end + + it "accepting various C types should receive these types as expected" do + @o.methodAcceptingInt(42, float:42, double:42, short:42, NSPoint:[42, 42], + NSRect:[42, 42, 42, 42], char:42).should == 1 + end +end Added: MacRuby/branches/experimental/spec/macruby/pointer_spec.rb =================================================================== --- MacRuby/branches/experimental/spec/macruby/pointer_spec.rb (rev 0) +++ MacRuby/branches/experimental/spec/macruby/pointer_spec.rb 2009-04-28 02:23:44 UTC (rev 1495) @@ -0,0 +1,52 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +framework 'Foundation' + +describe "A Pointer object" do +=begin # TODO + it "can be created using the #new class method and with a valid Objective-C type or a valid Symbol object" do + types = { + :object => '@', + :char => 'c', + :uchar => 'C', + :short => 's', + :ushort => 'S', + :int => 'i', + :uint => 'I', + :long => 'l', + :ulong => 'L', + :long_long => 'q', + :ulong_long => 'Q', + :float => 'f', + :double => 'd' + } + + types.each do |sym, typestr| + p = nil + lambda { p = Pointer.new(sym) }.should_not raise_error + p.type.should == typestr + + lambda { p = Pointer.new(typestr) }.should_not raise_error + p.type.should == typestr + end + + lambda { Pointer.new }.should raise_error(ArgumentError) + lambda { Pointer.new('invalid') }.should raise_error(TypeError) + end +=end + +=begin # TODO + it "can be assigned an object of the given type and retrieve it later, using #[] and #[]=" + p = Pointer.new('@') + o = Object.new + p[0] = o + p[0].should == o + + p = Pointer.new('i') + p[0] = 42 + p[0].should == 42 + + lambda { p[0] = Object.new }.should raise_error(TypeError) + end +=end +end
participants (1)
-
source_changes@macosforge.org