Revision: 1477 http://trac.macosforge.org/projects/ruby/changeset/1477 Author: lsansonetti@apple.com Date: 2009-04-23 15:50:17 -0700 (Thu, 23 Apr 2009) Log Message: ----------- implemented struct writers Modified Paths: -------------- MacRuby/branches/experimental/roxor.cpp MacRuby/branches/experimental/spec/macruby/struct_spec.rb Modified: MacRuby/branches/experimental/roxor.cpp =================================================================== --- MacRuby/branches/experimental/roxor.cpp 2009-04-23 05:38:29 UTC (rev 1476) +++ MacRuby/branches/experimental/roxor.cpp 2009-04-23 22:50:17 UTC (rev 1477) @@ -201,6 +201,8 @@ Function *compile_write_attr(ID name); Function *compile_stub(const char *types, int argc, bool is_objc); Function *compile_bs_struct_new(rb_vm_bs_boxed_t *bs_boxed); + Function *compile_bs_struct_writer(rb_vm_bs_boxed_t *bs_boxed, + int field); private: const char *fname; @@ -266,6 +268,7 @@ Function *newStructFunc; Function *getStructFieldsFunc; Function *checkArityFunc; + Function *setStructFunc; Function *newRangeFunc; Function *newRegexpFunc; Function *strInternFunc; @@ -351,6 +354,7 @@ void compile_get_struct_fields(Value *val, Value *buf, rb_vm_bs_boxed_t *bs_boxed); void compile_check_arity(Value *given, Value *requested); + void compile_set_struct(Value *rcv, int field, Value *val); Value *compile_conversion_to_c(const char *type, Value *val, Value *slot); @@ -661,6 +665,7 @@ newStructFunc = NULL; getStructFieldsFunc = NULL; checkArityFunc = NULL; + setStructFunc = NULL; newRangeFunc = NULL; newRegexpFunc = NULL; strInternFunc = NULL; @@ -8334,9 +8339,65 @@ compile_protected_call(checkArityFunc, params); } +extern "C" +void +rb_vm_set_struct(VALUE rcv, int field, VALUE val) +{ + VALUE *data; + Data_Get_Struct(rcv, VALUE, data); + GC_WB(&data[field], val); +} + +void +RoxorCompiler::compile_set_struct(Value *rcv, int field, Value *val) +{ + if (setStructFunc == NULL) { + // void rb_vm_set_struct(VALUE rcv, int field, VALUE val); + setStructFunc = cast<Function>(module->getOrInsertFunction( + "rb_vm_set_struct", + Type::VoidTy, RubyObjTy, Type::Int32Ty, RubyObjTy, NULL)); + } + + std::vector<Value *> params; + params.push_back(rcv); + params.push_back(ConstantInt::get(Type::Int32Ty, field)); + params.push_back(val); + + CallInst::Create(setStructFunc, params.begin(), params.end(), "", bb); +} + Function * +RoxorCompiler::compile_bs_struct_writer(rb_vm_bs_boxed_t *bs_boxed, int field) +{ + // VALUE foo(VALUE self, SEL sel, VALUE val); + Function *f = cast<Function>(module->getOrInsertFunction("", + RubyObjTy, RubyObjTy, PtrTy, RubyObjTy, NULL)); + Function::arg_iterator arg = f->arg_begin(); + Value *self = arg++; // self + arg++; // sel + Value *val = arg++; // val + + bb = BasicBlock::Create("EntryBlock", f); + + assert((unsigned)field < bs_boxed->as.s->fields_count); + const char *ftype = bs_boxed->as.s->fields[field].type; + const Type *llvm_type = convert_type(ftype); + + Value *fval = new AllocaInst(llvm_type, "", bb); + val = compile_conversion_to_c(ftype, val, fval); + val = compile_conversion_to_ruby(ftype, llvm_type, val); + + compile_set_struct(self, field, val); + + ReturnInst::Create(val, bb); + + return f; +} + +Function * RoxorCompiler::compile_bs_struct_new(rb_vm_bs_boxed_t *bs_boxed) { + // VALUE foo(VALUE self, SEL sel, int argc, VALUE *argv); Function *f = cast<Function>(module->getOrInsertFunction("", RubyObjTy, RubyObjTy, PtrTy, Type::Int32Ty, RubyObjPtrTy, NULL)); @@ -8415,16 +8476,21 @@ static ID boxed_ivar_type = 0; -static VALUE -rb_vm_struct_fake_new(VALUE rcv, SEL sel, int argc, VALUE *argv) +static inline rb_vm_bs_boxed_t * +locate_bs_boxed(VALUE klass) { - // Locate the boxed structure. - VALUE type = rb_ivar_get(rcv, boxed_ivar_type); + VALUE type = rb_ivar_get(klass, boxed_ivar_type); assert(type != Qnil); rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(RSTRING_PTR(type)); assert(bs_boxed != NULL); + return bs_boxed; +} +static VALUE +rb_vm_struct_fake_new(VALUE rcv, SEL sel, int argc, VALUE *argv) +{ // Generate the real #new method. + rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(rcv); Function *f = RoxorCompiler::shared->compile_bs_struct_new(bs_boxed); IMP imp = GET_VM()->compile(f); @@ -8435,6 +8501,43 @@ return ((VALUE (*)(VALUE, SEL, int, VALUE *))imp)(rcv, sel, argc, argv); } +static VALUE +rb_vm_struct_fake_set(VALUE rcv, SEL sel, VALUE val) +{ + // Locate the given field. + char buf[100]; + const char *selname = sel_getName(sel); + size_t s = strlcpy(buf, selname, sizeof buf); + if (buf[s - 1] == ':') { + s--; + } + assert(buf[s - 1] == '='); + buf[s - 1] = '\0'; + rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(CLASS_OF(rcv)); + int field = -1; + for (unsigned i = 0; i < bs_boxed->as.s->fields_count; i++) { + const char *fname = bs_boxed->as.s->fields[i].name; + if (strcmp(fname, buf) == 0) { + field = i; + break; + } + } + assert(field != -1); + + // Generate the new setter method. + Function *f = RoxorCompiler::shared->compile_bs_struct_writer( + bs_boxed, field); + IMP imp = GET_VM()->compile(f); + + // Replace the fake method with the new one in the runtime. + buf[s - 1] = '='; + buf[s] = '\0'; + rb_objc_define_method(*(VALUE *)rcv, buf, (void *)imp, 1); + + // Call the new method. + return ((VALUE (*)(VALUE, SEL, VALUE))imp)(rcv, sel, val); +} + // Readers are statically generated. #include "bs_struct_readers.c" @@ -8472,7 +8575,11 @@ // Readers. rb_objc_define_method(boxed->klass, boxed->as.s->fields[i].name, (void *)struct_readers[i], 0); - // Writers. (TODO) + // Writers. + char buf[100]; + snprintf(buf, sizeof buf, "%s=", boxed->as.s->fields[i].name); + rb_objc_define_method(boxed->klass, buf, + (void *)rb_vm_struct_fake_set, 1); } } Modified: MacRuby/branches/experimental/spec/macruby/struct_spec.rb =================================================================== --- MacRuby/branches/experimental/spec/macruby/struct_spec.rb 2009-04-23 05:38:29 UTC (rev 1476) +++ MacRuby/branches/experimental/spec/macruby/struct_spec.rb 2009-04-23 22:50:17 UTC (rev 1477) @@ -13,34 +13,34 @@ it "can be created with null values using the #new method with no argument" do o = NSPoint.new o.x.class.should == Float + o.y.class.should == Float o.x.should == 0.0 - o.y.class.should == Float o.y.should == 0.0 o = NSRect.new o.origin.class.should == NSPoint o.origin.x.class.should == Float + o.origin.y.class.should == Float o.origin.x.should == 0.0 - o.origin.y.class.should == Float o.origin.y.should == 0.0 o.size.class.should == NSSize o.size.width.class.should == Float + o.size.height.class.should == Float o.size.width.should == 0.0 - o.size.height.class.should == Float o.size.height.should == 0.0 end it "can be created with given values using the #new method with arguments" do o = NSPoint.new(1.0, 2.0) + o.y.class.should == Float o.x.class.should == Float o.x.should == 1.0 - o.y.class.should == Float o.y.should == 2.0 o = NSPoint.new(1, 2) o.x.class.should == Float + o.y.class.should == Float o.x.should == 1.0 - o.y.class.should == Float o.y.should == 2.0 fix1 = Object.new; def fix1.to_f; 1.0; end @@ -48,8 +48,8 @@ o = NSPoint.new(fix1, fix2) o.x.class.should == Float + o.y.class.should == Float o.x.should == 1.0 - o.y.class.should == Float o.y.should == 2.0 lambda { NSPoint.new(1) }.should raise_error(ArgumentError) @@ -60,13 +60,13 @@ o = NSRect.new(NSPoint.new(1, 2), NSSize.new(3, 4)) o.origin.class.should == NSPoint o.origin.x.class.should == Float + o.origin.y.class.should == Float o.origin.x == 1.0 - o.origin.y.class.should == Float o.origin.y == 2.0 o.size.class.should == NSSize o.size.width.class.should == Float + o.size.height.class.should == Float o.size.width == 3.0 - o.size.height.class.should == Float o.size.height == 4.0 lambda { NSRect.new(1) }.should raise_error(ArgumentError) @@ -77,4 +77,42 @@ lambda { NSRect.new(nil, nil) }.should raise_error(TypeError) end + it "has accessors for every field" do + p = NSPoint.new + p.x = 1 + p.y = 2 + p.x.class.should == Float + p.y.class.should == Float + p.x.should == 1.0 + p.y.should == 2.0 + + s = NSSize.new + s.width = 3 + s.height = 4 + s.width.class.should == Float + s.height.class.should == Float + s.width.should == 3.0 + s.height.should == 4.0 + + r = NSRect.new + r.origin = p + r.size = s + r.origin.class.should == NSPoint + r.size.class.should == NSSize + + lambda { r.origin = nil }.should raise_error(TypeError) + lambda { r.origin = Object.new }.should raise_error(TypeError) + + r.origin = [123, 456] + r.size = [789, 100] + + r.origin.x.class.should == Float + r.origin.y.class.should == Float + r.size.width.class.should == Float + r.size.height.class.should == Float + r.origin.x.should == 123.0 + r.origin.y.should == 456.0 + r.size.width.should == 789.0 + r.size.height.should == 100.0 + end end