forked from mitchellh/go-mruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
value.go
293 lines (246 loc) · 7.54 KB
/
value.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
package mruby
import (
"fmt"
"strconv"
"strings"
"unsafe"
)
// #include <stdlib.h>
// #include "gomruby.h"
import "C"
// Value is an interface that should be implemented by anything that can
// be represents as an mruby value.
type Value interface {
MrbValue(*Mrb) *MrbValue
}
type Int int
type NilType [0]byte
type String string
// Nil is a constant that can be used as a Nil Value
var Nil NilType
// MrbValue is a "value" internally in mruby. A "value" is what mruby calls
// basically anything in Ruby: a class, an object (instance), a variable,
// etc.
type MrbValue struct {
value C.mrb_value
state *C.mrb_state
}
// ValueType is an enum of types that a Value can be and is returned by
// Value.Type().
type ValueType uint32
const (
TypeFalse ValueType = iota // 0
TypeFree // 1
TypeTrue // 2
TypeFixnum // 3
TypeSymbol // 4
TypeUndef // 5
TypeFloat // 6
TypeCptr // 7
TypeObject // 8
TypeClass // 9
TypeModule // 10
TypeIClass // 11
TypeSClass // 12
TypeProc // 13
TypeArray // 14
TypeHash // 15
TypeString // 16
TypeRange // 17
TypeException // 18
TypeFile // 19
TypeEnv // 20
TypeData // 21
TypeFiber // 22
TypeMaxDefine // 23
TypeNil ValueType = 0xffffffff
)
func init() {
Nil = [0]byte{}
}
// Call calls a method with the given name and arguments on this
// value.
func (v *MrbValue) Call(method string, args ...Value) (*MrbValue, error) {
return v.call(method, args, nil)
}
// CallBlock is the same as call except that it expects the last
// argument to be a Proc that will be passed into the function call.
// It is an error if args is empty or if there is no block on the end.
func (v *MrbValue) CallBlock(method string, args ...Value) (*MrbValue, error) {
if len(args) == 0 {
return nil, fmt.Errorf("args must be non-empty and have a proc at the end")
}
n := len(args)
return v.call(method, args[:n-1], args[n-1])
}
func (v *MrbValue) call(method string, args []Value, block Value) (*MrbValue, error) {
var argv []C.mrb_value = nil
var argvPtr *C.mrb_value = nil
if len(args) > 0 {
// Make the raw byte slice to hold our arguments we'll pass to C
argv = make([]C.mrb_value, len(args))
for i, arg := range args {
argv[i] = arg.MrbValue(&Mrb{v.state}).value
}
argvPtr = &argv[0]
}
var blockV *C.mrb_value
if block != nil {
val := block.MrbValue(&Mrb{v.state}).value
blockV = &val
}
cs := C.CString(method)
defer C.free(unsafe.Pointer(cs))
// If we have a block, we have to call a separate function to
// pass a block in. Otherwise, we just call it directly.
var result C.mrb_value
if blockV == nil {
result = C.mrb_funcall_argv(
v.state,
v.value,
C.mrb_intern_cstr(v.state, cs),
C.mrb_int(len(argv)),
argvPtr)
} else {
result = C.mrb_funcall_with_block(
v.state,
v.value,
C.mrb_intern_cstr(v.state, cs),
C.mrb_int(len(argv)),
argvPtr,
*blockV)
}
if exc := checkException(v.state); exc != nil {
return nil, exc
}
return newValue(v.state, result), nil
}
// IsDead tells you if an object has been collected by the GC or not.
func (v *MrbValue) IsDead() bool {
return C.ushort(C.mrb_object_dead_p(v.state, C._go_mrb_basic_ptr(v.value))) != 0
}
// MrbValue so that *MrbValue implements the "Value" interface.
func (v *MrbValue) MrbValue(*Mrb) *MrbValue {
return v
}
// Mrb returns the Mrb state for this value.
func (v *MrbValue) Mrb() *Mrb {
return &Mrb{v.state}
}
// SetProcTargetClass sets the target class where a proc will be executed
// when this value is a proc.
func (v *MrbValue) SetProcTargetClass(c *Class) {
proc := C._go_mrb_proc_ptr(v.value)
proc.target_class = c.class
}
func (v *MrbValue) Type() ValueType {
if C._go_mrb_nil_p(v.value) == 1 {
return TypeNil
}
return ValueType(C._go_mrb_type(v.value))
}
// Exception is a special type of value that represents an error
// and implements the Error interface.
type Exception struct {
*MrbValue
File string
Line int
Message string
Backtrace []string
}
func (e *Exception) Error() string {
return e.Message
}
func (e *Exception) String() string {
return e.Message
}
//-------------------------------------------------------------------
// Type conversions to Go types
//-------------------------------------------------------------------
// Array returns the Array value of this value. If the Type of the MrbValue
// is not a TypeArray, then this will panic. If the MrbValue has a
// `to_a` function, you must call that manually prior to calling this
// method.
func (v *MrbValue) Array() *Array {
return &Array{v}
}
// Fixnum returns the numeric value of this object if the Type() is
// TypeFixnum. Calling this with any other type will result in undefined
// behavior.
func (v *MrbValue) Fixnum() int {
return int(C._go_mrb_fixnum(v.value))
}
// Float returns the numeric value of this object if the Type() is
// TypeFloat. Calling this with any other type will result in undefined
// behavior.
func (v *MrbValue) Float() float64 {
return float64(C._go_mrb_float(v.value))
}
// Hash returns the Hash value of this value. If the Type of the MrbValue
// is not a ValueTypeHash, then this will panic. If the MrbValue has a
// `to_h` function, you must call that manually prior to calling this
// method.
func (v *MrbValue) Hash() *Hash {
return &Hash{v}
}
// String returns the "to_s" result of this value.
func (v *MrbValue) String() string {
value := C.mrb_obj_as_string(v.state, v.value)
result := C.GoString(C.mrb_string_value_ptr(v.state, value))
return result
}
//-------------------------------------------------------------------
// Native Go types implementing the Value interface
//-------------------------------------------------------------------
func (i Int) MrbValue(m *Mrb) *MrbValue {
return m.FixnumValue(int(i))
}
func (NilType) MrbValue(m *Mrb) *MrbValue {
return m.NilValue()
}
func (s String) MrbValue(m *Mrb) *MrbValue {
return m.StringValue(string(s))
}
//-------------------------------------------------------------------
// Internal Functions
//-------------------------------------------------------------------
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,
}
}
func newValue(s *C.mrb_state, v C.mrb_value) *MrbValue {
return &MrbValue{
state: s,
value: v,
}
}