github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/eval.go (about) 1 package bas 2 3 import ( 4 "bytes" 5 "fmt" 6 "math" 7 "strings" 8 "unicode/utf8" 9 "unsafe" 10 11 "github.com/coyove/nj/internal" 12 "github.com/coyove/nj/typ" 13 ) 14 15 type Stacktrace struct { 16 Cursor uint32 17 StackOffset uint32 18 Callable *Object 19 } 20 21 func (r *Stacktrace) sourceLine() (src uint32) { 22 posv := r.Callable.fun.codeSeg.Pos 23 lastLine := uint32(math.MaxUint32) 24 cursor := r.Cursor - 1 25 if posv.Len() > 0 { 26 _, op, line := posv.Read(0) 27 for cursor > op && posv.Len() > 0 { 28 op, line = posv.Pop() 29 // fmt.Println(r.Callable.fun.name, cursor, op, line) 30 } 31 if cursor <= op { 32 return line 33 } 34 lastLine = line 35 } 36 return lastLine 37 } 38 39 // ExecError represents the runtime error/panic. 40 type ExecError struct { 41 fatal bool 42 root interface{} 43 stacks []Stacktrace 44 } 45 46 func (e *ExecError) IsPanic() bool { 47 return e.fatal 48 } 49 50 func (e *ExecError) GetCause() interface{} { 51 if e == nil { 52 return nil 53 } 54 return e.root 55 } 56 57 func (e *ExecError) Error() string { 58 msg := bytes.Buffer{} 59 e.printError(&msg, 0) 60 return msg.String() 61 } 62 63 func (e *ExecError) printError(msg *bytes.Buffer, idx int) { 64 if ee, ok := e.root.(*ExecError); ok { 65 ee.printError(msg, idx+1) 66 } else if ee, ok := e.root.(Value); ok && ee.IsError() { 67 ee.Error().printError(msg, idx+1) 68 } else { 69 fmt.Fprint(msg, e.root) 70 } 71 msg.WriteByte('\n') 72 73 if e.fatal { 74 msg.WriteString("fatal ") 75 } 76 77 if idx == 0 { 78 msg.WriteString("stacktrace:\n") 79 } else { 80 msg.WriteString(fmt.Sprintf("stacktrace %d:\n", idx)) 81 } 82 83 if len(e.stacks) == 0 { 84 msg.WriteString("none\n") 85 } else { 86 for i := len(e.stacks) - 1; i >= 0; i-- { 87 r := e.stacks[i] 88 if r.Callable.fun.native != nil { 89 msg.WriteString(fmt.Sprintf("%s (native function)\n\t<native code>\n", r.Callable.fun.name)) 90 } else { 91 ln := r.sourceLine() 92 msg.WriteString(fmt.Sprintf("%s at %s:%d (i%d)", 93 r.Callable.fun.name, 94 r.Callable.fun.codeSeg.Pos.Name, 95 ln, 96 r.Cursor-1, // the recorded cursor was advanced by 1 already 97 )) 98 msg.WriteString("\n\t") 99 line, ok := internal.LineOf(r.Callable.fun.top.Source, int(ln)) 100 if ok { 101 msg.WriteString(strings.TrimSpace(line)) 102 } else { 103 msg.WriteString("<unknown source>") 104 } 105 msg.WriteString("\n") 106 } 107 } 108 } 109 msg.Truncate(msg.Len() - 1) 110 } 111 112 func relayPanic(onPanic func() []Stacktrace) { 113 if r := recover(); r != nil { 114 if re, ok := r.(*ExecError); ok { 115 panic(re) 116 } 117 118 e := &ExecError{} 119 e.fatal = true 120 e.root = r 121 e.stacks = append([]Stacktrace{}, onPanic()...) 122 panic(e) 123 } 124 } 125 126 func internalExecCursorLoop(env Env, K *Object, retStack []Stacktrace) Value { 127 stackEnv := env 128 stackEnv.stackOffset = uint32(len(*env.stack)) 129 130 var cursor uint32 131 retStackStartSize := len(retStack) 132 133 defer relayPanic(func() []Stacktrace { 134 retStack = append(retStack, Stacktrace{ 135 Cursor: cursor, 136 Callable: K, 137 StackOffset: env.stackOffset, 138 }) 139 if stackEnv.runtime.stack0.Callable != nil { 140 retStack = append(retStack, stackEnv.runtime.stack0) 141 } 142 return retStack 143 }) 144 145 code := K.fun.codeSeg.CodePtr() 146 for { 147 v := (*typ.Inst)(unsafe.Pointer(code + typ.InstSize*uintptr(cursor))) 148 opa, opb := v.A, v.B 149 cursor++ 150 151 switch v.Opcode { 152 case typ.OpSet: 153 env._set(opa, env._get(opb)) 154 case typ.OpInc: 155 va := env._ref(opa) 156 if vb := env._get(opb); va.IsInt64() && vb.IsInt64() { 157 *va = Int64(va.UnsafeInt64() + vb.UnsafeInt64()) 158 } else if va.IsNumber() && vb.IsNumber() { 159 *va = Float64(va.Float64() + vb.Float64()) 160 } else if vat := va.Type(); vat == typ.String && vb.Type() == typ.String { 161 *va = Str(va.Str() + vb.Str()) 162 } else if vat == typ.Native { 163 va.Native().Append(vb) 164 } else { 165 internal.Panic("inc "+errNeedNumbersOrStrings, va.simple(), vb.simple()) 166 } 167 env.A = *va 168 cursor = uint32(int32(cursor) + int32(int16(v.C))) 169 case typ.OpNext: 170 va, vb := env._get(opa), env._get(opb) 171 switch va.Type() { 172 case typ.Nil: 173 env.A = Nil 174 case typ.Native: 175 env.A = va.Native().internalNext(vb) 176 case typ.Object: 177 env.A = va.Object().internalNext(vb) 178 case typ.String: 179 idx := 0 180 if vb != Nil { 181 idx = vb.Native().Get(0).Int() 182 } else { 183 vb = Array(Nil, Nil) 184 } 185 if r, sz := utf8.DecodeRuneInString(va.Str()[idx:]); sz == 0 { 186 vb.Native().Set(0, Nil) 187 vb.Native().Set(1, Nil) 188 } else { 189 vb.Native().Set(0, Int(idx+sz)) 190 vb.Native().Set(1, Int(int(r))) 191 } 192 env.A = vb 193 default: 194 internal.Panic("can't iterate over %v", va.simple()) 195 } 196 case typ.OpLen: 197 env.A = Int(env._get(opa).Len()) 198 case typ.OpExt: 199 switch v.OpcodeExt { 200 case typ.OpExtInc16: 201 va := env._ref(opa) 202 if va.IsInt64() { 203 *va = Int64(va.UnsafeInt64() + int64(int16(opb))) 204 } else if va.IsNumber() { 205 *va = Float64(va.Float64() + float64(int16(opb))) 206 } else if va.Type() == typ.Native { 207 va.Native().Append(Int(int(int16(opb)))) 208 } else { 209 internal.Panic("inc "+errNeedNumbers, va.simple(), int16(opb)) 210 } 211 env.A = *va 212 cursor = uint32(int32(cursor) + int32(int16(v.C))) 213 case typ.OpExtLess16: 214 if va := env._ref(opa); va.IsInt64() { 215 env.A = Bool(va.UnsafeInt64() < int64(int16(opb))) 216 } else if va.IsNumber() { 217 env.A = Bool(va.UnsafeFloat64() < float64(int16(opb))) 218 } else { 219 internal.Panic("comparison "+errNeedNumbers, va.simple(), int16(opb)) 220 } 221 case typ.OpExtAdd16: 222 if va := env._ref(opa); va.IsInt64() { 223 env.A = Int64(va.UnsafeInt64() + int64(int16(opb))) 224 } else if va.IsNumber() { 225 env.A = Float64(va.UnsafeFloat64() + float64(int16(opb))) 226 } else if va.Type() == typ.Native { 227 va.Native().Append(Int(int(int16(opb)))) 228 env.A = *va 229 } else { 230 internal.Panic("add "+errNeedNumbers, va.simple(), int16(opb)) 231 } 232 case typ.OpExtRSub16: 233 if va := env._ref(opa); va.IsInt64() { 234 env.A = Int64(int64(int16(opb)) - va.UnsafeInt64()) 235 } else if va.IsNumber() { 236 env.A = Float64(float64(int16(opb)) - va.UnsafeFloat64()) 237 } else { 238 internal.Panic("sub "+errNeedNumbers, va.simple(), int16(opb)) 239 } 240 case typ.OpExtGreat16: 241 if va := env._ref(opa); va.IsInt64() { 242 env.A = Bool(va.UnsafeInt64() > int64(int16(opb))) 243 } else if va.IsNumber() { 244 env.A = Bool(va.UnsafeFloat64() > float64(int16(opb))) 245 } else { 246 internal.Panic("comparison "+errNeedNumbers, va.simple(), int16(opb)) 247 } 248 case typ.OpExtNeq16: 249 env.A = Bool(env._get(opa) != Int64(int64(int16(opb)))) 250 case typ.OpExtEq16: 251 env.A = Bool(env._get(opa) == Int64(int64(int16(opb)))) 252 case typ.OpExtStore16: 253 subject, v := env._ref(opa), env._get(v.C) 254 if st := subject.Type(); st == typ.Native { 255 subject.Native().Set(int(int16(opb)), v) 256 } else if st == typ.Object { 257 subject.Object().Set(Int(int(int16(opb))), v) 258 } else { 259 internal.Panic("can't index %v using %v", subject.simple(), int16(opb)) 260 } 261 env.A = v 262 case typ.OpExtLoad16: 263 a := env._ref(opa) 264 if at := a.Type(); at == typ.Native { 265 env.A = a.Native().Get(int(int16(opb))) 266 } else if at == typ.String { 267 env.A = Int64(int64(a.Str()[int16(opb)])) 268 } else if at == typ.Object { 269 env.A = a.Object().Get(Int(int(int16(opb)))) 270 } else { 271 env.A = Nil 272 } 273 if v.C != typ.RegA { 274 env._set(v.C, env.A) 275 } 276 case typ.OpExtBitAnd: 277 va, vb := env._ref(opa), env._ref(opb) 278 if va.Type() == typ.Native && vb.Type() == typ.Native { 279 va.Native().Concat(vb.Native()) 280 env.A = *va 281 } else { 282 bassertTwoInts("and", va, vb) 283 env.A = Int64(va.Int64() & vb.Int64()) 284 } 285 case typ.OpExtBitOr: 286 va, vb := env._ref(opa), env._ref(opb) 287 bassertTwoInts("or", va, vb) 288 env.A = Int64(va.Int64() | vb.Int64()) 289 case typ.OpExtBitXor: 290 va, vb := env._ref(opa), env._ref(opb) 291 bassertTwoInts("xor", va, vb) 292 env.A = Int64(va.Int64() ^ vb.Int64()) 293 case typ.OpExtBitLsh: 294 va, vb := env._ref(opa), env._ref(opb) 295 bassertTwoInts("lsh", va, vb) 296 env.A = Int64(va.Int64() << vb.Int64()) 297 case typ.OpExtBitRsh: 298 va, vb := env._ref(opa), env._ref(opb) 299 bassertTwoInts("rsh", va, vb) 300 env.A = Int64(va.Int64() >> vb.Int64()) 301 case typ.OpExtBitURsh: 302 va, vb := env._ref(opa), env._ref(opb) 303 bassertTwoInts("ursh", va, vb) 304 env.A = Int64(int64(uint64(va.Int64()) >> vb.Int64())) 305 case typ.OpExtBitAnd16: 306 va := env._get(opa) 307 bassertOneInt("and", va) 308 env.A = Int64(va.Int64() & int64(int16(opb))) 309 case typ.OpExtBitOr16: 310 va := env._get(opa) 311 bassertOneInt("or", va) 312 env.A = Int64(va.Int64() | int64(int16(opb))) 313 case typ.OpExtBitXor16: 314 va := env._get(opa) 315 bassertOneInt("xor", va) 316 env.A = Int64(va.Int64() ^ int64(int16(opb))) 317 case typ.OpExtBitLsh16: 318 va := env._get(opa) 319 bassertOneInt("lsh", va) 320 env.A = Int64(va.Int64() << int64(int16(opb))) 321 case typ.OpExtBitRsh16: 322 va := env._get(opa) 323 bassertOneInt("rsh", va) 324 env.A = Int64(va.Int64() >> int64(int16(opb))) 325 case typ.OpExtBitURsh16: 326 va := env._get(opa) 327 bassertOneInt("uRsh", va) 328 env.A = Int64(int64(uint64(va.Int64()) >> int64(int16(opb)))) 329 } 330 case typ.OpAdd: 331 if va, vb := env._ref(opa), env._ref(opb); va.IsInt64() && vb.IsInt64() { 332 env.A = Int64(va.UnsafeInt64() + vb.UnsafeInt64()) 333 } else if va.IsNumber() && vb.IsNumber() { 334 env.A = Float64(va.Float64() + vb.Float64()) 335 } else if vat, vbt := va.Type(), vb.Type(); vat == typ.String && vbt == typ.String { 336 env.A = Str(va.Str() + vb.Str()) 337 } else if vat == typ.Native { 338 va.Native().Append(*vb) 339 env.A = *va 340 } else { 341 internal.Panic("add "+errNeedNumbersOrStrings, va.simple(), vb.simple()) 342 } 343 case typ.OpSub: 344 if va, vb := env._ref(opa), env._ref(opb); va.IsInt64() && vb.IsInt64() { 345 env.A = Int64(va.UnsafeInt64() - vb.UnsafeInt64()) 346 } else if va.IsNumber() && vb.IsNumber() { 347 env.A = Float64(va.Float64() - vb.Float64()) 348 } else if va.IsObject() { 349 env.A = va.Object().Delete(*vb) 350 } else { 351 internal.Panic("sub "+errNeedNumbers, va.simple(), vb.simple()) 352 } 353 case typ.OpMul: 354 if va, vb := env._ref(opa), env._ref(opb); va.IsInt64() && vb.IsInt64() { 355 env.A = Int64(va.UnsafeInt64() * vb.UnsafeInt64()) 356 } else if va.IsNumber() && vb.IsNumber() { 357 env.A = Float64(va.Float64() * vb.Float64()) 358 } else { 359 internal.Panic("mul "+errNeedNumbers, va.simple(), vb.simple()) 360 } 361 case typ.OpDiv: 362 if va, vb := env._ref(opa), env._ref(opb); va.IsNumber() && vb.IsNumber() { 363 env.A = Float64(va.Float64() / vb.Float64()) 364 } else { 365 internal.Panic("div "+errNeedNumbers, va.simple(), vb.simple()) 366 } 367 case typ.OpIDiv: 368 if va, vb := env._ref(opa), env._ref(opb); va.IsNumber() && vb.IsNumber() { 369 env.A = Int64(va.Int64() / vb.Int64()) 370 } else { 371 internal.Panic("idiv "+errNeedNumbers, va.simple(), vb.simple()) 372 } 373 case typ.OpMod: 374 if va, vb := env._get(opa), env._get(opb); va.IsNumber() && vb.IsNumber() { 375 env.A = Int64(va.Int64() % vb.Int64()) 376 } else { 377 internal.Panic("mod "+errNeedNumbers, va.simple(), vb.simple()) 378 } 379 case typ.OpEq: 380 env.A = Bool(env._ref(opa).Equal(env._get(opb))) 381 case typ.OpNeq: 382 env.A = Bool(!env._ref(opa).Equal(env._get(opb))) 383 case typ.OpLess: 384 if va, vb := env._ref(opa), env._ref(opb); va.IsInt64() && vb.IsInt64() { 385 env.A = Bool(va.UnsafeInt64() < vb.UnsafeInt64()) 386 } else if va.IsNumber() && vb.IsNumber() { 387 env.A = Bool(va.Float64() < vb.Float64()) 388 } else if va.Type() == typ.String && vb.Type() == typ.String { 389 env.A = Bool(lessStr(*va, *vb)) 390 } else { 391 internal.Panic("comparison "+errNeedNumbersOrStrings, va.simple(), vb.simple()) 392 } 393 case typ.OpLessEq: 394 if va, vb := env._ref(opa), env._ref(opb); va.IsInt64() && vb.IsInt64() { 395 env.A = Bool(va.UnsafeInt64() <= vb.UnsafeInt64()) 396 } else if va.IsNumber() && vb.IsNumber() { 397 env.A = Bool(va.Float64() <= vb.Float64()) 398 } else if va.Type() == typ.String && vb.Type() == typ.String { 399 env.A = Bool(!lessStr(*vb, *va)) 400 } else { 401 internal.Panic("comparison "+errNeedNumbersOrStrings, va.simple(), vb.simple()) 402 } 403 case typ.OpNot: 404 env.A = Bool(env._get(opa).IsFalse()) 405 case typ.OpCreateArray: 406 env.A = newArray(append([]Value{}, stackEnv.Stack()...)...).ToValue() 407 stackEnv.clear() 408 case typ.OpCreateObject: 409 stk := stackEnv.Stack() 410 o := NewObject(len(stk) / 2) 411 for i := 0; i < len(stk); i += 2 { 412 o.Set(stk[i], stk[i+1]) 413 } 414 env.A = o.ToValue() 415 stackEnv.clear() 416 case typ.OpIsProto: 417 if a, b := env._get(opa), env._get(opb); b.IsString() { 418 env.A = Bool(TestShapeFast(a, b.Str()) == nil) 419 } else if b.IsObject() { 420 env.A = Bool(a.HasPrototype(b.Object())) 421 } else { 422 env.A = Bool(a.Equal(b)) 423 } 424 case typ.OpStore: 425 subject, k, v := env._ref(opa), env._get(opb), env._get(v.C) 426 switch subject.Type() { 427 case typ.Object: 428 subject.Object().Set(k, v) 429 case typ.Native: 430 if k.IsInt64() { 431 subject.Native().Set(k.Int(), v) 432 } else { 433 subject.Native().SetKey(k, v) 434 } 435 default: 436 internal.Panic("can't index %v using %v", subject.simple(), k.simple()) 437 } 438 env.A = v 439 case typ.OpLoad: 440 switch a, idx := env._ref(opa), env._get(opb); a.Type() { 441 case typ.Object: 442 env.A = a.Object().Get(idx) 443 case typ.Native: 444 if idx.IsInt64() { 445 env.A = a.Native().Get(idx.Int()) 446 } else { 447 env.A, _ = a.Native().GetKey(idx) 448 } 449 case typ.String: 450 if idx.IsInt64() { 451 env.A = Int64(int64(a.Str()[idx.UnsafeInt64()])) 452 } else { 453 env.A = setObjectRecv(Proto.Str.Get(idx), *a) 454 } 455 default: 456 env.A = Nil 457 } 458 env._set(v.C, env.A) 459 case typ.OpSlice: 460 a, start, end := env._get(opa), env._get(opb), env._get(v.C) 461 switch a.Type() { 462 case typ.Native: 463 if a := a.Native(); a.HasPrototype(&Proto.NativeMap) { 464 if v, ok := a.GetKey(start); ok { 465 env.A = v 466 } else { 467 env.A = end 468 } 469 } else { 470 if !start.IsInt64() || !end.IsInt64() { 471 internal.Panic("can't slice %v using %v and %v", a.ToValue().simple(), start.simple(), end.simple()) 472 } 473 if end := end.Int(); end == -1 { 474 env.A = a.Slice(start.Int(), a.Len()).ToValue() 475 } else { 476 env.A = a.Slice(start.Int(), end).ToValue() 477 } 478 } 479 case typ.String: 480 if !start.IsInt64() || !end.IsInt64() { 481 internal.Panic("can't slice %v using %v and %v", a.simple(), start.simple(), end.simple()) 482 } 483 if end := end.Int(); end == -1 { 484 env.A = Str(a.Str()[start.Int():a.Len()]) 485 } else { 486 env.A = Str(a.Str()[start.Int():end]) 487 } 488 case typ.Object: 489 env.A = a.Object().GetDefault(start, end) 490 default: 491 internal.Panic("can't slice %v", a.simple()) 492 } 493 case typ.OpPush: 494 stackEnv.push(env._get(opa)) 495 case typ.OpPushUnpack: 496 switch a := env._get(opa); a.Type() { 497 case typ.Native: 498 *stackEnv.stack = append(*stackEnv.stack, a.Native().Values()...) 499 case typ.Nil: 500 default: 501 internal.Panic("arguments unpacking expects array, got %v", a.simple()) 502 } 503 case typ.OpRet: 504 v := env._get(opa) 505 if len(retStack) == retStackStartSize { 506 return v 507 } 508 // Return to upper stack 509 r := retStack[len(retStack)-1] 510 cursor = r.Cursor 511 K = r.Callable 512 code = K.fun.codeSeg.CodePtr() 513 env.stackOffset = r.StackOffset 514 env.A = v 515 env.top = K.fun.top 516 *env.stack = (*env.stack)[:env.stackOffset+uint32(r.Callable.fun.stackSize)] 517 stackEnv.stackOffset = uint32(len(*env.stack)) 518 retStack = retStack[:len(retStack)-1] 519 case typ.OpFunction: 520 if opa == typ.RegA { 521 env.A = K.ToValue() 522 } else { 523 o := env._get(opa).Object().Copy() 524 if opb == 1 { 525 o.Merge(K) 526 for addr, name := range o.fun.caps { 527 if name == "" { 528 continue 529 } 530 if uint16(addr) == v.C { 531 // Recursive closure, e.g.: 532 // function foo() 533 // function bar() 534 // self.bar() 535 // end 536 // return bar 537 // end 538 o.Set(Str(name), o.ToValue()) 539 } else { 540 o.Set(Str(name), env._get(uint16(addr))) 541 } 542 } 543 } 544 env._set(v.C, o.ToValue()) 545 } 546 case typ.OpCall, typ.OpTailCall: 547 a := env._refgp(opa) 548 if a.Type() != typ.Object { 549 internal.Panic("can't call %v", a.simple()) 550 } 551 obj := a.Object() 552 cls := obj.fun 553 if v.OpcodeExt == 1 { 554 stackEnv.push(env._get(opb)) 555 } else if v.OpcodeExt == 2 { 556 stackEnv.push(env._get(opb)) 557 stackEnv.push(env._get(v.C)) 558 } 559 stackEnv.A = obj.this 560 if cls.varg { 561 s, w := stackEnv.Stack(), int(cls.numArgs)-1 562 if len(s) > w { 563 s[w] = newVarargArray(s[w:]).ToValue() 564 } else { 565 if len(s) < w { 566 panicNotEnoughArgs(obj) 567 } 568 stackEnv.resize(w + 1) 569 stackEnv._set(uint16(w), Nil) 570 } 571 } 572 env.checkStackOverflow() 573 574 last := Stacktrace{ 575 Callable: K, 576 Cursor: cursor, 577 StackOffset: env.stackOffset, 578 } 579 580 if cls.native != nil { 581 stackEnv.top = env.top 582 stackEnv.runtime.stack0 = Stacktrace{Callable: obj} 583 stackEnv.runtime.stack1 = last 584 stackEnv.runtime.stackN = retStack 585 cls.native(&stackEnv) 586 cursor = stackEnv.runtime.stack1.Cursor 587 env.A = stackEnv.A 588 stackEnv.runtime = stacktraces{} 589 stackEnv.clear() 590 } else if v.Opcode == typ.OpCall { 591 if stackEnv.Size() < int(cls.numArgs) { 592 panicNotEnoughArgs(obj) 593 } 594 // Switch 'env' to 'stackEnv' and move up 'stackEnv'. 595 stackEnv.resizeZero(int(cls.stackSize), int(cls.numArgs)) 596 cursor = 0 597 K = obj 598 code = obj.fun.codeSeg.CodePtr() 599 env.stackOffset = stackEnv.stackOffset 600 env.top = cls.top 601 env.A = stackEnv.A 602 603 retStack = append(retStack, last) 604 stackEnv.stackOffset = uint32(len(*env.stack)) 605 } else { 606 if stackEnv.Size() < int(cls.numArgs) { 607 panicNotEnoughArgs(obj) 608 } 609 // Move arguments from 'stackEnv' to 'env'. 610 *env.stack = append((*env.stack)[:env.stackOffset], stackEnv.Stack()...) 611 612 // Resize 'env' to allocate enough space for the next function and move up 'stackEnv'. 613 env.resizeZero(int(cls.stackSize), int(cls.numArgs)) 614 cursor = 0 615 K = obj 616 code = obj.fun.codeSeg.CodePtr() 617 env.top = cls.top 618 env.A = stackEnv.A 619 stackEnv.stackOffset = uint32(len(*env.stack)) 620 } 621 case typ.OpJmp: 622 cursor = uint32(int32(cursor) + v.D()) 623 case typ.OpJmpFalse: 624 if env.A.IsFalse() { 625 cursor = uint32(int32(cursor) + v.D()) 626 } 627 case typ.OpLoadTop: 628 if a := topSymbols.stack[opa]; opb != typ.RegPhantom { 629 env._set(v.C, a.AssertObject("load top").Get(env._get(opb))) 630 } else { 631 env._set(v.C, a) 632 } 633 } 634 } 635 }