Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

support for C-level blocks (note: this requires a not-yet-released Br…

…idgeSupport with special annotations for blocks)

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@4495 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information...
commit 112362a8157540343fe374d45908e2ce3ba1b515 1 parent c02ab1a
Laurent Sansonetti authored
View
37 bs.c
@@ -319,10 +319,10 @@ undecorate_struct_type(const char *src, char *dest, size_t dest_len,
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 @@ bs_parser_parse(bs_parser_t *parser, const char *path,
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 @@ bs_parser_parse(bs_parser_t *parser, const char *path,
}
// 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) {
View
12 bs.h
@@ -36,9 +36,15 @@ extern "C" {
#include <CoreFoundation/CoreFoundation.h>
#include <objc/runtime.h>
-/* Extend objc/runtime.h and add function pointers tokens */
-#define _MR_C_FPTR_B '<'
-#define _MR_C_FPTR_E '>'
+/* Extend objc/runtime.h and add function pointers and blocks tokens.
+ * A pointer to `void foo (int, char)' will be encoded as <^vic>.
+ * A `void (^)(int, char)' block will be encoded as <@vic>.
+ */
+
+#define _MR_C_LAMBDA_B '<'
+#define _MR_C_LAMBDA_FUNCPTR '^'
+#define _MR_C_LAMBDA_BLOCK '@'
+#define _MR_C_LAMBDA_E '>'
/* Attribute and element representations.
* See BridgeSupport(5) for more information.
View
216 compiler.cpp
@@ -267,6 +267,7 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode)
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 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode)
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 @@ RoxorCompiler::compile_get_cptr(Value *val, const char *type, Value *slot)
}
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 @@ RoxorCompiler::compile_conversion_to_c(const char *type, Value *val,
}
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);
-
- 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++;
- }
- 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);
- }
+ type++;
+ const bool is_block = *type == _MR_C_LAMBDA_BLOCK;
+
+ Value *funcptr = compile_lambda_to_funcptr(type, val, slot,
+ is_block);
+ if (!is_block) {
+ // A pure function pointer, let's pass it.
+ return funcptr;
}
- // 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 @@ RoxorCompiler::convert_type(const char *type)
}
break;
- case _MR_C_FPTR_B:
+ case _MR_C_LAMBDA_B:
return PtrTy;
case _C_STRUCT_B:
View
4 compiler.h
@@ -254,6 +254,7 @@ class RoxorCompiler {
Function *rvalToDoubleFunc;
Function *rvalToSelFunc;
Function *rvalToCharPtrFunc;
+ Function *initBlockFunc;
Constant *zeroVal;
Constant *oneVal;
@@ -283,6 +284,7 @@ class RoxorCompiler {
const Type *IntTy;
const PointerType *Int32PtrTy;
const Type *BitTy;
+ const Type *BlockLiteralTy;
unsigned dbg_mdkind;
@@ -422,6 +424,8 @@ class RoxorCompiler {
void compile_set_struct(Value *rcv, int field, Value *val);
Value *compile_xmalloc(size_t len);
+ Value *compile_lambda_to_funcptr(const char *type, Value *val,
+ Value *slot, bool is_block);
Value *compile_conversion_to_c(const char *type, Value *val,
Value *slot);
Value *compile_conversion_to_ruby(const char *type,
View
31 kernel.c
@@ -1012,3 +1012,34 @@ vm_get_special(char code)
}
return val;
}
+
+// Support for C-level blocks.
+// Following the ABI specifications as documented in the
+// BlockImplementation.txt file of LLVM.
+
+struct ruby_block_descriptor {
+ unsigned long int reserved;
+ unsigned long int block_size;
+};
+
+struct ruby_block_literal {
+ void *isa;
+ int flags;
+ int reserved;
+ void *imp;
+ struct ruby_block_descriptor *descriptor;
+};
+
+static struct ruby_block_descriptor ruby_block_descriptor_value = {
+ 0, sizeof(struct ruby_block_literal)
+};
+
+PRIMITIVE void
+vm_init_c_block(struct ruby_block_literal *b, void *imp)
+{
+ b->isa = &_NSConcreteStackBlock;
+ b->flags = (1 << 29);
+ b->reserved = 0;
+ b->imp = imp;
+ b->descriptor = &ruby_block_descriptor_value;
+}
View
6 objc.h
@@ -160,9 +160,9 @@ SkipFirstType(const char *type)
case _C_UNION_B:
return type + SubtypeUntil (type, _C_UNION_E) + 1;
- /* Function pointers */
- case _MR_C_FPTR_B:
- return type + SubtypeUntil (type, _MR_C_FPTR_E) + 1;
+ /* lambdas */
+ case _MR_C_LAMBDA_B:
+ return type + SubtypeUntil (type, _MR_C_LAMBDA_E) + 1;
/* basic types */
default:
View
14 spec/macruby/language/objc_method_spec.rb
@@ -744,7 +744,7 @@ def @o.a
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 @@ def @o.a
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 @@ def @o.a
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
Please sign in to comment.
Something went wrong with that request. Please try again.