Revision: 1485 http://trac.macosforge.org/projects/ruby/changeset/1485 Author: lsansonetti@apple.com Date: 2009-04-23 22:32:50 -0700 (Thu, 23 Apr 2009) Log Message: ----------- some opaque types work Modified Paths: -------------- MacRuby/branches/experimental/roxor.cpp Added Paths: ----------- MacRuby/branches/experimental/spec/macruby/opaque_spec.rb Modified: MacRuby/branches/experimental/roxor.cpp =================================================================== --- MacRuby/branches/experimental/roxor.cpp 2009-04-24 04:28:43 UTC (rev 1484) +++ MacRuby/branches/experimental/roxor.cpp 2009-04-24 05:32:50 UTC (rev 1485) @@ -266,7 +266,9 @@ Function *dupArrayFunc; Function *newArrayFunc; Function *newStructFunc; + Function *newOpaqueFunc; Function *getStructFieldsFunc; + Function *getOpaqueDataFunc; Function *checkArityFunc; Function *setStructFunc; Function *newRangeFunc; @@ -301,6 +303,7 @@ const Type *RubyObjPtrTy; const Type *RubyObjPtrPtrTy; const Type *PtrTy; + const Type *PtrPtrTy; const Type *IntTy; void compile_node_error(const char *msg, NODE *node) { @@ -351,8 +354,11 @@ void compile_rethrow_exception(void); Value *compile_lvar_slot(ID name); Value *compile_new_struct(Value *klass, std::vector<Value *> &fields); + Value *compile_new_opaque(Value *klass, Value *val); void compile_get_struct_fields(Value *val, Value *buf, rb_vm_bs_boxed_t *bs_boxed); + Value *compile_get_opaque_data(Value *val, rb_vm_bs_boxed_t *bs_boxed, + Value *slot); void compile_check_arity(Value *given, Value *requested); void compile_set_struct(Value *rcv, int field, Value *val); @@ -501,6 +507,7 @@ bs_element_method_t *find_bs_method(Class klass, SEL sel); rb_vm_bs_boxed_t *find_bs_boxed(std::string type); rb_vm_bs_boxed_t *find_bs_struct(std::string type); + rb_vm_bs_boxed_t *find_bs_opaque(std::string type); #if ROXOR_ULTRA_LAZY_JIT std::map<SEL, std::map<Class, rb_vm_method_source_t *> *> @@ -664,7 +671,9 @@ dupArrayFunc = NULL; newArrayFunc = NULL; newStructFunc = NULL; + newOpaqueFunc = NULL; getStructFieldsFunc = NULL; + getOpaqueDataFunc = NULL; checkArityFunc = NULL; setStructFunc = NULL; newRangeFunc = NULL; @@ -705,6 +714,7 @@ splatArgFollowsVal = ConstantInt::get(RubyObjTy, SPLAT_ARG_FOLLOWS); cObject = ConstantInt::get(RubyObjTy, (long)rb_cObject); PtrTy = PointerType::getUnqual(Type::Int8Ty); + PtrPtrTy = PointerType::getUnqual(PtrTy); #if ROXOR_COMPILER_DEBUG level = 0; @@ -5132,7 +5142,47 @@ CallInst::Create(getStructFieldsFunc, params.begin(), params.end(), "", bb); } +extern "C" +void * +rb_vm_get_opaque_data(VALUE rval, rb_vm_bs_boxed_t *bs_boxed, void **ocval) +{ + if (rval == Qnil) { + return *ocval = NULL; + } + else { + if (!rb_obj_is_kind_of(rval, bs_boxed->klass)) { + rb_raise(rb_eTypeError, + "cannot convert `%s' (%s) to opaque type %s", + RSTRING_PTR(rb_inspect(rval)), + rb_obj_classname(rval), + rb_class2name(bs_boxed->klass)); + } + VALUE *data; + Data_Get_Struct(rval, VALUE, data); + return *ocval = (void *)data; + } +} + Value * +RoxorCompiler::compile_get_opaque_data(Value *val, rb_vm_bs_boxed_t *bs_boxed, + Value *slot) +{ + if (getOpaqueDataFunc == NULL) { + getOpaqueDataFunc = cast<Function>(module->getOrInsertFunction( + "rb_vm_get_opaque_data", + PtrTy, RubyObjTy, PtrTy, PtrPtrTy, NULL)); + } + + std::vector<Value *> params; + params.push_back(val); + params.push_back(compile_const_pointer(bs_boxed)); + params.push_back(slot); + + return CallInst::Create(getOpaqueDataFunc, params.begin(), params.end(), + "", bb); +} + +Value * RoxorCompiler::compile_conversion_to_c(const char *type, Value *val, Value *slot) { @@ -5242,6 +5292,15 @@ } } break; + + case _C_PTR: + { + rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_opaque(type); + if (bs_boxed != NULL) { + return compile_get_opaque_data(val, bs_boxed, slot); + } + } + break; } if (func_name == NULL) { @@ -5321,6 +5380,13 @@ return Data_Wrap_Struct(klass, NULL, NULL, data); } +extern "C" +VALUE +rb_vm_new_opaque(VALUE klass, void *val) +{ + return Data_Wrap_Struct(klass, NULL, NULL, val); +} + Value * RoxorCompiler::compile_new_struct(Value *klass, std::vector<Value *> &fields) { @@ -5343,6 +5409,22 @@ } Value * +RoxorCompiler::compile_new_opaque(Value *klass, Value *val) +{ + if (newOpaqueFunc == NULL) { + newOpaqueFunc = cast<Function>(module->getOrInsertFunction( + "rb_vm_new_opaque", RubyObjTy, RubyObjTy, PtrTy, NULL)); + } + + std::vector<Value *> params; + params.push_back(klass); + params.push_back(val); + + return CallInst::Create(newOpaqueFunc, params.begin(), params.end(), + "", bb); +} + +Value * RoxorCompiler::compile_conversion_to_ruby(const char *type, const Type *llvm_type, Value *val) { @@ -5413,25 +5495,37 @@ break; case _C_STRUCT_B: - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(type); - if (bs_boxed != NULL) { - std::vector<Value *> params; + { + rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(type); + if (bs_boxed != NULL) { + std::vector<Value *> params; - for (unsigned i = 0; i < bs_boxed->as.s->fields_count; - i++) { - - const char *ftype = bs_boxed->as.s->fields[i].type; - const Type *llvm_ftype = convert_type(ftype); - Value *fval = ExtractValueInst::Create(val, i, "", bb); + for (unsigned i = 0; i < bs_boxed->as.s->fields_count; + i++) { - params.push_back(compile_conversion_to_ruby(ftype, - llvm_ftype, fval)); + const char *ftype = bs_boxed->as.s->fields[i].type; + const Type *llvm_ftype = convert_type(ftype); + Value *fval = ExtractValueInst::Create(val, i, "", bb); + + params.push_back(compile_conversion_to_ruby(ftype, + llvm_ftype, fval)); + } + + Value *klass = ConstantInt::get(RubyObjTy, bs_boxed->klass); + return compile_new_struct(klass, params); } + } + break; - Value *klass = ConstantInt::get(RubyObjTy, bs_boxed->klass); - return compile_new_struct(klass, params); + case _C_PTR: + { + rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_opaque(type); + if (bs_boxed != NULL) { + Value *klass = ConstantInt::get(RubyObjTy, bs_boxed->klass); + return compile_new_opaque(klass, val); + } } - break; + break; } if (func_name == NULL) { @@ -8546,7 +8640,7 @@ #include "bs_struct_readers.c" static VALUE -rb_vm_struct_equal(VALUE rcv, SEL sel, VALUE val) +rb_vm_boxed_equal(VALUE rcv, SEL sel, VALUE val) { if (rcv == val) { return Qtrue; @@ -8556,19 +8650,21 @@ return Qfalse; } - rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(klass, true); + rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(klass); - VALUE *rcv_data; - VALUE *val_data; - Data_Get_Struct(rcv, VALUE, rcv_data); - Data_Get_Struct(val, VALUE, val_data); + VALUE *rcv_data; Data_Get_Struct(rcv, VALUE, rcv_data); + VALUE *val_data; Data_Get_Struct(val, VALUE, val_data); - for (unsigned i = 0; i < bs_boxed->as.s->fields_count; i++) { - if (!rb_equal(rcv_data[i], val_data[i])) { - return Qfalse; + if (bs_boxed->bs_type == BS_ELEMENT_STRUCT) { + for (unsigned i = 0; i < bs_boxed->as.s->fields_count; i++) { + if (!rb_equal(rcv_data[i], val_data[i])) { + return Qfalse; + } } + return Qtrue; } - return Qtrue; + + return rcv_data == val_data ? Qtrue : Qfalse; } static VALUE @@ -8614,6 +8710,29 @@ return Data_Wrap_Struct(klass, NULL, NULL, new_data); } +static VALUE +rb_boxed_fields(VALUE rcv, SEL sel) +{ + rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(rcv); + VALUE ary = rb_ary_new(); + if (bs_boxed->bs_type == BS_ELEMENT_STRUCT) { + for (unsigned i = 0; i < bs_boxed->as.s->fields_count; i++) { + VALUE field = ID2SYM(rb_intern(bs_boxed->as.s->fields[i].name)); + rb_ary_push(ary, field); + } + } + return ary; +} + +static VALUE +rb_vm_opaque_new(VALUE rcv, SEL sel) +{ + // XXX instead of doing this, we should perhaps simply delete the new + // method on the class... + rb_raise(rb_eRuntimeError, "can't allocate opaque type `%s'", + rb_class2name(rcv)); +} + static bool register_bs_boxed(bs_element_type_t type, void *value) { @@ -8656,8 +8775,8 @@ } // Define other utility methods. - rb_objc_define_method(boxed->klass, "==", - (void *)rb_vm_struct_equal, 1); + rb_objc_define_method(*(VALUE *)boxed->klass, "fields", + (void *)rb_boxed_fields, 0); rb_objc_define_method(boxed->klass, "dup", (void *)rb_vm_struct_dup, 0); rb_objc_define_method(boxed->klass, "clone", @@ -8665,6 +8784,13 @@ rb_objc_define_method(boxed->klass, "inspect", (void *)rb_vm_struct_inspect, 0); } + else { + // Opaque methods. + rb_objc_define_method(*(VALUE *)boxed->klass, "new", + (void *)rb_vm_opaque_new, -1); + } + // Common methods. + rb_objc_define_method(boxed->klass, "==", (void *)rb_vm_boxed_equal, 1); GET_VM()->bs_boxed[octype] = boxed; @@ -8684,20 +8810,6 @@ return bs_boxed->bs_type == BS_ELEMENT_OPAQUE ? Qtrue : Qfalse; } -static VALUE -rb_boxed_fields(VALUE rcv, SEL sel) -{ - rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(rcv); - VALUE ary = rb_ary_new(); - if (bs_boxed->bs_type == BS_ELEMENT_STRUCT) { - for (unsigned i = 0; i < bs_boxed->as.s->fields_count; i++) { - VALUE field = ID2SYM(rb_intern(bs_boxed->as.s->fields[i].name)); - rb_ary_push(ary, field); - } - } - return ary; -} - VALUE rb_cBoxed; static void @@ -8710,8 +8822,6 @@ (void *)rb_boxed_objc_type, 0); rb_objc_define_method(*(VALUE *)rb_cBoxed, "opaque?", (void *)rb_boxed_is_opaque, 0); - rb_objc_define_method(*(VALUE *)rb_cBoxed, "fields", - (void *)rb_boxed_fields, 0); } static inline void @@ -8787,6 +8897,13 @@ return boxed->is_struct() ? boxed : NULL; } +inline rb_vm_bs_boxed_t * +RoxorVM::find_bs_opaque(std::string type) +{ + rb_vm_bs_boxed_t *boxed = find_bs_boxed(type); + return boxed->is_struct() ? NULL : boxed; +} + static inline void register_bs_class(bs_element_class_t *bs_class) { Added: MacRuby/branches/experimental/spec/macruby/opaque_spec.rb =================================================================== --- MacRuby/branches/experimental/spec/macruby/opaque_spec.rb (rev 0) +++ MacRuby/branches/experimental/spec/macruby/opaque_spec.rb 2009-04-24 05:32:50 UTC (rev 1485) @@ -0,0 +1,31 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +framework 'Foundation' + +describe "A BridgeSupport opaque type" do + it "is an instance of Boxed" do + NSZone.superclass.should == Boxed + end + + it "cannot be created with #new" do + lambda { NSZone.new }.should raise_error(RuntimeError) + end + + it "can be created from an Objective-C API, and passed back to Objective-C" do + z = 123.zone + z.class.should == NSZone + lambda { Object.allocWithZone(z).init }.should_not raise_error + end + + it "can be compared to an exact same instance using #==" do + 123.zone.should == 456.zone + end + + it "returns true when the #opaque? class method is called" do + NSZone.opaque?.should == true + end + + it "returns its Objective-C encoding type when then #type class method is called" do + NSZone.type.should == '^{_NSZone=}' + end +end
participants (1)
-
source_changes@macosforge.org