Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ ref. https://docs.ruby-lang.org/ja/latest/function/index.html
* [ ] `rb_free_generic_ivar`
* [ ] `rb_frozen_class_p`
* [ ] `rb_funcall`
* [ ] `rb_funcall2`
* [x] `rb_funcall2`
* [ ] `rb_funcall3`
* [ ] `rb_gc`
* [ ] `rb_gc_call_finalizer_at_exit`
Expand Down
6 changes: 6 additions & 0 deletions _scripts/dump_ruby_c_functions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def snake_to_camel(str)
str.split("_").map(&:capitalize).join
end

# Cast C type to cgo type. (Used in wrapper function)
# @param typename [String]
# @return [String]
def cast_to_cgo_type(typename)
Expand All @@ -185,13 +186,16 @@ def cast_to_cgo_type(typename)
return "C.uint"
when "char*"
return "string2Char"
when "VALUE*"
return "toCValueArray"
when /^VALUE\s*\(\*func\)\s*\(ANYARGS\)$/
return "toFunctionPointer"
end

"C.#{typename}"
end

# Convert C type to Go type. (used in wrapper function args and return type)
# @param typename [String]
# @return [String]
def ruby_c_type_to_go_type(typename)
Expand All @@ -200,6 +204,8 @@ def ruby_c_type_to_go_type(typename)
return "uint"
when "char*", "const char*"
return "string"
when "VALUE*"
return "[]VALUE"
when /^VALUE\s*\(\*func\)\s*\(ANYARGS\)$/
return "unsafe.Pointer"
when /^[A-Z]+$/, "int"
Expand Down
22 changes: 22 additions & 0 deletions ruby-internal-eval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ruby

/*
#include "ruby.h"
*/
import "C"

// c.f. https://github.com/ruby/ruby/blob/master/include/ruby/internal/eval.h

// RbFuncallv calls `rb_funcallv` in C
//
// Original definition is following
//
// VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
func RbFuncallv(recv VALUE, mid ID, argc int, argv []VALUE) VALUE {
return VALUE(C.rb_funcallv(C.VALUE(recv), C.ID(mid), C.int(argc), toCValueArray(argv)))
}

// RbFuncall2 is alias to [RbFuncallv]
func RbFuncall2(recv VALUE, mid ID, argc int, argv []VALUE) VALUE {
return RbFuncallv(recv, mid, argc, argv)
}
21 changes: 21 additions & 0 deletions testdata/dummy/ext/dummy/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ package main
VALUE rb_dummy_sum(VALUE self, VALUE a, VALUE b);
VALUE rb_dummy_with_block(VALUE self, VALUE arg);
VALUE rb_dummy_hello(VALUE self, VALUE name);
VALUE rb_dummy_round_num(VALUE self, VALUE num, VALUE ndigits);
VALUE rb_dummy_to_string(VALUE self, VALUE source);

VALUE rb_dummy_unit_kilobyte(VALUE self);
*/
import "C"
Expand Down Expand Up @@ -41,6 +44,22 @@ func rb_dummy_hello(_ C.VALUE, name C.VALUE) C.VALUE {
return C.VALUE(ruby.String2Value(result))
}

//export rb_dummy_round_num
func rb_dummy_round_num(_ C.VALUE, num C.VALUE, ndigits C.VALUE) C.VALUE {
// Call Integer#round
result := ruby.RbFuncall2(ruby.VALUE(num), ruby.RbIntern("round"), 1, []ruby.VALUE{ruby.VALUE(ndigits)})

return C.VALUE(result)
}

//export rb_dummy_to_string
func rb_dummy_to_string(_ C.VALUE, source C.VALUE) C.VALUE {
// Call Object#to_s
result := ruby.RbFuncall2(ruby.VALUE(source), ruby.RbIntern("to_s"), 0, []ruby.VALUE{})

return C.VALUE(result)
}

//export rb_dummy_unit_kilobyte
func rb_dummy_unit_kilobyte(self C.VALUE) C.VALUE {
sourceID := ruby.RbIntern("@source")
Expand All @@ -60,6 +79,8 @@ func Init_dummy() {
ruby.RbDefineSingletonMethod(rb_mDummy, "sum", C.rb_dummy_sum, 2)
ruby.RbDefineSingletonMethod(rb_mDummy, "with_block", C.rb_dummy_with_block, 1)
ruby.RbDefineSingletonMethod(rb_mDummy, "hello", C.rb_dummy_hello, 1)
ruby.RbDefineSingletonMethod(rb_mDummy, "round_num", C.rb_dummy_round_num, 2)
ruby.RbDefineSingletonMethod(rb_mDummy, "to_string", C.rb_dummy_to_string, 1)

// Create Dummy::InnerClass class
ruby.RbDefineClassUnder(rb_mDummy, "InnerClass", ruby.VALUE(C.rb_cObject))
Expand Down
4 changes: 4 additions & 0 deletions testdata/dummy/sig/dummy.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ module Dummy

def self.hello: (String name) -> String

def self.round_num: (Integer num, Integer ndigits) -> Integer

def self.to_string: (untyped source) -> String

class InnerClass
end

Expand Down
14 changes: 14 additions & 0 deletions testdata/dummy/spec/dummy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,18 @@
expect(actual).to eq "Hello, sue445"
end
end

describe ".round_num" do
it "works" do
actual = Dummy.round_num(15, -1)
expect(actual).to eq 20
end
end

describe ".to_string" do
it "works" do
actual = Dummy.to_string(123)
expect(actual).to eq "123"
end
end
end
9 changes: 9 additions & 0 deletions wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,12 @@ func string2Value(str string) C.VALUE {
func toFunctionPointer(fun unsafe.Pointer) *[0]byte {
return (*[0]byte)(fun)
}

// toCValueArray convert from `[]ruby.VALUE` to `*C.VALUE`. (for internal use within package)
func toCValueArray(values []VALUE) *C.VALUE {
if len(values) == 0 {
return (*C.VALUE)(unsafe.Pointer(nil))
}

return (*C.VALUE)(unsafe.Pointer(&values[0]))
}