Skip to content

Commit

Permalink
Separate stores for class and instance methods
Browse files Browse the repository at this point in the history
  • Loading branch information
zhulik committed Sep 16, 2024
1 parent 742ab5c commit 9713fb6
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 23 deletions.
8 changes: 4 additions & 4 deletions class.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (c *Class) String() string {

// DefineClassMethod defines a class-level method on the given class.
func (c *Class) DefineClassMethod(name string, cb Func, spec ArgSpec) {
c.GRuby().insertMethod(c.class.c, name, cb)
c.GRuby().insertClassMethod(c.class.c, name, cb)

cstr := C.CString(name)
defer freeStr(cstr)
Expand All @@ -28,7 +28,7 @@ func (c *Class) DefineClassMethod(name string, cb Func, spec ArgSpec) {
c.GRuby().state,
c.class,
cstr,
C._go_mrb_func_t(),
C._go_grb_class_method_call_t(),
C.mrb_aspec(spec))
}

Expand All @@ -43,7 +43,7 @@ func (c *Class) DefineConst(name string, value Value) {
// DefineMethod defines an instance method on the class.
func (c *Class) DefineMethod(name string, cb Func, spec ArgSpec) {
grb := c.GRuby()
grb.insertMethod(c.class, name, cb)
grb.insertInstanceMethod(c.class, name, cb)

cstr := C.CString(name)
defer freeStr(cstr)
Expand All @@ -52,7 +52,7 @@ func (c *Class) DefineMethod(name string, cb Func, spec ArgSpec) {
grb.state,
c.class,
cstr,
C._go_mrb_func_t(),
C._go_grb_instance_method_call_t(),
C.mrb_aspec(spec))
}

Expand Down
2 changes: 1 addition & 1 deletion decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ func decodeStructHashGetter(grb *GRuby, hash Hash) decodeStructGetter {
}

// decodeStructObjectMethods is a decodeStructGetter that reads values from
// an object by calling methods.
// an object by calling instanceMethods.
func decodeStructObjectMethods(_ *GRuby, v Value) decodeStructGetter {
return func(key string) (Value, error) {
return v.Call(key)
Expand Down
2 changes: 1 addition & 1 deletion decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestDecode(t *testing.T) {
structString{Foo: "bar"},
},

// Struct from object with methods
// Struct from object with instanceMethods
{
testDecodeObjectMethods,
&outStructString,
Expand Down
27 changes: 24 additions & 3 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import (
"unsafe"
)

type MethodType = int

const (
MethodTypeInstance = iota
MethodTypeClass
)

// Func is the signature of a function in Go that you use to expose to Ruby
// code.
//
Expand All @@ -15,16 +22,30 @@ import (
// The second return value is an exception, if any. This will be raised.
type Func func(grb *GRuby, self Value) (Value, Value)

//export goGRBFuncCall
func goGRBFuncCall(state *C.mrb_state, value C.mrb_value) C.mrb_value {
//export goGRBInstanceMethodCall
func goGRBInstanceMethodCall(state *C.mrb_state, value C.mrb_value) C.mrb_value {
return callMethod(state, value, MethodTypeInstance)
}

//export goGRBClassMethodCall
func goGRBClassMethodCall(state *C.mrb_state, value C.mrb_value) C.mrb_value {
return callMethod(state, value, MethodTypeClass)
}

func callMethod(state *C.mrb_state, value C.mrb_value, methodType MethodType) C.mrb_value {
grb := states.get(state)
// Get the call info, which we use to lookup the proc
callInfo := state.c.ci

// Lookup the class itself
class := *(**C.struct_RClass)(unsafe.Pointer(&callInfo.u[0]))

method := grb.methods.get(class, callInfo.mid)
var method Func
if methodType == MethodTypeInstance {
method = grb.instanceMethods.get(class, callInfo.mid)
} else {
method = grb.classMethods.get(class, callInfo.mid)
}

result, exc := method(grb, grb.value(value))

Expand Down
22 changes: 16 additions & 6 deletions gruby.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ type GRuby struct {
loadedFiles map[string]bool
getArgAccumulator Values

methods methodsStore
instanceMethods methodsStore
classMethods methodsStore

trueV Value
falseV Value
Expand Down Expand Up @@ -66,7 +67,11 @@ func New(mutators ...Mutator) (*GRuby, error) {
grb := &GRuby{
state: C.mrb_open(),
loadedFiles: map[string]bool{},
methods: methodsStore{
instanceMethods: methodsStore{
grb: nil,
classes: classMethodMap{},
},
classMethods: methodsStore{
grb: nil,
classes: classMethodMap{},
},
Expand All @@ -75,7 +80,8 @@ func New(mutators ...Mutator) (*GRuby, error) {
falseV: nil,
nilV: nil,
}
grb.methods.grb = grb
grb.instanceMethods.grb = grb
grb.classMethods.grb = grb
grb.trueV = grb.value(C.mrb_true_value())
grb.falseV = grb.value(C.mrb_false_value())
grb.nilV = grb.value(C.mrb_nil_value())
Expand Down Expand Up @@ -440,7 +446,7 @@ func (g *GRuby) TrueValue() Value {
return g.trueV
}

// When called from a methods defined in Go, returns current ruby backtrace.
// When called from a instanceMethods defined in Go, returns current ruby backtrace.
func (g *GRuby) Backtrace() []string {
backtrace := g.value(C.mrb_get_backtrace(g.state))
values := MustToGo[Values](backtrace)
Expand Down Expand Up @@ -486,8 +492,12 @@ func (g *GRuby) value(v C.mrb_value) Value {
}
}

func (g *GRuby) insertMethod(class *C.struct_RClass, name string, callback Func) {
g.methods.add(class, name, callback)
func (g *GRuby) insertInstanceMethod(class *C.struct_RClass, name string, callback Func) {
g.instanceMethods.add(class, name, callback)
}

func (g *GRuby) insertClassMethod(class *C.struct_RClass, name string, callback Func) {
g.classMethods.add(class, name, callback)
}

func checkException(grb *GRuby) error {
Expand Down
15 changes: 8 additions & 7 deletions gruby.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@
//-------------------------------------------------------------------
// Helpers to deal with calling back into Go.
//-------------------------------------------------------------------
// This is declard in func.go and is a way for us to call back into
// Go to execute a method.
extern mrb_value goGRBFuncCall(mrb_state *, mrb_value);
extern mrb_value goGRBInstanceMethodCall(mrb_state *, mrb_value);
static inline mrb_func_t _go_grb_instance_method_call_t()
{
return &goGRBInstanceMethodCall;
}

// This method is used as a way to get a valid mrb_func_t that actually
// just calls back into Go.
static inline mrb_func_t _go_mrb_func_t()
extern mrb_value goGRBClassMethodCall(mrb_state *, mrb_value);
static inline mrb_func_t _go_grb_class_method_call_t()
{
return &goGRBFuncCall;
return &goGRBClassMethodCall;
}

//-------------------------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ func (s *methodsStore) add(class *C.struct_RClass, name string, callback Func) {

func (s *methodsStore) get(class *C.struct_RClass, name C.mrb_sym) Func {
// the caller _must_ call `add`` before calling `get`, crashes otherwise.
return s.grb.methods.classes[class][name]
cls := s.classes[class]
return cls[name]
}

0 comments on commit 9713fb6

Please sign in to comment.