Modified: MacRuby/trunk/bs.c (4494 => 4495)
--- MacRuby/trunk/bs.c 2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/bs.c 2010-09-08 00:53:10 UTC (rev 4495)
@@ -319,10 +319,10 @@
bool ok;
int nested;
- opposite =
- *p_src == '{' ? '}' :
- *p_src == '(' ? ')' :
- *p_src == '[' ? ']' : 0;
+ opposite =
+ *p_src == '{' ? '}' :
+ *p_src == '(' ? ')' :
+ *p_src == '[' ? ']' : 0;
for (i = 0, ok = false, nested = 0;
i < src_len - (p_src - src) && !ok;
@@ -1100,23 +1100,18 @@
retval = func != NULL ? func->retval : method->retval;
}
else {
- args_count = func != NULL ? func->args_count : method->args_count;
+ args_count = func != NULL ? func->args_count
+ : method->args_count;
arg = &args[args_count - 1];
}
- // Let's get the old type to keep the number of pointers
- char *old_type = (retval ? retval->type : arg->type);
- int nb_ptr = 0;
+ // Determine if we deal with a block or a function pointer.
+ const char *old_type = (retval ? retval->type : arg->type);
+ const char lambda_type = *old_type == '@'
+ ? _MR_C_LAMBDA_BLOCK
+ : _MR_C_LAMBDA_FUNCPTR;
- while (old_type && *old_type == '^') {
- nb_ptr++;
- old_type++;
- }
- // Decrementing because if we have ^^?, the last ^ (along with ?)
- // is going to be replaced by <...>, so we don't want to keep it
- nb_ptr--;
-
- char tmp_type[1026]; // 2 less to fit <, and >
+ char tmp_type[1025]; // 3 less to fit <, type and >
char new_type[1028];
// Function ptr return type
@@ -1127,13 +1122,9 @@
}
// Clear the final type string
memset(new_type, 0, sizeof(new_type));
- // Copy the number of pointers
- for (int i = 0; i < nb_ptr; i++) {
- strlcat(new_type, "^", sizeof(new_type));
- }
// Append the function pointer type
- snprintf(new_type + nb_ptr, sizeof(new_type) - nb_ptr,
- "%c%s%c", _MR_C_FPTR_B, tmp_type, _MR_C_FPTR_E);
+ snprintf(new_type, sizeof(new_type), "%c%c%s%c",
+ _MR_C_LAMBDA_B, lambda_type, tmp_type, _MR_C_LAMBDA_E);
// Free the old values
if (retval) {
Modified: MacRuby/trunk/compiler.cpp (4494 => 4495)
--- MacRuby/trunk/compiler.cpp 2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/compiler.cpp 2010-09-08 00:53:10 UTC (rev 4495)
@@ -267,6 +267,7 @@
rvalToDoubleFunc = get_function("vm_rval_to_double");
rvalToSelFunc = get_function("vm_rval_to_sel");
rvalToCharPtrFunc = get_function("vm_rval_to_charptr");
+ initBlockFunc = get_function("vm_init_c_block");
VoidTy = Type::getVoidTy(context);
Int1Ty = Type::getInt1Ty(context);
@@ -303,6 +304,9 @@
Int32PtrTy = PointerType::getUnqual(Int32Ty);
BitTy = Type::getInt1Ty(context);
+ BlockLiteralTy = module->getTypeByName("struct.ruby_block_literal");
+ assert(BlockLiteralTy != NULL);
+
#if ROXOR_COMPILER_DEBUG
level = 0;
#endif
@@ -5192,8 +5196,114 @@
}
Value *
+RoxorCompiler::compile_lambda_to_funcptr(const char *type,
+ Value *val, Value *slot, bool is_block)
+{
+ GlobalVariable *proc_gvar =
+ new GlobalVariable(*RoxorCompiler::module,
+ RubyObjTy, false, GlobalValue::InternalLinkage,
+ nilVal, "");
+ new StoreInst(val, proc_gvar, bb);
+
+ const size_t buf_len = strlen(type + 1) + 1;
+ assert(buf_len > 1);
+ char *buf = (char *)malloc(buf_len);
+
+ const char *p = GetFirstType(type + 1, buf, buf_len);
+ const Type *ret_type = convert_type(buf);
+ int argc = 0;
+
+ std::vector<std::string> arg_ctypes;
+ 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.
+ arg_types.push_back(PtrTy);
+ }
+
+ while (*p != _MR_C_LAMBDA_E) {
+ p = GetFirstType(p, buf, buf_len);
+ arg_ctypes.push_back(std::string(buf));
+ arg_types.push_back(convert_type(buf));
+ argc++;
+ }
+ FunctionType *ft = FunctionType::get(ret_type, arg_types,
+ false);
+
+ // ret_type stub(arg1, arg2, ...)
+ // {
+ // VALUE *argv = alloc(argc);
+ // argv[0] = arg1;
+ // argv[1] = arg2;
+ // return rb_proc_check_and_call(procval, argc, argv);
+ // }
+ Function *f = cast<Function>(module->getOrInsertFunction("",
+ ft));
+
+ BasicBlock *oldbb = bb;
+ bb = BasicBlock::Create(context, "EntryBlock", f);
+
+ Function::arg_iterator arg = f->arg_begin();
+
+ Value *argv;
+ if (argc == 0) {
+ argv = new BitCastInst(compile_const_pointer(NULL),
+ RubyObjPtrTy, "", bb);
+ }
+ else {
+ argv = new AllocaInst(RubyObjTy, ConstantInt::get(Int32Ty, argc),
+ "", bb);
+ int off = 0;
+ if (is_block) {
+ // Skip block literal argument.
+ off++;
+ arg++;
+ }
+ for (int i = 0; i < argc; i++) {
+ Value *index = ConstantInt::get(Int32Ty, i);
+ Value *aslot = GetElementPtrInst::Create(argv, index, "", bb);
+ Value *rval = compile_conversion_to_ruby(arg_ctypes[i].c_str(),
+ arg_types[i + off], arg++);
+ new StoreInst(rval, aslot, bb);
+ }
+ }
+
+ // VALUE rb_proc_check_and_call(
+ // VALUE self, int argc, VALUE *argv
+ // )
+ Function *proc_call_f =
+ cast<Function>(module->getOrInsertFunction(
+ "rb_proc_check_and_call",
+ RubyObjTy,
+ RubyObjTy, Int32Ty, RubyObjPtrTy, NULL));
+
+ Value *args[] = {
+ new LoadInst(proc_gvar, "", bb),
+ ConstantInt::get(Int32Ty, argc),
+ argv
+ };
+ Value *ret_val = compile_protected_call(proc_call_f, args,
+ args + 3);
+
+ if (ret_type != VoidTy) {
+ GetFirstType(type + 1, buf, buf_len);
+ ret_val = compile_conversion_to_c(buf, ret_val,
+ new AllocaInst(ret_type, "", bb));
+ ReturnInst::Create(context, ret_val, bb);
+ }
+ else {
+ ReturnInst::Create(context, bb);
+ }
+
+ bb = oldbb;
+ free(buf);
+ return new BitCastInst(f, PtrTy, "", bb);
+}
+
+Value *
RoxorCompiler::compile_conversion_to_c(const char *type, Value *val,
- Value *slot)
+ Value *slot)
{
type = SkipTypeModifiers(type);
@@ -5327,96 +5437,28 @@
}
break;
- case _MR_C_FPTR_B:
+ case _MR_C_LAMBDA_B:
{
- GlobalVariable *proc_gvar =
- new GlobalVariable(*RoxorCompiler::module,
- RubyObjTy, false, GlobalValue::InternalLinkage,
- nilVal, "");
- new StoreInst(val, proc_gvar, bb);
+ type++;
+ const bool is_block = *type == _MR_C_LAMBDA_BLOCK;
- const size_t buf_len = strlen(type + 1) + 1;
- assert(buf_len > 1);
- char *buf = (char *)malloc(buf_len);
-
- const char *p = GetFirstType(type + 1, buf, buf_len);
- const Type *ret_type = convert_type(buf);
- int argc = 0;
-
- std::vector<std::string> arg_ctypes;
- std::vector<const Type *> arg_types;
- while (*p != _MR_C_FPTR_E) {
- p = GetFirstType(p, buf, buf_len);
- arg_ctypes.push_back(std::string(buf));
- arg_types.push_back(convert_type(buf));
- argc++;
+ Value *funcptr = compile_lambda_to_funcptr(type, val, slot,
+ is_block);
+ if (!is_block) {
+ // A pure function pointer, let's pass it.
+ return funcptr;
}
- FunctionType *ft = FunctionType::get(ret_type, arg_types,
- false);
- // ret_type stub(arg1, arg2, ...)
- // {
- // VALUE *argv = alloc(argc);
- // argv[0] = arg1;
- // argv[1] = arg2;
- // return rb_proc_check_and_call(procval, argc, argv);
- // }
- Function *f = cast<Function>(module->getOrInsertFunction("",
- ft));
-
- BasicBlock *oldbb = bb;
- bb = BasicBlock::Create(context, "EntryBlock", f);
-
- Function::arg_iterator arg = f->arg_begin();
-
- Value *argv;
- if (argc == 0) {
- argv = new BitCastInst(compile_const_pointer(NULL),
- RubyObjPtrTy, "", bb);
- }
- else {
- argv = new AllocaInst(RubyObjTy,
- ConstantInt::get(Int32Ty, argc), "", bb);
- for (int i = 0; i < argc; i++) {
- Value *index = ConstantInt::get(Int32Ty, i);
- Value *aslot = GetElementPtrInst::Create(argv, index,
- "", bb);
- Value *rval = compile_conversion_to_ruby(
- arg_ctypes[i].c_str(), arg_types[i], arg++);
- new StoreInst(rval, aslot, bb);
- }
- }
-
- // VALUE rb_proc_check_and_call(
- // VALUE self, int argc, VALUE *argv
- // )
- Function *proc_call_f =
- cast<Function>(module->getOrInsertFunction(
- "rb_proc_check_and_call",
- RubyObjTy,
- RubyObjTy, Int32Ty, RubyObjPtrTy, NULL));
-
+ // A C-level block. We allocate on the stack the literal
+ // structure following the ABI, initialize it then pass
+ // a pointer to it.
+ Value *block_lit = new AllocaInst(BlockLiteralTy, "", bb);
Value *args[] = {
- new LoadInst(proc_gvar, "", bb),
- ConstantInt::get(Int32Ty, argc),
- argv
+ block_lit,
+ funcptr
};
- Value *ret_val = compile_protected_call(proc_call_f, args,
- args + 3);
-
- if (ret_type != VoidTy) {
- GetFirstType(type + 1, buf, buf_len);
- ret_val = compile_conversion_to_c(buf, ret_val,
- new AllocaInst(ret_type, "", bb));
- ReturnInst::Create(context, ret_val, bb);
- }
- else {
- ReturnInst::Create(context, bb);
- }
-
- bb = oldbb;
- free(buf);
- return new BitCastInst(f, PtrTy, "", bb);
+ CallInst::Create(initBlockFunc, args, args + 2, "", bb);
+ return block_lit;
}
break;
@@ -5769,7 +5811,7 @@
}
break;
- case _MR_C_FPTR_B:
+ case _MR_C_LAMBDA_B:
return PtrTy;
case _C_STRUCT_B:
Modified: MacRuby/trunk/spec/macruby/language/objc_method_spec.rb (4494 => 4495)
--- MacRuby/trunk/spec/macruby/language/objc_method_spec.rb 2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/spec/macruby/language/objc_method_spec.rb 2010-09-08 00:53:10 UTC (rev 4495)
@@ -744,7 +744,7 @@
end
describe "A Proc object" do
- it "can be used when a BridgeSupport Obj-C method takes a function pointer as an argument" do
+ it "can be used when an Objective-C method takes a function pointer as an argument" do
framework 'Foundation'
array = [1, 42, 6, 2, 3]
@@ -760,7 +760,7 @@
lambda { array.sortedArrayUsingFunction(too_many_args_proc, context:nil) }.should raise_error(ArgumentError)
end
- it "can be used when a BridgeSupport C function takes a function pointer as an argument" do
+ it "can be used when a C function takes a function pointer as an argument" do
functionMultiplicatingByTwoViaFctPtr(42, Proc.new { |x| x * 2 }).should == 84
functionMultiplicatingByTwoViaFctPtr(42, ->(x) { x * 2 }).should == 84
@@ -769,6 +769,16 @@
lambda { functionMultiplicatingByTwoViaFctPtr(42, Proc.new { 1 }) }.should raise_error(ArgumentError)
lambda { functionMultiplicatingByTwoViaFctPtr(42, Proc.new { |x, y| x * y }) }.should raise_error(ArgumentError)
end
+
+ it "can be used when an Objective-C method takes a Block as argument" do
+ ary = ['zero', 'one', 'two', 'three', 'four']
+ res = []
+ ary.enumerateObjectsUsingBlock(Proc.new { |obj, idx, stop|
+ res << obj
+ stop.assign(true) if idx == 2
+ })
+ res.should == ['zero', 'one', 'two']
+ end
end
describe "Ignored Obj-C selectors" do