Browse files

fix the compilation of C blocks closures to not use a global variable…

… to store/load the Proc object to call, but instead retrieve a reference to it from the block literal argument

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@5163 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
1 parent 7beebf0 commit 807d2d26cdc5ab4b4a377ee3fa6757a3c5d3dd9a @lrz lrz committed Jan 14, 2011
Showing with 49 additions and 12 deletions.
  1. +27 −12 compiler.cpp
  2. +1 −0 compiler.h
  3. +6 −0 kernel.c
  4. +15 −0 spec/macruby/language/objc_method_spec.rb
View
39 compiler.cpp
@@ -274,6 +274,7 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode)
rvalToSelFunc = get_function("vm_rval_to_sel");
rvalToCharPtrFunc = get_function("vm_rval_to_charptr");
initBlockFunc = get_function("vm_init_c_block");
+ blockProcFunc = get_function("vm_ruby_block_literal_proc");
setCurrentMRIMethodContext = NULL;
VoidTy = Type::getVoidTy(context);
@@ -5249,11 +5250,17 @@ Value *
RoxorCompiler::compile_lambda_to_funcptr(const char *type,
Value *val, Value *slot, bool is_block)
{
- GlobalVariable *proc_gvar =
- new GlobalVariable(*RoxorCompiler::module,
+ GlobalVariable *proc_gvar = NULL;
+ if (!is_block) {
+ // When compiling a function pointer closure, the Proc object we
+ // want to call must be preserved as a global variable.
+ // This isn't needed for C blocks because we can retrieve the Proc
+ // object from the block literal argument.
+ proc_gvar = new GlobalVariable(*RoxorCompiler::module,
RubyObjTy, false, GlobalValue::InternalLinkage,
nilVal, "");
- new StoreInst(val, proc_gvar, bb);
+ new StoreInst(val, proc_gvar, bb);
+ }
const size_t buf_len = strlen(type + 1) + 1;
assert(buf_len > 1);
@@ -5267,8 +5274,7 @@ RoxorCompiler::compile_lambda_to_funcptr(const char *type,
std::vector<const Type *> arg_types;
if (is_block) {
- // The Block ABI specifies that the first argument is a pointer
- // to the block literal, which we don't really care about.
+ // The block literal argument.
arg_types.push_back(PtrTy);
}
@@ -5295,6 +5301,10 @@ RoxorCompiler::compile_lambda_to_funcptr(const char *type,
bb = BasicBlock::Create(context, "EntryBlock", f);
Function::arg_iterator arg = f->arg_begin();
+ Value *block_lit = NULL;
+ if (is_block) {
+ block_lit = arg++;
+ }
Value *argv;
if (argc == 0) {
@@ -5304,12 +5314,7 @@ RoxorCompiler::compile_lambda_to_funcptr(const char *type,
else {
argv = new AllocaInst(RubyObjTy, ConstantInt::get(Int32Ty, argc),
"", bb);
- int off = 0;
- if (is_block) {
- // Skip block literal argument.
- off++;
- arg++;
- }
+ const int off = is_block ? 1 : 0;
for (int i = 0; i < argc; i++) {
Value *index = ConstantInt::get(Int32Ty, i);
Value *aslot = GetElementPtrInst::Create(argv, index, "", bb);
@@ -5319,6 +5324,16 @@ RoxorCompiler::compile_lambda_to_funcptr(const char *type,
}
}
+ Value *proc;
+ if (is_block) {
+ block_lit = new BitCastInst(block_lit,
+ PointerType::getUnqual(BlockLiteralTy), "", bb);
+ proc = CallInst::Create(blockProcFunc, block_lit, "", bb);
+ }
+ else {
+ proc = new LoadInst(proc_gvar, "", bb);
+ }
+
// VALUE rb_proc_check_and_call(
// VALUE self, int argc, VALUE *argv
// )
@@ -5329,7 +5344,7 @@ RoxorCompiler::compile_lambda_to_funcptr(const char *type,
RubyObjTy, Int32Ty, RubyObjPtrTy, NULL));
Value *args[] = {
- new LoadInst(proc_gvar, "", bb),
+ proc,
ConstantInt::get(Int32Ty, argc),
argv
};
View
1 compiler.h
@@ -263,6 +263,7 @@ class RoxorCompiler {
Function *rvalToSelFunc;
Function *rvalToCharPtrFunc;
Function *initBlockFunc;
+ Function *blockProcFunc;
Function *setCurrentMRIMethodContext;
Constant *zeroVal;
View
6 kernel.c
@@ -1089,3 +1089,9 @@ vm_init_c_block(struct ruby_block_literal *b, void *imp, VALUE proc)
b->descriptor = &ruby_block_descriptor_value;
GC_WB(&b->ruby_proc, proc);
}
+
+PRIMITIVE VALUE
+vm_ruby_block_literal_proc(struct ruby_block_literal *b)
+{
+ return b->ruby_proc;
+}
View
15 spec/macruby/language/objc_method_spec.rb
@@ -816,6 +816,21 @@ def @o.a
res.should == ['zero', 'one', 'two']
end
+ it "can be used when an Objective-C method takes a Block as argument (nested)" do
+ ary1 = ['zero', 'one', 'two', 'three', 'four']
+ ary2 = ['zero', 'un', 'deux', 'trois', 'quatre']
+ res = []
+ ary1.enumerateObjectsUsingBlock(Proc.new { |obj, idx, stop|
+ res << obj
+ ary2.enumerateObjectsUsingBlock(Proc.new { |obj, idx, stop|
+ res << obj
+ stop.assign(true) if idx == 1
+ })
+ stop.assign(true) if idx == 2
+ })
+ res.should == ['zero', 'zero', 'un', 'one', 'zero', 'un', 'two', 'zero', 'un']
+ end
+
it "is properly retained/released when transformed as a Block" do
o = TestMethod.new
o.methodSavingBlockReference(Proc.new { |x, y| x * y })

0 comments on commit 807d2d2

Please sign in to comment.