[macruby-changes] [4495] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Sep 7 17:53:11 PDT 2010


Revision: 4495
          http://trac.macosforge.org/projects/ruby/changeset/4495
Author:   lsansonetti at apple.com
Date:     2010-09-07 17:53:10 -0700 (Tue, 07 Sep 2010)
Log Message:
-----------
support for C-level blocks (note: this requires a not-yet-released BridgeSupport with special annotations for blocks)

Modified Paths:
--------------
    MacRuby/trunk/bs.c
    MacRuby/trunk/bs.h
    MacRuby/trunk/compiler.cpp
    MacRuby/trunk/compiler.h
    MacRuby/trunk/kernel.c
    MacRuby/trunk/objc.h
    MacRuby/trunk/spec/macruby/language/objc_method_spec.rb

Modified: MacRuby/trunk/bs.c
===================================================================
--- 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/bs.h
===================================================================
--- MacRuby/trunk/bs.h	2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/bs.h	2010-09-08 00:53:10 UTC (rev 4495)
@@ -36,10 +36,16 @@
 #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.
  */

Modified: MacRuby/trunk/compiler.cpp
===================================================================
--- 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/compiler.h
===================================================================
--- MacRuby/trunk/compiler.h	2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/compiler.h	2010-09-08 00:53:10 UTC (rev 4495)
@@ -254,6 +254,7 @@
 	Function *rvalToDoubleFunc;
 	Function *rvalToSelFunc;
 	Function *rvalToCharPtrFunc;
+	Function *initBlockFunc;
 
 	Constant *zeroVal;
 	Constant *oneVal;
@@ -283,6 +284,7 @@
 	const Type *IntTy;
 	const PointerType *Int32PtrTy;
 	const Type *BitTy;
+	const Type *BlockLiteralTy;
 
 	unsigned dbg_mdkind;
 
@@ -422,6 +424,8 @@
 	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,

Modified: MacRuby/trunk/kernel.c
===================================================================
--- MacRuby/trunk/kernel.c	2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/kernel.c	2010-09-08 00:53:10 UTC (rev 4495)
@@ -1012,3 +1012,34 @@
     }
     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;
+}

Modified: MacRuby/trunk/objc.h
===================================================================
--- MacRuby/trunk/objc.h	2010-09-04 01:55:20 UTC (rev 4494)
+++ MacRuby/trunk/objc.h	2010-09-08 00:53:10 UTC (rev 4495)
@@ -160,9 +160,9 @@
             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:

Modified: MacRuby/trunk/spec/macruby/language/objc_method_spec.rb
===================================================================
--- 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100907/04e27dea/attachment-0001.html>


More information about the macruby-changes mailing list