github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/gocont.go (about) 1 package runtime 2 3 import ( 4 "errors" 5 "fmt" 6 "unsafe" 7 ) 8 9 // GoCont implements Cont for functions written in Go. 10 type GoCont struct { 11 *GoFunction 12 next Cont 13 args []Value 14 etc *[]Value 15 nArgs int 16 } 17 18 var _ Cont = (*GoCont)(nil) 19 20 // NewGoCont returns a new pointer to GoCont for the given GoFunction and Cont. 21 func NewGoCont(t *Thread, f *GoFunction, next Cont) *GoCont { 22 var args []Value 23 var etc *[]Value 24 if f.nArgs > 0 { 25 t.RequireArrSize(unsafe.Sizeof(Value{}), f.nArgs) 26 args = t.argsPool.get(f.nArgs) 27 } 28 if f.hasEtc { 29 etc = new([]Value) 30 } 31 t.RequireSize(unsafe.Sizeof(GoCont{})) 32 cont := t.goContPool.get() 33 *cont = GoCont{ 34 GoFunction: f, 35 args: args, 36 etc: etc, 37 next: next, 38 } 39 return cont 40 } 41 42 // Push implements Cont.Push. 43 func (c *GoCont) Push(r *Runtime, v Value) { 44 if c.nArgs < len(c.args) { 45 c.args[c.nArgs] = v 46 c.nArgs++ 47 } else if c.etc != nil { 48 r.RequireSize(unsafe.Sizeof(Value{})) 49 *c.etc = append(*c.etc, v) 50 } 51 } 52 53 // PushingNext is convenient when implementing go functions. It pushes the 54 // given values to c.Next() and returns it. 55 func (c *GoCont) PushingNext(r *Runtime, vals ...Value) Cont { 56 next := c.Next() 57 next.PushEtc(r, vals) 58 return next 59 } 60 61 // PushingNext1 is convenient when implementing go functions. It pushes the 62 // given value to c.Next() and returns it. 63 func (c *GoCont) PushingNext1(r *Runtime, val Value) Cont { 64 next := c.Next() 65 next.Push(r, val) 66 return next 67 } 68 69 // PushEtc pushes a slice of values to the continutation. 70 func (c *GoCont) PushEtc(r *Runtime, etc []Value) { 71 if c.nArgs < len(c.args) { 72 for i, v := range etc { 73 c.args[c.nArgs] = v 74 c.nArgs++ 75 if c.nArgs == len(c.args) { 76 etc = etc[i+1:] 77 goto FillEtc 78 } 79 } 80 return 81 } 82 FillEtc: 83 if c.etc == nil { 84 return 85 } 86 r.RequireArrSize(unsafe.Sizeof(Value{}), len(etc)) 87 *c.etc = append(*c.etc, etc...) 88 } 89 90 // RunInThread implements Cont.RunInThread. 91 func (c *GoCont) RunInThread(t *Thread) (next Cont, err error) { 92 if err := t.CheckRequiredFlags(c.safetyFlags); err != nil { 93 return nil, err 94 } 95 t.RequireCPU(1) 96 97 t.goFunctionCallDepth++ 98 defer func() { t.goFunctionCallDepth-- }() 99 100 if t.goFunctionCallDepth > maxGoFunctionCallDepth { 101 return nil, errors.New("stack overflow") 102 } 103 next, err = c.f(t, c) 104 _ = t.triggerReturn(t, c) 105 106 if err != nil { 107 // If there is an error, c is still potentially needed for error 108 // handling, so do not return it to the pool. It will get GCed when no 109 // longer referenced, so it's OK. 110 return 111 } 112 if c.args != nil { 113 t.ReleaseArrSize(unsafe.Sizeof(Value{}), c.nArgs) 114 t.argsPool.release(c.args) 115 } 116 t.ReleaseSize(unsafe.Sizeof(GoCont{})) 117 t.goContPool.release(c) 118 return 119 } 120 121 // Next returns the next continuation. 122 func (c *GoCont) Next() Cont { 123 return c.next 124 } 125 126 // Parent returns the continuation's parent. 127 func (c *GoCont) Parent() Cont { 128 return c.next 129 } 130 131 // DebugInfo returns c's debug info. 132 func (c *GoCont) DebugInfo() *DebugInfo { 133 name := c.name 134 if name == "" { 135 name = "<go function>" 136 } 137 return &DebugInfo{ 138 Source: "[Go]", 139 CurrentLine: 0, 140 Name: name, 141 } 142 } 143 144 // NArgs returns the number of args pushed to the continuation. 145 func (c *GoCont) NArgs() int { 146 return c.nArgs 147 } 148 149 // Arg returns the n-th arg of the continuation. It doesn't do any range check! 150 func (c *GoCont) Arg(n int) Value { 151 return c.args[n] 152 } 153 154 // Args returns the slice of args pushed to the continuation. 155 func (c *GoCont) Args() []Value { 156 return c.args[:c.nArgs] 157 } 158 159 // Etc returns the etc args pushed to the continuation they exist. 160 func (c *GoCont) Etc() []Value { 161 if c.etc == nil { 162 return nil 163 } 164 return *c.etc 165 } 166 167 // Check1Arg returns a non-nil error if the continuation doesn't have at least 168 // one arg. 169 func (c *GoCont) Check1Arg() error { 170 if c.nArgs == 0 { 171 return errors.New("bad argument #1 (value needed)") 172 } 173 return nil 174 } 175 176 // CheckNArgs returns a non-nil error if the continuation doesn't have at least 177 // n args. 178 func (c *GoCont) CheckNArgs(n int) error { 179 if c.nArgs < n { 180 return fmt.Errorf("%d arguments needed", n) 181 } 182 return nil 183 } 184 185 // StringArg returns the n-th argument as a string if possible, otherwise a 186 // non-nil error. No range check! 187 func (c *GoCont) StringArg(n int) (string, error) { 188 s, ok := c.Arg(n).TryString() 189 if !ok { 190 return "", fmt.Errorf("#%d must be a string", n+1) 191 } 192 return s, nil 193 } 194 195 // BoolArg returns the n-th argument as a string if possible, otherwise a 196 // non-nil error. No range check! 197 func (c *GoCont) BoolArg(n int) (bool, error) { 198 arg := c.Arg(n) 199 if arg.IsNil() { 200 return false, nil 201 } 202 b, ok := arg.TryBool() 203 if !ok { 204 return false, fmt.Errorf("#%d must be a boolean", n+1) 205 } 206 return b, nil 207 } 208 209 // CallableArg returns the n-th argument as a callable if possible, otherwise a 210 // non-nil error. No range check! 211 func (c *GoCont) CallableArg(n int) (Callable, error) { 212 f, ok := c.Arg(n).TryCallable() 213 if !ok { 214 return nil, fmt.Errorf("#%d must be a callable", n+1) 215 } 216 return f, nil 217 } 218 219 // ClosureArg returns the n-th argument as a closure if possible, otherwise a 220 // non-nil error. No range check! 221 func (c *GoCont) ClosureArg(n int) (*Closure, error) { 222 f, ok := c.Arg(n).TryClosure() 223 if !ok { 224 return nil, fmt.Errorf("#%d must be a lua function", n+1) 225 } 226 return f, nil 227 } 228 229 // ThreadArg returns the n-th argument as a thread if possible, otherwise a 230 // non-nil error. No range check! 231 func (c *GoCont) ThreadArg(n int) (*Thread, error) { 232 t, ok := c.Arg(n).TryThread() 233 if !ok { 234 return nil, fmt.Errorf("#%d must be a thread", n+1) 235 } 236 return t, nil 237 } 238 239 // IntArg returns the n-th argument as an Int if possible, otherwise a 240 // non-nil error. No range check! 241 func (c *GoCont) IntArg(n int) (int64, error) { 242 i, ok := ToInt(c.Arg(n)) 243 if !ok { 244 return 0, fmt.Errorf("#%d must be an integer", n+1) 245 } 246 return i, nil 247 } 248 249 // FloatArg returns the n-th argument as a Float if possible, otherwise a 250 // non-nil error. No range check! 251 func (c *GoCont) FloatArg(n int) (float64, error) { 252 x, ok := ToFloat(c.Arg(n)) 253 if !ok { 254 return 0, fmt.Errorf("#%d must be a number", n+1) 255 } 256 return x, nil 257 } 258 259 // TableArg returns the n-th argument as a table if possible, otherwise a 260 // non-nil error. No range check! 261 func (c *GoCont) TableArg(n int) (*Table, error) { 262 t, ok := c.Arg(n).TryTable() 263 if !ok { 264 return nil, fmt.Errorf("#%d must be a table", n+1) 265 } 266 return t, nil 267 } 268 269 // UserDataArg returns the n-th argument as a UserData if possible, otherwise a 270 // non-nil error. No range check! 271 func (c *GoCont) UserDataArg(n int) (*UserData, error) { 272 t, ok := c.Arg(n).TryUserData() 273 if !ok { 274 return nil, fmt.Errorf("#%d must be userdata", n+1) 275 } 276 return t, nil 277 }