[macruby-changes] [3847] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Mon Mar 22 23:23:18 PDT 2010


Revision: 3847
          http://trac.macosforge.org/projects/ruby/changeset/3847
Author:   martinlagardette at apple.com
Date:     2010-03-22 23:23:16 -0700 (Mon, 22 Mar 2010)
Log Message:
-----------
Add support for Proc as function pointers (BridgeSupport only)

 - Procs and lambdas can now be passed to any C function / Obj-C method as function pointers (a BridgeSupport file describing the prototype is necessary)
 - Modified `bs.c` to return `<types>` instead of `^?` for the function pointers type
 - Fixed a bug in `bs.c` where it did not parse BridgeSupport methods/functions with function pointers arguments correctly
 - Added some new specs for the new feature
 - Fixes #529

Modified Paths:
--------------
    MacRuby/trunk/bs.c
    MacRuby/trunk/bs.h
    MacRuby/trunk/compiler.cpp
    MacRuby/trunk/objc.h
    MacRuby/trunk/proc.c
    MacRuby/trunk/spec/macruby/fixtures/method.bridgesupport
    MacRuby/trunk/spec/macruby/fixtures/method.m
    MacRuby/trunk/spec/macruby/language/objc_method_spec.rb

Modified: MacRuby/trunk/bs.c
===================================================================
--- MacRuby/trunk/bs.c	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/bs.c	2010-03-23 06:23:16 UTC (rev 3847)
@@ -436,6 +436,7 @@
   unsigned int i;
 #define MAX_ARGS 128
   bs_element_arg_t args[MAX_ARGS];
+  bs_element_arg_t fptr_args[MAX_ARGS];
   char *protocol_name = NULL;
   int func_ptr_arg_depth;
   bs_element_function_pointer_t *func_ptr;
@@ -855,7 +856,9 @@
                      "for method '%s'", MAX_ARGS, (char *)method->name);
             } 
 
-            bs_arg = &args[(*argc)++];
+	    bs_element_arg_t *args_from =
+		(func_ptr == NULL ? args : fptr_args);
+	    bs_arg = &args_from[(*argc)++];
 
             if (method != NULL && func_ptr == NULL) {
               char *index = get_attribute(reader, "index");
@@ -1056,21 +1059,49 @@
         {
           if (func_ptr != NULL 
               && func_ptr_arg_depth == xmlTextReaderDepth(reader)) {
+
+	      char tmp_type[1026]; // 2 less to fit <, and >
+	      char new_type[1028];
+
+	      // return type
+	      strlcpy(tmp_type, func_ptr->retval->type, sizeof(tmp_type));
+	      free(func_ptr->retval->type);
+	      // args
+	      for (i = 0; i < func_ptr->args_count; i++) {
+		  strlcat(tmp_type, fptr_args[i].type, sizeof(tmp_type));
+		  free(fptr_args[i].type);
+	      }
+	      snprintf(new_type, sizeof(new_type),
+		       "%c%s%c", _C_FPTR_B, tmp_type, _C_FPTR_E);
+
+	      if (atom->val == BS_XML_RETVAL) {
+		  bs_element_retval_t *retval =
+		      func != NULL ? func->retval : method->retval;
+		  free(retval->type);
+		  retval->type = strdup(new_type);
+	      }
+	      else {
+		  unsigned args_count = 
+		      func != NULL ? func->args_count : method->args_count;
+		  bs_element_arg_t *arg = &args[args_count - 1];
+		  free(arg->type);
+		  arg->type = strdup(new_type);
+	      }
             
-            if (func_ptr->args_count > 0) {
-              size_t len;
+	      if (func_ptr->args_count > 0) {
+		  size_t len;
       
-              len = sizeof(bs_element_arg_t) * func_ptr->args_count;
-              func_ptr->args = (bs_element_arg_t *)malloc(len);
-              ASSERT_ALLOC(func_ptr->args);
-              memcpy(func_ptr->args, args, len);
-            }
-            else {
-              func_ptr->args = NULL;
-            }
+		  len = sizeof(bs_element_arg_t) * func_ptr->args_count;
+		  func_ptr->args = (bs_element_arg_t *)malloc(len);
+		  ASSERT_ALLOC(func_ptr->args);
+		  memcpy(func_ptr->args, fptr_args, len);
+	      }
+	      else {
+		  func_ptr->args = NULL;
+	      }
                         
-            func_ptr = NULL;
-            func_ptr_arg_depth = -1;
+	      func_ptr = NULL;
+	      func_ptr_arg_depth = -1;
           }
           break;
         }

Modified: MacRuby/trunk/bs.h
===================================================================
--- MacRuby/trunk/bs.h	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/bs.h	2010-03-23 06:23:16 UTC (rev 3847)
@@ -36,6 +36,10 @@
 #include <CoreFoundation/CoreFoundation.h>
 #include <objc/runtime.h>
 
+/* Extend objc/runtime.h and add function pointers tokens */
+#define _C_FPTR_B '<'
+#define _C_FPTR_E '>'
+
 /* Attribute and element representations.
  * See BridgeSupport(5) for more information.
  */

Modified: MacRuby/trunk/compiler.cpp
===================================================================
--- MacRuby/trunk/compiler.cpp	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/compiler.cpp	2010-03-23 06:23:16 UTC (rev 3847)
@@ -25,6 +25,7 @@
 #include "version.h"
 #include "encoding.h"
 #include "re.h"
+#include "bs.h"
 
 extern "C" const char *ruby_node_name(int node);
 
@@ -6492,6 +6493,91 @@
 	    }
 	    break;
 
+        case _C_FPTR_B:
+	    {
+		GlobalVariable *proc_gvar = new GlobalVariable(*RoxorCompiler::module,
+			RubyObjTy, false, GlobalValue::InternalLinkage, nilVal, "");
+		new StoreInst(val, proc_gvar, bb);
+
+		char buf[100];
+		const char *p = GetFirstType(type + 1, buf, sizeof(buf));
+		const Type *ret_type = convert_type(buf);
+		int argc = 0;
+
+		std::vector<std::string> arg_ctypes;
+		std::vector<const Type *> arg_types;
+		while (*p != _C_FPTR_E) {
+		    p = GetFirstType(p, buf, sizeof(buf));
+		    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);
+		    }
+		}
+
+		// 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));
+		std::vector<Value *> params;
+		params.push_back(new LoadInst(proc_gvar, "", bb));
+		params.push_back(ConstantInt::get(Int32Ty, argc));
+		params.push_back(argv);
+		Value *ret_val = compile_protected_call(proc_call_f, params);
+
+		if (ret_type != VoidTy) {
+		    GetFirstType(type + 1, buf, sizeof(buf));
+		    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;
+		Value *ret = new BitCastInst(f, PtrTy, "", bb);
+
+		return ret;
+	    }
+	    break;
+
 	case _C_PTR:
 	    {
 		rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_opaque(type);
@@ -6868,6 +6954,9 @@
 	case _C_ULNG_LNG:
 	    return Int64Ty;
 
+	case _C_FPTR_B:
+	    return PtrTy;
+
 	case _C_STRUCT_B:
 	    rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_struct(type);
 	    if (bs_boxed != NULL) {

Modified: MacRuby/trunk/objc.h
===================================================================
--- MacRuby/trunk/objc.h	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/objc.h	2010-03-23 06:23:16 UTC (rev 3847)
@@ -83,8 +83,8 @@
 
 	switch (*type)
 	{
-	    case ']': case '}': case ')': level--; break;
-	    case '[': case '{': case '(': level += 1; break;
+	    case ']': case '}': case ')': case '>': level--; break;
+	    case '[': case '{': case '(': case '<': level += 1; break;
 	}
 
 	type += 1;
@@ -149,6 +149,10 @@
             case '(':
                 return type + SubtypeUntil (type, ')') + 1;
 
+                /* Function pointers */
+            case '<':
+                return type + SubtypeUntil (type, '>') + 1;
+
                 /* basic types */
             default:
                 return type;

Modified: MacRuby/trunk/proc.c
===================================================================
--- MacRuby/trunk/proc.c	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/proc.c	2010-03-23 06:23:16 UTC (rev 3847)
@@ -451,6 +451,25 @@
     return proc_call(self, 0, argc, argv);
 }
 
+VALUE
+rb_proc_check_and_call(VALUE proc, int argc, VALUE *argv)
+{
+    VALUE tmp = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
+    if (NIL_P(tmp)) {
+        rb_raise(rb_eTypeError,
+		"wrong type %s (expected Proc)",
+		rb_obj_classname(proc));
+    }
+    proc = tmp;
+
+    const int arity = rb_proc_arity(proc);
+    if (arity != argc) {
+	rb_raise(rb_eArgError, "expected Proc with %d arguments (got %d)",
+		argc, arity);
+    }
+    return proc_call(proc, 0, argc, argv);
+}
+
 /*
  *  call-seq:
  *     prc.arity -> fixnum

Modified: MacRuby/trunk/spec/macruby/fixtures/method.bridgesupport
===================================================================
--- MacRuby/trunk/spec/macruby/fixtures/method.bridgesupport	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/spec/macruby/fixtures/method.bridgesupport	2010-03-23 06:23:16 UTC (rev 3847)
@@ -28,4 +28,12 @@
     <method type="i@:i" selector="informalProtocolMethod1:"/>
     <method type="B@:ii" selector="informalProtocolMethod2:withValue:"/>
   </informal_protocol>
+  <function name="functionMultiplicatingByTwoViaFctPtr">
+    <arg name="number" declared_type="CFNumberRef" type="^{__CFNumber=}"/>
+    <arg name="multiplier" function_pointer="true" type="^?" type_modifier="n">
+      <arg declared_type="CFNumberRef" type="^{__CFNumber=}"/>
+      <retval declared_type="CFNumberRef" type="^{__CFNumber=}"/>
+    </arg>
+    <retval declared_type="CFNumberRef" type="^{__CFNumber=}"/>
+  </function>
 </signatures>

Modified: MacRuby/trunk/spec/macruby/fixtures/method.m
===================================================================
--- MacRuby/trunk/spec/macruby/fixtures/method.m	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/spec/macruby/fixtures/method.m	2010-03-23 06:23:16 UTC (rev 3847)
@@ -740,6 +740,11 @@
 
 @end
 
+CFNumberRef functionMultiplicatingByTwoViaFctPtr(CFNumberRef nb, CFNumberRef (*multiplier)(CFNumberRef))
+{
+    return (*multiplier)(nb);
+}
+
 void
 Init_method(void)
 {

Modified: MacRuby/trunk/spec/macruby/language/objc_method_spec.rb
===================================================================
--- MacRuby/trunk/spec/macruby/language/objc_method_spec.rb	2010-03-23 05:23:05 UTC (rev 3846)
+++ MacRuby/trunk/spec/macruby/language/objc_method_spec.rb	2010-03-23 06:23:16 UTC (rev 3847)
@@ -702,3 +702,31 @@
     @o['foo'].should == 'ok'
   end
 end
+
+describe "A Proc object" do
+  it "can be used when a BridgeSupport Obj-C method takes a function pointer as an argument" do
+    framework 'Foundation'
+
+    array = [1, 42, 6, 2, 3]
+    proc = Proc.new { |a, b, _| a <=> b }
+    too_few_args_proc = Proc.new { |a| a <=> b }
+    too_many_args_proc = Proc.new { |a, b, c, d| a <=> b }
+
+    array.sortedArrayUsingFunction(proc, context:nil).should == [1, 2, 3, 6, 42]
+    
+    lambda { array.sortedArrayUsingFunction(1, context:nil) }.should raise_error(TypeError)
+    
+    lambda { array.sortedArrayUsingFunction(too_few_args_proc, context:nil) }.should raise_error(ArgumentError)
+    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
+    functionMultiplicatingByTwoViaFctPtr(42, Proc.new { |x| x * 2 }).should == 84
+    functionMultiplicatingByTwoViaFctPtr(42, ->(x) { x * 2 }).should == 84
+
+    lambda { functionMultiplicatingByTwoViaFctPtr(42, 1) }.should raise_error(TypeError)
+
+    lambda { functionMultiplicatingByTwoViaFctPtr(42, Proc.new { 1 }) }.should raise_error(ArgumentError)
+    lambda { functionMultiplicatingByTwoViaFctPtr(42, Proc.new { |x, y| x * y }) }.should raise_error(ArgumentError)
+  end
+end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100322/9f378ee8/attachment-0001.html>


More information about the macruby-changes mailing list