go-mruby icon indicating copy to clipboard operation
go-mruby copied to clipboard

mruby.Run does not work with a Proc returned from LoadString

Open mikesimons opened this issue 9 years ago • 6 comments

mrb := NewMrb()
defer mrb.Close()

proc, _ := mrb.LoadString(`
    Proc.new do
        puts 1
    end
`)

val, err := mrb.Run(proc, nil)

fmt.Printf("%#v, %#v", val, err)

Expected: Prints "1", returns nil, nil Actual: (_mruby.MrbValue)(nil), &mruby.Exception{MrbValue:(_mruby.MrbValue)(0xc8200920e0), cachedString:"unexpected return"}

For some reason mruby.Run does not work with a Proc returned from mruby.LoadString. There may be good technical reasons (though I can't think of what they might be) but this surprised me. The error unexpected return is also non-obvious as there is no return injected anywhere AFAICT.

It is necessary to use something like the following to create a Proc that works with mruby.Run:

mrb := NewMrb()
defer mrb.Close()

parser := NewParser(mrb)
defer parser.Close()

context := NewCompileContext(mrb)
defer context.Close()

parser.Parse(`puts 1`, context)
proc := parser.GenerateCode()

val, err := mrb.Run(proc, nil)
fmt.Printf("%#v, %#v", val, err)

This works as expected. Drilling down in to the differences between the MrbValues generated with these two approaches should be sufficient to determine a cause (and potential solution).

mikesimons avatar Mar 07 '16 13:03 mikesimons

Pretty odd. I expect its some weirdness with the C API. Steps forward would be:

  1. Determine if mrb_run is supposed to work with procs at all.
  2. If yes, fix it!
  3. If no, ask ourselves why? And determine if go-mruby should "just work".

mitchellh avatar Mar 07 '16 15:03 mitchellh

(Note: not telling you you have to do this, just posting that thats what the steps are in general)

mitchellh avatar Mar 07 '16 15:03 mitchellh

while I was trying to verify this, I ran into a full panic with the following ruby:

def foo
end

The return value is indeed the foo func yet this trace occurs on a recent version of master:

[fail:1][75] erikh@islay erikh/ruby-builder% go run ~/test.go                                [14:34]
foo
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x2a5 pc=0x40c3bf2]

runtime stack:
runtime.throw(0x4135538, 0x2a)
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/panic.go:566 +0x95
runtime.sigpanic()
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/sigpanic_unix.go:12 +0x2cc

goroutine 1 [syscall, locked to thread]:
runtime.cgocall(0x408a750, 0xc42004fe18, 0x0)
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/cgocall.go:131 +0x110 fp=0xc42004fdc8 sp=0xc42004fd88
github.com/mitchellh/go-mruby._Cfunc_mrb_run(0x4402850, 0x28d, 0x48084a0, 0x8, 0x0, 0x0)
        ??:0 +0x57 fp=0xc42004fe18 sp=0xc42004fdc8
github.com/mitchellh/go-mruby.(*Mrb).Run(0xc420086018, 0x41b2440, 0xc420088060, 0x41b2440, 0xc420088080, 0x4, 0x0, 0x0)
        /Users/erikh/src/github.com/mitchellh/go-mruby/mruby.go:193 +0x1b4 fp=0xc42004feb8 sp=0xc42004fe18
main.main()
        /Users/erikh/test.go:20 +0x11d fp=0xc42004ff48 sp=0xc42004feb8
runtime.main()
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/proc.go:183 +0x1f4 fp=0xc42004ffa0 sp=0xc42004ff48
runtime.goexit()
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc42004ffa8 sp=0xc42004ffa0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /usr/local/Cellar/go/1.7.1/libexec/src/runtime/asm_amd64.s:2086 +0x1
exit status 2

erikh avatar Oct 05 '16 21:10 erikh

@erikh I've replicated it but given that this code already isn't behaving as we anticipated I'm not entirely surprised.

My test case was:

mrb := mruby.NewMrb()
defer mrb.Close()

result, err := mrb.LoadString(`
def foo
end
`)

// mrb.Run(result, nil) // Causes SIGSEGV

fmt.Printf("Result: %#v, %s\n", result, err)

This printed:

Result: &mruby.MrbValue{value:mruby._Ctype_struct_mrb_value{value:[8]uint8{0x8d, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, tt:0x4, _:[4]uint8{0x0, 0x0, 0x0, 0x0}}, state:(*mruby._Ctype_struct_mrb_state)(0x2578760)}, %!s(<nil>)

tt:0x4 field of mrb_value indicates that result contains an MRB_TT_SYMBOL

IRB behaves similarly (returning a symbol from a function declaration):

> irb
irb(main):001:0> def foo
irb(main):002:1> end
=> :foo

I think we'd need to expose / use mrb_f_send to make your test work.

mikesimons avatar Oct 06 '16 13:10 mikesimons

makes sense. I'll see what I can do this week.

On 6 Oct 2016, at 6:53, Mike Simons wrote:

@erikh I've replicated it but given that this code already isn't behaving as we anticipated I'm not entirely surprised.

My test case was:

mrb := mruby.NewMrb()
defer mrb.Close()

result, err := mrb.LoadString(`
def foo
end
`)

// mrb.Run(result, nil) // Causes SIGSEGV

fmt.Printf("Result: %#v, %s\n", result, err)

This printed:

Result: 
&mruby.MrbValue{value:mruby._Ctype_struct_mrb_value{value:[8]uint8{0x8d, 
0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, tt:0x4, _:[4]uint8{0x0, 0x0, 0x0, 
0x0}}, state:(*mruby._Ctype_struct_mrb_state)(0x2578760)}, %!s(<nil>)

tt:0x4 field of mrb_value indicates that result contains an mrb_value of type MRB_TT_SYMBOL

IRB behaves similarly (returning a symbol from a function declaration):

> irb
irb(main):001:0> def foo
irb(main):002:1> end
=> :foo

I think we'd need to expose / use mrb_f_send to make your test work.

You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/mitchellh/go-mruby/issues/23#issuecomment-251967662

erikh avatar Oct 06 '16 14:10 erikh

Just a note; since such an easy mistake to make can cause a segfault, we should probably inspect the mrbvalue and ensure it's a proc before handing it over to C. Might also be interesting to try this with a more recent version of mruby (I tested against the version in the makefile)

mikesimons avatar Oct 14 '16 23:10 mikesimons