[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