github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/func.go (about) 1 package bas 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math" 8 "os" 9 "strings" 10 11 "github.com/coyove/nj/internal" 12 "github.com/coyove/nj/typ" 13 ) 14 15 type funcbody struct { 16 name string 17 codeSeg internal.Packet 18 stackSize uint16 19 numArgs byte 20 varg bool 21 method bool 22 native func(env *Env) 23 top *Program 24 locals []string 25 caps []string 26 jumps map[string]int 27 } 28 29 type Program struct { 30 stack *[]Value 31 main *Object 32 symbols *Map 33 functions *Map 34 stopped bool 35 36 File string 37 Source string 38 MaxStackSize int64 39 Globals Map 40 Stdout io.Writer 41 Stderr io.Writer 42 Stdin io.Reader 43 } 44 45 var topSymbols struct { 46 sym Map 47 store Map 48 stack []Value 49 } 50 51 func GetTopIndex(v Value) int { 52 x, ok := topSymbols.sym.Get(v) 53 if !ok { 54 return 0 55 } 56 return int(x.UnsafeInt64()) 57 58 } 59 60 func TopSymbols() Map { 61 return topSymbols.store.Copy() 62 } 63 64 func AddTopValue(k string, v Value) { 65 sk := Str(k) 66 idx, ok := topSymbols.sym.Get(sk) 67 if ok { 68 topSymbols.stack[idx.Int()] = v 69 } else { 70 idx := len(topSymbols.stack) 71 topSymbols.sym.Set(sk, Int(idx)) 72 topSymbols.stack = append(topSymbols.stack, v) 73 } 74 topSymbols.store.Set(sk, v) 75 } 76 77 func AddTopFunc(k string, f func(*Env)) { 78 AddTopValue(k, Func(k, f)) 79 } 80 81 // Func creates a callable object 82 func Func(name string, f func(*Env)) Value { 83 if name == "" { 84 name = internal.UnnamedFunc() 85 } 86 if f == nil { 87 f = func(*Env) {} 88 } 89 obj := NewObject(0) 90 obj.fun = &funcbody{name: name, native: f} 91 obj.SetPrototype(&Proto.Func) 92 return obj.ToValue() 93 } 94 95 func (p *Program) Run() (v1 Value) { 96 p.stopped = false 97 if p.MaxStackSize <= 0 { 98 p.MaxStackSize = math.MaxInt64 99 } 100 101 newEnv := Env{ 102 top: p, 103 stack: p.stack, 104 } 105 defer catchPanicError(&newEnv, &v1) 106 v1 = internalExecCursorLoop(newEnv, p.main, nil) 107 return 108 } 109 110 // Stop stops the program from running unsafely, it can't stop any Go-native functions or goroutines. 111 func (p *Program) Stop() { 112 p.stopped = true 113 return 114 } 115 116 func (p *Program) GoString() string { 117 x := &bytes.Buffer{} 118 p.main.printAll(x) 119 p.functions.Foreach(func(f Value, idx *Value) bool { 120 x.WriteByte('\n') 121 (*p.stack)[idx.Int()&typ.RegLocalMask].Object().printAll(x) 122 return true 123 }) 124 return x.String() 125 } 126 127 func (p *Program) Get(k string) (v Value, ok bool) { 128 addr, ok := p.symbols.Get(Str(k)) 129 if !ok { 130 return Nil, false 131 } 132 return (*p.stack)[addr.Int64()], true 133 } 134 135 func (p *Program) Set(k string, v Value) (ok bool) { 136 addr, ok := p.symbols.Get(Str(k)) 137 if !ok { 138 return false 139 } 140 (*p.stack)[addr.Int64()] = v 141 return true 142 } 143 144 func (p *Program) LocalsObject() *Object { 145 r := NewObject(len(p.main.fun.locals)) 146 for i, name := range p.main.fun.locals { 147 r.Set(Str(name), (*p.stack)[i]) 148 } 149 return r 150 } 151 152 // Apply calls the object with provided 'this' value, 'e' is for stacktracing and is optional. 153 func (m *Object) Apply(e *Env, this Value, args ...Value) Value { 154 return callobj(m, e.getStacktraces(), e.getTop(), false, this, args...) 155 } 156 157 // TryApply calls the object with provided 'this' value, 'e' is for stacktracing and is optional. 158 // Panic will be recovered and returned as an error. 159 func (m *Object) TryApply(e *Env, this Value, args ...Value) (res Value) { 160 return callobj(m, e.getStacktraces(), e.getTop(), true, this, args...) 161 } 162 163 // Call calls the object, 'e' is for stacktracing and is optional. 164 func (m *Object) Call(e *Env, args ...Value) (res Value) { 165 return callobj(m, e.getStacktraces(), e.getTop(), false, m.this, args...) 166 } 167 168 // TryCall calls the object, 'e' is for stacktracing and is optional. Panic will be recovered and returned as an error. 169 func (m *Object) TryCall(e *Env, args ...Value) (res Value) { 170 return callobj(m, e.getStacktraces(), e.getTop(), true, m.this, args...) 171 } 172 173 func callobj(m *Object, r stacktraces, g *Program, outErr bool, this Value, args ...Value) (res Value) { 174 c := m.fun 175 newEnv := Env{ 176 A: this, 177 top: c.top, 178 stack: &args, 179 } 180 181 if c.top == nil { 182 newEnv.top = g 183 } 184 185 if outErr { 186 defer catchPanicError(&newEnv, &res) 187 } 188 189 if c.native != nil { 190 defer relayPanic(func() []Stacktrace { return newEnv.runtime.Stacktrace(false) }) 191 newEnv.runtime = r.push(Stacktrace{ 192 Callable: m, 193 }) 194 c.native(&newEnv) 195 return newEnv.A 196 } 197 198 if c.varg { 199 s := *newEnv.stack 200 if len(s) > int(c.numArgs)-1 { 201 s[c.numArgs-1] = newVarargArray(s[c.numArgs-1:]).ToValue() 202 } else { 203 if newEnv.Size() < int(c.numArgs)-1 { 204 panicNotEnoughArgs(m) 205 } 206 newEnv.resize(int(c.numArgs)) 207 newEnv._set(uint16(c.numArgs)-1, Nil) 208 } 209 } else { 210 if newEnv.Size() < int(c.numArgs) { 211 panicNotEnoughArgs(m) 212 } 213 } 214 newEnv.resizeZero(int(c.stackSize), int(c.numArgs)) 215 216 return internalExecCursorLoop(newEnv, m, r.Stacktrace(false)) 217 } 218 219 func (o *Object) funcSig() string { 220 c := o.fun 221 p := bytes.NewBufferString(c.name) 222 p.WriteString(internal.IfStr(c.method, "({this},", "(")) 223 if c.native != nil { 224 p.WriteString("...") 225 } else { 226 for i := 0; i < int(c.numArgs); i++ { 227 fmt.Fprintf(p, "a%d,", i) 228 } 229 } 230 internal.CloseBuffer(p, internal.IfStr(c.varg, "...)", ")")) 231 return p.String() 232 } 233 234 func (obj *Object) GoString() string { 235 buf := &bytes.Buffer{} 236 obj.printAll(buf) 237 return buf.String() 238 } 239 240 func (obj *Object) printAll(w io.Writer) { 241 cls, p := obj.fun, obj.fun.top 242 internal.WriteString(w, "start)\t"+obj.funcSig()+"\n") 243 if obj.parent != nil { 244 internal.WriteString(w, "proto)\t"+obj.parent.Name()+"\n") 245 } 246 if cls.native != nil { 247 internal.WriteString(w, "0)\t0\tnative code\n") 248 } else { 249 if cls == p.main.fun { 250 internal.WriteString(w, "source)\t"+cls.top.File+"\n") 251 } 252 253 readAddr := func(a uint16, rValue bool) string { 254 if a == typ.RegA { 255 return "a" 256 } 257 258 suffix := "" 259 if addr := a & typ.RegLocalMask; a != addr && rValue && int(addr) < len(*p.stack) { 260 x := (*p.stack)[addr] 261 if x != Nil { 262 suffix = ":" + x.simple() 263 } 264 } 265 266 if a > typ.RegLocalMask { 267 return fmt.Sprintf("g%d", a&typ.RegLocalMask) + suffix 268 } 269 return fmt.Sprintf("sp+%d", a&typ.RegLocalMask) + suffix 270 } 271 272 oldpos := cls.codeSeg.Pos 273 274 var cursor uint32 275 for cursor < uint32(len(cls.codeSeg.Code)) { 276 inst := cls.codeSeg.Code[cursor] 277 cursor++ 278 bop, a, b, c := inst.Opcode, inst.A, inst.B, inst.C 279 280 if oldpos.Len() > 0 { 281 c1 := cursor - 1 282 _, op, line := oldpos.Read(0) 283 for uint32(c1) > op && oldpos.Len() > 0 { 284 op, line = oldpos.Pop() 285 } 286 internal.WriteString(w, fmt.Sprintf("%d)\t%d\t", line, c1)) 287 } else { 288 internal.WriteString(w, fmt.Sprintf("$)\t%d\t", cursor-1)) 289 } 290 291 switch bop { 292 case typ.OpSet: 293 fmt.Fprintf(w, "%s = %s", readAddr(a, false), readAddr(b, false)) 294 case typ.OpCreateArray: 295 internal.WriteString(w, "createarray") 296 case typ.OpCreateObject: 297 internal.WriteString(w, "createobject") 298 case typ.OpFunction: 299 if a == typ.RegA { 300 internal.WriteString(w, "loadself") 301 } else if b == 0 { 302 fmt.Fprintf(w, "%s = loadfunction %s", readAddr(c, false), readAddr(a, true)) 303 } else if b == 1 { 304 fmt.Fprintf(w, "%s = loadclosure %s", readAddr(c, false), readAddr(a, true)) 305 } 306 case typ.OpTailCall: 307 if inst.OpcodeExt == 1 { 308 fmt.Fprintf(w, "tailfastcall %v %v", readAddr(a, true), readAddr(b, false)) 309 } else if inst.OpcodeExt == 2 { 310 fmt.Fprintf(w, "tailfastcall %v %v %v", readAddr(a, true), readAddr(b, false), readAddr(c, false)) 311 } else { 312 fmt.Fprintf(w, "tailcall %v", readAddr(a, true)) 313 } 314 case typ.OpCall: 315 if inst.OpcodeExt == 1 { 316 fmt.Fprintf(w, "fastcall %v %v", readAddr(a, true), readAddr(b, false)) 317 } else if inst.OpcodeExt == 2 { 318 fmt.Fprintf(w, "fastcall %v %v %v", readAddr(a, true), readAddr(b, false), readAddr(c, false)) 319 } else { 320 fmt.Fprintf(w, "call %v", readAddr(a, true)) 321 } 322 case typ.OpJmpFalse: 323 fmt.Fprintf(w, "jmpfalse %d", uint32(int32(cursor)+inst.D())) 324 case typ.OpJmp: 325 fmt.Fprintf(w, "jmp %d", uint32(int32(cursor)+inst.D())) 326 case typ.OpInc: 327 if c != 0 { 328 fmt.Fprintf(w, "inc %s %s jmp %d", readAddr(a, false), readAddr(b, false), int32(cursor)+int32(int16(c))) 329 } else { 330 fmt.Fprintf(w, "inc %s %s", readAddr(a, false), readAddr(b, false)) 331 } 332 case typ.OpLoad: 333 fmt.Fprintf(w, "%s = %s[%s]", readAddr(c, false), readAddr(a, false), readAddr(b, false)) 334 case typ.OpStore: 335 fmt.Fprintf(w, "%s[%s] = %s", readAddr(a, false), readAddr(b, false), readAddr(c, false)) 336 case typ.OpSlice: 337 fmt.Fprintf(w, "sliceload %s %s %s", readAddr(a, false), readAddr(b, false), readAddr(c, false)) 338 case typ.OpLoadTop: 339 if b != typ.RegPhantom { 340 fmt.Fprintf(w, "%s = loadtop %s[%s]", readAddr(c, false), topSymbols.stack[a].simple(), readAddr(b, true)) 341 } else { 342 fmt.Fprintf(w, "%s = loadtop %s", readAddr(c, false), topSymbols.stack[a].simple()) 343 } 344 case typ.OpExt: 345 switch inst.OpcodeExt { 346 case typ.OpExtAdd16: 347 fmt.Fprintf(w, "add %s $%d", readAddr(a, false), int16(b)) 348 case typ.OpExtRSub16: 349 fmt.Fprintf(w, "sub $%d %s", int16(b), readAddr(a, false)) 350 case typ.OpExtLess16: 351 fmt.Fprintf(w, "less %s $%d", readAddr(a, false), int16(b)) 352 case typ.OpExtGreat16: 353 fmt.Fprintf(w, "less $%d %s", int16(b), readAddr(a, false)) 354 case typ.OpExtEq16: 355 fmt.Fprintf(w, "eq %s $%d", readAddr(a, false), int16(b)) 356 case typ.OpExtNeq16: 357 fmt.Fprintf(w, "neq %s $%d", readAddr(a, false), int16(b)) 358 case typ.OpExtInc16: 359 if c != 0 { 360 fmt.Fprintf(w, "inc %s $%d jmp %d", readAddr(a, false), int16(b), int32(cursor)+int32(int16(c))) 361 } else { 362 fmt.Fprintf(w, "inc %s $%d", readAddr(a, false), int16(b)) 363 } 364 case typ.OpExtLoad16: 365 fmt.Fprintf(w, "%s = %s[$%d]", readAddr(c, false), readAddr(a, false), int16(b)) 366 case typ.OpExtStore16: 367 fmt.Fprintf(w, "%s[$%d] = %s", readAddr(a, false), int16(b), readAddr(c, false)) 368 case typ.OpExtBitAnd: 369 fmt.Fprintf(w, "bitand %s %s", readAddr(a, false), readAddr(b, false)) 370 case typ.OpExtBitOr: 371 fmt.Fprintf(w, "bitor %s %s", readAddr(a, false), readAddr(b, false)) 372 case typ.OpExtBitXor: 373 fmt.Fprintf(w, "bitxor %s %s", readAddr(a, false), readAddr(b, false)) 374 case typ.OpExtBitLsh: 375 fmt.Fprintf(w, "bitlsh %s %s", readAddr(a, false), readAddr(b, false)) 376 case typ.OpExtBitRsh: 377 fmt.Fprintf(w, "bitrsh %s %s", readAddr(a, false), readAddr(b, false)) 378 case typ.OpExtBitURsh: 379 fmt.Fprintf(w, "bitursh %s %s", readAddr(a, false), readAddr(b, false)) 380 case typ.OpExtBitAnd16: 381 fmt.Fprintf(w, "bitand %s $%d", readAddr(a, false), int16(b)) 382 case typ.OpExtBitOr16: 383 fmt.Fprintf(w, "bitor %s $%d", readAddr(a, false), int16(b)) 384 case typ.OpExtBitXor16: 385 fmt.Fprintf(w, "bitxor %s $%d", readAddr(a, false), int16(b)) 386 case typ.OpExtBitLsh16: 387 fmt.Fprintf(w, "bitlsh %s $%d", readAddr(a, false), int16(b)) 388 case typ.OpExtBitRsh16: 389 fmt.Fprintf(w, "bitrsh %s $%d", readAddr(a, false), int16(b)) 390 case typ.OpExtBitURsh16: 391 fmt.Fprintf(w, "bitursh %s $%d", readAddr(a, false), int16(b)) 392 default: 393 fmt.Fprintf(w, "? %02x", inst.OpcodeExt) 394 } 395 default: 396 if us, ok := typ.UnaryOpcode[bop]; ok { 397 fmt.Fprintf(w, "%v %v", us, readAddr(a, false)) 398 } else if bs, ok := typ.BinaryOpcode[bop]; ok { 399 fmt.Fprintf(w, "%v %v %v", bs, readAddr(a, false), readAddr(b, false)) 400 } else { 401 fmt.Fprintf(w, "? %02x", bop) 402 } 403 } 404 405 internal.WriteString(w, "\n") 406 } 407 } 408 ki := 0 409 obj.Foreach(func(k Value, v *Value) bool { 410 fmt.Fprintf(w, "prop%d)\t%v\t%v\n", ki, k, *v) 411 ki++ 412 return true 413 }) 414 internal.WriteString(w, "end)\t"+obj.funcSig()) 415 } 416 417 func NewBareFunc(f string, varg bool, np byte, ss uint16, 418 locals, caps []string, jt map[string]int, code internal.Packet) *Object { 419 obj := NewObject(0) 420 obj.SetPrototype(&Proto.Func) 421 obj.fun = &funcbody{} 422 obj.fun.varg = varg 423 obj.fun.numArgs = np 424 obj.fun.name = f 425 obj.fun.stackSize = ss 426 obj.fun.codeSeg = code 427 obj.fun.locals = locals 428 obj.fun.method = strings.Contains(f, ".") 429 obj.fun.caps = caps 430 obj.fun.jumps = jt 431 return obj 432 } 433 434 func NewBareProgram(coreStack []Value, top *Object, symbols, funcs *Map) *Program { 435 cls := &Program{} 436 cls.main = top 437 cls.stack = &coreStack 438 cls.symbols = symbols 439 cls.functions = funcs 440 cls.Stdout = os.Stdout 441 cls.Stdin = os.Stdin 442 cls.Stderr = os.Stderr 443 444 cls.main.fun.top = cls 445 cls.functions.Foreach(func(f Value, idx *Value) bool { 446 (*cls.stack)[idx.Int()&typ.RegLocalMask].Object().fun.top = cls 447 return true 448 }) 449 return cls 450 }