Skip to content
Draft
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
60 changes: 43 additions & 17 deletions RbCall.jl/src/RbCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,54 @@ export _refcnt, _incref, _decref, gc_guard_references

#########################################################################

const gc_guard_references = Dict{Any,Clong}()

function _refcnt(value::Any)
return get(gc_guard_references, value , 0)
end

function _incref(value::Any)
gc_guard_references[value] = _refcnt(value) + 1
return value
if sizeof(Clong) == sizeof(Ptr{Cvoid})
const VALUE = Culong
const SVALUE = Clong
const ID = Culong
elseif sizeof(Clonglong) == sizeof(Ptr{Cvoid})
const VALUE = Culonglong
const SVALUE = Clonglong
const ID = Culonglong
else
error("ruby requires sizeof(Ptr{Cvoid}) == sizeof(Clong) or sizeof(Clonglong) to be compiled.")
end

function _decref(value::Any)
if haskey(gc_guard_references, value)
new_count = _refcnt(value) - 1
if new_count == 0
delete!(gc_guard_references, value)
else
gc_guard_references[value] = new_count
end
mutable struct RubyObject
o::VALUE
function RubyObject(o::VALUE)
ro = new(o)
gc_guard_register(ro)
finalizer(gc_guard_unregister, ro)
return ro
end
end

#########################################################################

const RUBY_FIXNUM_MAX = typemax(Clong) >> 1
const RUBY_FIXNUM_MIN = typemin(Clong) >> 1

RB_FIXNUM_P(v::VALUE) = (x & RUBY_FIXNUM_FLAG) != 0
RB_POSFIXABLE(x::Integer) = x < RUBY_FIXNUM_MAX+1
RB_NEGFIXABLE(x::Integer) = x >= RUBY_FIXNUM_MIN
RB_FIXABLE(x::Integer) = RB_POSFIXABLE(x) && RB_NEGFIXABLE(x)

RUBY_Qfalse = 0x00
RUBY_Qtrue = 0x14
RUBY_Qnil = 0x08
RUBY_Qundef = 0x34

RUBY_IMMEDIATE_MAXK = 0x07
RUBY_FIXNUM_FLAG = 0x01
RUBY_FLONUM_MASK = 0x03
RUBY_FLONUM_FLAG = 0x02
RUBY_SYMBOL_FLAG = 0x0c

RUBY_SPECIAL_SHIFT = 8

#########################################################################

include("callback.jl")
include("gc.jl")

end # module RbCall
2 changes: 2 additions & 0 deletions RbCall.jl/src/callback.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
function rbfunction(f, argtypes...; kwtypes...)
end
20 changes: 20 additions & 0 deletions RbCall.jl/src/conversion.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function julia_object_wrapper_new(value::Any)
rb_cObject = cglobal(:rb_cObject, Ptr{Cvoid})
ptr = ccall(:rb_class_new_instance, Ptr{Cvoid}, (Cint, Ptr{Cvoid}, Ptr{Cvoid}), 0, C_NULL, rb_cObject)
obj = RubyObject(ptr)

end

function convert(::VALUE, x::Integer)
if RB_FIXABLE(x)
# to Fixnum
return RB_INT2FIX(x)
elseif typemin(Clonglong) <= x < 0
return ccall(:rb_ll2inum, VALUE, (Clonglong,), Clonglong(x))
elseif 0 <= x <= typemax(Culonglong)
return ccall(:rb_ull2big, VALUE, (Culonglong,), Culonglong(x))
else
return RUBY_Qnil
# TODO
end
end
59 changes: 59 additions & 0 deletions RbCall.jl/src/gc.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
### GC Guard for Julia objects

const gc_guard_references = Dict{Any,Clong}()

function _refcnt(value::Any)
return get(gc_guard_references, value , 0)
end

function _incref(value::Any)
gc_guard_references[value] = _refcnt(value) + 1
return value
end

function _decref(value::Any)
if haskey(gc_guard_references, value)
new_count = _refcnt(value) - 1
if new_count == 0
delete!(gc_guard_references, value)
else
gc_guard_references[value] = new_count
end
end
end

### GC Guard for Ruby objects

gc_guard_table = VALUE(0)

function init_gc_guard_table()
if gc_guard_table != 0
return
end

gc_guard_table = ccall(:rb_hash_new, VALUE, ())
mJulia = ccall(:rb_define_module, VALUE, (Cstring,), "Julia")
id_gc_guard_table = ccall(:rb_intern, ID, (Cstring,), "@gc_guard_table")
ccall(:rb_ivar_set, VALUE, (VALUE, ID, VALUE), mJulia, id_gc_guard_table, gc_guard_table)
end

function gc_guard_register_(ro::RubyObject)
cnt = convert(Integer, ccall(:rb_hash_lookup2, VALUE, (VALUE, VALUE, VALUE), gc_guard_table, ro.o, INT2NUM(0)))
ccall(:rb_hash_aset, VALUE, (VALUE, VALUE, VALUE), gc_guard_table, ro.o, INT2NUM(cnt + 1))
end

function gc_guard_register(ro::RubyObject)
init_gc_guard_table()
global gc_guard_register
gc_guard_register = gc_guard_register_
gc_guard_register_(ro)
end

function gc_guard_unregister(ro::RubyObject)
cnt = convert(Integer, ccall(:rb_hash_lookup2, VALUE, (VALUE, VALUE, VALUE), gc_guard_table, ro.o, INT2NUM(0)))
if cnt <= 1
ccall(:rb_hash_delete, VALUE, (VALUE, VALUE), gc_guard_table, ro.o)
else
ccall(:rb_hash_aset, VALUE, (VALUE, VALUE, VALUE), gc_guard_table, ro.o, INT2NUM(cnt - 1))
end
end
3 changes: 3 additions & 0 deletions ext/julia/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ struct rbjl_api_table {
jl_datatype_t **jl_float16_type;
jl_datatype_t **jl_float32_type;
jl_datatype_t **jl_float64_type;
jl_datatype_t **jl_sym_type;
jl_datatype_t **jl_method_instance_type;
jl_datatype_t **jl_method_type;
jl_datatype_t **jl_module_type;

jl_module_t **jl_main_module;
Expand Down
10 changes: 8 additions & 2 deletions ext/julia/libjulia.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ init_api_table(VALUE handle)
INIT_API_TABLE_ENTRY(jl_float16_type);
INIT_API_TABLE_ENTRY(jl_float32_type);
INIT_API_TABLE_ENTRY(jl_float64_type);
INIT_API_TABLE_ENTRY(jl_sym_type);
INIT_API_TABLE_ENTRY(jl_method_instance_type);
INIT_API_TABLE_ENTRY(jl_method_type);
INIT_API_TABLE_ENTRY(jl_module_type);

INIT_API_TABLE_ENTRY(jl_main_module);
Expand Down Expand Up @@ -108,10 +111,11 @@ jl_eval_string(VALUE handle, VALUE arg, VALUE raw_p)
/* TODO: exception handling */

if (RTEST(raw_p)) {
return rbjl_value_ptr_new(ans);
goto raw;
}

if (jl_is_string(ans)) {
/* TODO: encoding */
return rb_str_new2(JULIA_API(jl_string_ptr)(ans));
}
if (jl_is_bool(ans)) {
Expand Down Expand Up @@ -153,7 +157,9 @@ jl_eval_string(VALUE handle, VALUE arg, VALUE raw_p)
if (jl_is_float64(ans)) {
return DBL2NUM(JULIA_API(jl_unbox_float64)(ans));
}
return rb_str_new2(JULIA_API(jl_typeof_str)(ans));

raw:
return rbjl_value_ptr_new(ans);
}

static void
Expand Down
10 changes: 10 additions & 0 deletions ext/julia/value_ptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,19 @@ value_ptr_refcnt(VALUE self)
return LONG2NUM(refcnt);
}

static VALUE
value_ptr_typeof_str(VALUE self)
{
jl_value_t *value;
TypedData_Get_Struct(self, jl_value_t, &rbjl_value_ptr_data_type, value);
const char *s = JULIA_API(jl_typeof_str)(value);
return rb_str_new_cstr(s);
}

void
rbjl_init_value_ptr(void)
{
rbjl_cJuliaValuePtr = rb_define_class_under(rbjl_mJulia, "ValuePtr", rb_cData);
rb_define_method(rbjl_cJuliaValuePtr, "__refcnt__", value_ptr_refcnt, 0);
rb_define_method(rbjl_cJuliaValuePtr, "__typeof_str__", value_ptr_typeof_str, 0);
}
2 changes: 2 additions & 0 deletions lib/julia.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module Julia
require 'julia/version'
require 'julia/libjulia'
require 'julia/function'
require 'julia/object_wrapper'
require 'julia/init'

module_function
Expand Down
4 changes: 4 additions & 0 deletions lib/julia/function.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module Julia
class Function
end
end
9 changes: 9 additions & 0 deletions lib/julia/object_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Julia
module ObjectWrapper
def initialize(value_ptr)
@__value_ptr__ = value_ptr
end

attr_reader :__value_ptr__
end
end
8 changes: 8 additions & 0 deletions spec/julia/conversion_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require "spec_helper"

RSpec.describe "Julia object conversion" do
specify 'function' do
func = Julia.eval("x -> x^2 + 2x + 1")
expect(func.class).to eq(Julia::Function)
end
end
11 changes: 11 additions & 0 deletions spec/julia/function_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "spec_helper"

RSpec.describe "Julia object conversion" do
specify 'function' do
func = Julia.eval("x -> x^2 + 2x + 1")
p func.__typeof_str__
expect(func.(5)).to eq(36)
expect(func.(5).class).to eq(Integer)
expect(func.(5.0).class).to eq(Float)
end
end
24 changes: 24 additions & 0 deletions spec/julia/value_ptr_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require "spec_helper"

RSpec.describe Julia::ValuePtr do
describe 'GC guard reference' do
specify do
rbcall_module = Julia.eval("Main.RbCall", raw: true)
expect(rbcall_module).to be_kind_of(Julia::ValuePtr)
before_count = rbcall_module.__refcnt__

GC.disable
rbcall_other = Julia.eval("Main.RbCall", raw: true)
expect(rbcall_other).not_to be(rbcall_module)
after_count = rbcall_module.__refcnt__
expect(after_count).to eq(before_count + 1)

GC.enable

rbcall_other = nil
GC.start
after_gc_count = rbcall_module.__refcnt__
expect(after_gc_count).to eq(before_count)
end
end
end
8 changes: 8 additions & 0 deletions spec/julia/wrapper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require "spec_helper"

RSpec.describe "Julia object wrapper" do
specify do
func = Julia.eval("x -> x^2 + 2x + 1")
expect(func).to be_kind_of(Julia::ObjectWrapper)
end
end