func newExceptionValue(s *C.mrb_state) *Exception { if s.exc == nil { panic("exception value init without exception") } arenaIndex := C.mrb_gc_arena_save(s) defer C.mrb_gc_arena_restore(s, C.int(arenaIndex)) // Convert the RObject* to an mrb_value value := C.mrb_obj_value(unsafe.Pointer(s.exc)) // Retrieve and convert backtrace to []string (avoiding reflection in Decode) var backtrace []string mrbBacktrace := newValue(s, C.mrb_exc_backtrace(s, value)).Array() for i := 0; i < mrbBacktrace.Len(); i++ { ln, _ := mrbBacktrace.Get(i) backtrace = append(backtrace, ln.String()) } // Extract file + line from first backtrace line file := "Unknown" line := 0 if len(backtrace) > 0 { fileAndLine := strings.Split(backtrace[0], ":") if len(fileAndLine) >= 2 { file = fileAndLine[0] line, _ = strconv.Atoi(fileAndLine[1]) } } result := newValue(s, value) return &Exception{ MrbValue: result, Message: result.String(), File: file, Line: line, Backtrace: backtrace, } }
// This saves the index into the arena. // // Restore the arena index later by calling ArenaRestore. // // The arena is where objects returned by functions such as LoadString // are stored. By saving the index and then later restoring it with // ArenaRestore, these objects can be garbage collected. Otherwise, the // objects will never be garbage collected. // // The recommended usage pattern for memory management is to save // the arena index prior to any Ruby execution, to turn the resulting // Ruby value into Go values as you see fit, then to restore the arena // index so that GC can collect any values. // // Of course, when Close() is called, all objects in the arena are // garbage collected anyways, so if you're only calling mruby for a short // period of time, you might not have to worry about saving/restoring the // arena. func (m *Mrb) ArenaSave() ArenaIndex { return ArenaIndex(C.mrb_gc_arena_save(m.state)) }