github.com/yuin/gopher-lua@v1.1.2-0.20231212122839-2348fd042596/compile.go (about) 1 package lua 2 3 import ( 4 "fmt" 5 "math" 6 "reflect" 7 8 "github.com/yuin/gopher-lua/ast" 9 ) 10 11 /* internal constants & structs {{{ */ 12 13 const maxRegisters = 200 14 15 type expContextType int 16 17 const ( 18 ecGlobal expContextType = iota 19 ecUpvalue 20 ecLocal 21 ecTable 22 ecVararg 23 ecMethod 24 ecNone 25 ) 26 27 const regNotDefined = opMaxArgsA + 1 28 const labelNoJump = 0 29 30 type expcontext struct { 31 ctype expContextType 32 reg int 33 // varargopt >= 0: wants varargopt+1 results, i.e a = func() 34 // varargopt = -1: ignore results i.e func() 35 // varargopt = -2: receive all results i.e a = {func()} 36 varargopt int 37 } 38 39 type assigncontext struct { 40 ec *expcontext 41 keyrk int 42 valuerk int 43 keyks bool 44 needmove bool 45 } 46 47 type lblabels struct { 48 t int 49 f int 50 e int 51 b bool 52 } 53 54 type constLValueExpr struct { 55 ast.ExprBase 56 57 Value LValue 58 } 59 60 // }}} 61 62 /* utilities {{{ */ 63 var _ecnone0 = &expcontext{ecNone, regNotDefined, 0} 64 var _ecnonem1 = &expcontext{ecNone, regNotDefined, -1} 65 var _ecnonem2 = &expcontext{ecNone, regNotDefined, -2} 66 var ecfuncdef = &expcontext{ecMethod, regNotDefined, 0} 67 68 func ecupdate(ec *expcontext, ctype expContextType, reg, varargopt int) { 69 if ec == _ecnone0 || ec == _ecnonem1 || ec == _ecnonem2 { 70 panic("can not update ec cache") 71 } 72 ec.ctype = ctype 73 ec.reg = reg 74 ec.varargopt = varargopt 75 } 76 77 func ecnone(varargopt int) *expcontext { 78 switch varargopt { 79 case 0: 80 return _ecnone0 81 case -1: 82 return _ecnonem1 83 case -2: 84 return _ecnonem2 85 } 86 return &expcontext{ecNone, regNotDefined, varargopt} 87 } 88 89 func shouldmove(ec *expcontext, reg int) bool { 90 return ec.ctype == ecLocal && ec.reg != regNotDefined && ec.reg != reg 91 } 92 93 func sline(pos ast.PositionHolder) int { 94 return pos.Line() 95 } 96 97 func eline(pos ast.PositionHolder) int { 98 line := pos.LastLine() 99 if line == 0 { 100 return pos.Line() 101 } 102 return line 103 } 104 105 func savereg(ec *expcontext, reg int) int { 106 if ec.ctype != ecLocal || ec.reg == regNotDefined { 107 return reg 108 } 109 return ec.reg 110 } 111 112 func raiseCompileError(context *funcContext, line int, format string, args ...interface{}) { 113 msg := fmt.Sprintf(format, args...) 114 panic(&CompileError{context: context, Line: line, Message: msg}) 115 } 116 117 func isVarArgReturnExpr(expr ast.Expr) bool { 118 switch ex := expr.(type) { 119 case *ast.FuncCallExpr: 120 return !ex.AdjustRet 121 case *ast.Comma3Expr: 122 return !ex.AdjustRet 123 } 124 return false 125 } 126 127 func lnumberValue(expr ast.Expr) (LNumber, bool) { 128 if ex, ok := expr.(*ast.NumberExpr); ok { 129 lv, err := parseNumber(ex.Value) 130 if err != nil { 131 lv = LNumber(math.NaN()) 132 } 133 return lv, true 134 } else if ex, ok := expr.(*constLValueExpr); ok { 135 return ex.Value.(LNumber), true 136 } 137 return 0, false 138 } 139 140 /* utilities }}} */ 141 142 type gotoLabelDesc struct { // {{{ 143 Id int 144 Name string 145 Pc int 146 Line int 147 NumActiveLocalVars int 148 } 149 150 func newLabelDesc(id int, name string, pc, line, n int) *gotoLabelDesc { 151 return &gotoLabelDesc{ 152 Id: id, 153 Name: name, 154 Pc: pc, 155 Line: line, 156 NumActiveLocalVars: n, 157 } 158 } 159 160 func (l *gotoLabelDesc) SetNumActiveLocalVars(n int) { 161 l.NumActiveLocalVars = n 162 } // }}} 163 164 type CompileError struct { // {{{ 165 context *funcContext 166 Line int 167 Message string 168 } 169 170 func (e *CompileError) Error() string { 171 return fmt.Sprintf("compile error near line(%v) %v: %v", e.Line, e.context.Proto.SourceName, e.Message) 172 } // }}} 173 174 type codeStore struct { // {{{ 175 codes []uint32 176 lines []int 177 pc int 178 } 179 180 func (cd *codeStore) Add(inst uint32, line int) { 181 if l := len(cd.codes); l <= 0 || cd.pc == l { 182 cd.codes = append(cd.codes, inst) 183 cd.lines = append(cd.lines, line) 184 } else { 185 cd.codes[cd.pc] = inst 186 cd.lines[cd.pc] = line 187 } 188 cd.pc++ 189 } 190 191 func (cd *codeStore) AddABC(op int, a int, b int, c int, line int) { 192 cd.Add(opCreateABC(op, a, b, c), line) 193 } 194 195 func (cd *codeStore) AddABx(op int, a int, bx int, line int) { 196 cd.Add(opCreateABx(op, a, bx), line) 197 } 198 199 func (cd *codeStore) AddASbx(op int, a int, sbx int, line int) { 200 cd.Add(opCreateASbx(op, a, sbx), line) 201 } 202 203 func (cd *codeStore) PropagateKMV(top int, save *int, reg *int, inc int) { 204 lastinst := cd.Last() 205 if opGetArgA(lastinst) >= top { 206 switch opGetOpCode(lastinst) { 207 case OP_LOADK: 208 cindex := opGetArgBx(lastinst) 209 if cindex <= opMaxIndexRk { 210 cd.Pop() 211 *save = opRkAsk(cindex) 212 return 213 } 214 case OP_MOVE: 215 cd.Pop() 216 *save = opGetArgB(lastinst) 217 return 218 } 219 } 220 *save = *reg 221 *reg = *reg + inc 222 } 223 224 func (cd *codeStore) PropagateMV(top int, save *int, reg *int, inc int) { 225 lastinst := cd.Last() 226 if opGetArgA(lastinst) >= top { 227 switch opGetOpCode(lastinst) { 228 case OP_MOVE: 229 cd.Pop() 230 *save = opGetArgB(lastinst) 231 return 232 } 233 } 234 *save = *reg 235 *reg = *reg + inc 236 } 237 238 func (cd *codeStore) AddLoadNil(a, b, line int) { 239 last := cd.Last() 240 if opGetOpCode(last) == OP_LOADNIL && (opGetArgA(last)+opGetArgB(last)) == a { 241 cd.SetB(cd.LastPC(), b) 242 } else { 243 cd.AddABC(OP_LOADNIL, a, b, 0, line) 244 } 245 } 246 247 func (cd *codeStore) SetOpCode(pc int, v int) { 248 opSetOpCode(&cd.codes[pc], v) 249 } 250 251 func (cd *codeStore) SetA(pc int, v int) { 252 opSetArgA(&cd.codes[pc], v) 253 } 254 255 func (cd *codeStore) SetB(pc int, v int) { 256 opSetArgB(&cd.codes[pc], v) 257 } 258 259 func (cd *codeStore) SetC(pc int, v int) { 260 opSetArgC(&cd.codes[pc], v) 261 } 262 263 func (cd *codeStore) SetBx(pc int, v int) { 264 opSetArgBx(&cd.codes[pc], v) 265 } 266 267 func (cd *codeStore) SetSbx(pc int, v int) { 268 opSetArgSbx(&cd.codes[pc], v) 269 } 270 271 func (cd *codeStore) At(pc int) uint32 { 272 return cd.codes[pc] 273 } 274 275 func (cd *codeStore) List() []uint32 { 276 return cd.codes[:cd.pc] 277 } 278 279 func (cd *codeStore) PosList() []int { 280 return cd.lines[:cd.pc] 281 } 282 283 func (cd *codeStore) LastPC() int { 284 return cd.pc - 1 285 } 286 287 func (cd *codeStore) Last() uint32 { 288 if cd.pc == 0 { 289 return opInvalidInstruction 290 } 291 return cd.codes[cd.pc-1] 292 } 293 294 func (cd *codeStore) Pop() { 295 cd.pc-- 296 } /* }}} Code */ 297 298 /* {{{ VarNamePool */ 299 300 type varNamePoolValue struct { 301 Index int 302 Name string 303 } 304 305 type varNamePool struct { 306 names []string 307 offset int 308 } 309 310 func newVarNamePool(offset int) *varNamePool { 311 return &varNamePool{make([]string, 0, 16), offset} 312 } 313 314 func (vp *varNamePool) Names() []string { 315 return vp.names 316 } 317 318 func (vp *varNamePool) List() []varNamePoolValue { 319 result := make([]varNamePoolValue, len(vp.names), len(vp.names)) 320 for i, name := range vp.names { 321 result[i].Index = i + vp.offset 322 result[i].Name = name 323 } 324 return result 325 } 326 327 func (vp *varNamePool) LastIndex() int { 328 return vp.offset + len(vp.names) 329 } 330 331 func (vp *varNamePool) Find(name string) int { 332 for i := len(vp.names) - 1; i >= 0; i-- { 333 if vp.names[i] == name { 334 return i + vp.offset 335 } 336 } 337 return -1 338 } 339 340 func (vp *varNamePool) RegisterUnique(name string) int { 341 index := vp.Find(name) 342 if index < 0 { 343 return vp.Register(name) 344 } 345 return index 346 } 347 348 func (vp *varNamePool) Register(name string) int { 349 vp.names = append(vp.names, name) 350 return len(vp.names) - 1 + vp.offset 351 } 352 353 /* }}} VarNamePool */ 354 355 /* FuncContext {{{ */ 356 357 type codeBlock struct { 358 LocalVars *varNamePool 359 BreakLabel int 360 Parent *codeBlock 361 RefUpvalue bool 362 LineStart int 363 LastLine int 364 labels map[string]*gotoLabelDesc 365 firstGotoIndex int 366 } 367 368 func newCodeBlock(localvars *varNamePool, blabel int, parent *codeBlock, pos ast.PositionHolder, firstGotoIndex int) *codeBlock { 369 bl := &codeBlock{localvars, blabel, parent, false, 0, 0, map[string]*gotoLabelDesc{}, firstGotoIndex} 370 if pos != nil { 371 bl.LineStart = pos.Line() 372 bl.LastLine = pos.LastLine() 373 } 374 return bl 375 } 376 377 func (b *codeBlock) AddLabel(label *gotoLabelDesc) *gotoLabelDesc { 378 if old, ok := b.labels[label.Name]; ok { 379 return old 380 } 381 b.labels[label.Name] = label 382 return nil 383 } 384 385 func (b *codeBlock) GetLabel(label string) *gotoLabelDesc { 386 if v, ok := b.labels[label]; ok { 387 return v 388 } 389 return nil 390 } 391 392 func (b *codeBlock) LocalVarsCount() int { 393 count := 0 394 for block := b; block != nil; block = block.Parent { 395 count += len(block.LocalVars.Names()) 396 } 397 return count 398 } 399 400 type funcContext struct { 401 Proto *FunctionProto 402 Code *codeStore 403 Parent *funcContext 404 Upvalues *varNamePool 405 Block *codeBlock 406 Blocks []*codeBlock 407 regTop int 408 labelId int 409 labelPc map[int]int 410 gotosCount int 411 unresolvedGotos map[int]*gotoLabelDesc 412 } 413 414 func newFuncContext(sourcename string, parent *funcContext) *funcContext { 415 fc := &funcContext{ 416 Proto: newFunctionProto(sourcename), 417 Code: &codeStore{make([]uint32, 0, 1024), make([]int, 0, 1024), 0}, 418 Parent: parent, 419 Upvalues: newVarNamePool(0), 420 Block: newCodeBlock(newVarNamePool(0), labelNoJump, nil, nil, 0), 421 regTop: 0, 422 labelId: 1, 423 labelPc: map[int]int{}, 424 gotosCount: 0, 425 unresolvedGotos: map[int]*gotoLabelDesc{}, 426 } 427 fc.Blocks = []*codeBlock{fc.Block} 428 return fc 429 } 430 431 func (fc *funcContext) CheckUnresolvedGoto() { 432 for i := fc.Block.firstGotoIndex; i < fc.gotosCount; i++ { 433 gotoLabel, ok := fc.unresolvedGotos[i] 434 if !ok { 435 continue 436 } 437 raiseCompileError(fc, fc.Proto.LastLineDefined, "no visible label '%s' for <goto> at line %d", gotoLabel.Name, gotoLabel.Line) 438 } 439 } 440 441 func (fc *funcContext) AddUnresolvedGoto(label *gotoLabelDesc) { 442 fc.unresolvedGotos[fc.gotosCount] = label 443 fc.gotosCount++ 444 } 445 446 func (fc *funcContext) AddNamedLabel(label *gotoLabelDesc) { 447 if old := fc.Block.AddLabel(label); old != nil { 448 raiseCompileError(fc, label.Line+1, "label '%s' already defined on line %d", label.Name, old.Line) 449 } 450 fc.SetLabelPc(label.Id, label.Pc) 451 } 452 453 func (fc *funcContext) GetNamedLabel(name string) *gotoLabelDesc { 454 return fc.Block.GetLabel(name) 455 } 456 457 func (fc *funcContext) ResolveGoto(from, to *gotoLabelDesc, index int) { 458 if from.NumActiveLocalVars < to.NumActiveLocalVars { 459 varName := fc.Block.LocalVars.Names()[len(fc.Block.LocalVars.Names())-1] 460 raiseCompileError(fc, to.Line+1, "<goto %s> at line %d jumps into the scope of local '%s'", to.Name, from.Line, varName) 461 } 462 fc.Code.SetSbx(from.Pc, to.Id) 463 delete(fc.unresolvedGotos, index) 464 } 465 466 func (fc *funcContext) FindLabel(block *codeBlock, gotoLabel *gotoLabelDesc, i int) bool { 467 target := block.GetLabel(gotoLabel.Name) 468 if target != nil { 469 if gotoLabel.NumActiveLocalVars > target.NumActiveLocalVars && block.RefUpvalue { 470 fc.Code.SetA(gotoLabel.Pc-1, target.NumActiveLocalVars) 471 } 472 fc.ResolveGoto(gotoLabel, target, i) 473 return true 474 } 475 return false 476 } 477 478 func (fc *funcContext) ResolveCurrentBlockGotosWithParentBlock() { 479 blockActiveLocalVars := fc.Block.Parent.LocalVarsCount() 480 for i := fc.Block.firstGotoIndex; i < fc.gotosCount; i++ { 481 gotoLabel, ok := fc.unresolvedGotos[i] 482 if !ok { 483 continue 484 } 485 if gotoLabel.NumActiveLocalVars > blockActiveLocalVars { 486 if fc.Block.RefUpvalue { 487 fc.Code.SetA(gotoLabel.Pc-1, blockActiveLocalVars) 488 } 489 gotoLabel.SetNumActiveLocalVars(blockActiveLocalVars) 490 } 491 fc.FindLabel(fc.Block.Parent, gotoLabel, i) 492 } 493 } 494 495 func (fc *funcContext) ResolveForwardGoto(target *gotoLabelDesc) { 496 for i := fc.Block.firstGotoIndex; i <= fc.gotosCount; i++ { 497 gotoLabel, ok := fc.unresolvedGotos[i] 498 if !ok { 499 continue 500 } 501 if gotoLabel.Name == target.Name { 502 fc.ResolveGoto(gotoLabel, target, i) 503 } 504 } 505 } 506 507 func (fc *funcContext) NewLabel() int { 508 ret := fc.labelId 509 fc.labelId++ 510 return ret 511 } 512 513 func (fc *funcContext) SetLabelPc(label int, pc int) { 514 fc.labelPc[label] = pc 515 } 516 517 func (fc *funcContext) GetLabelPc(label int) int { 518 return fc.labelPc[label] 519 } 520 521 func (fc *funcContext) ConstIndex(value LValue) int { 522 ctype := value.Type() 523 for i, lv := range fc.Proto.Constants { 524 if lv.Type() == ctype && lv == value { 525 return i 526 } 527 } 528 fc.Proto.Constants = append(fc.Proto.Constants, value) 529 v := len(fc.Proto.Constants) - 1 530 if v > opMaxArgBx { 531 raiseCompileError(fc, fc.Proto.LineDefined, "too many constants") 532 } 533 return v 534 } 535 func (fc *funcContext) BlockLocalVarsCount() int { 536 count := 0 537 for block := fc.Block; block != nil; block = block.Parent { 538 count += len(block.LocalVars.Names()) 539 } 540 return count 541 } 542 543 func (fc *funcContext) RegisterLocalVar(name string) int { 544 ret := fc.Block.LocalVars.Register(name) 545 fc.Proto.DbgLocals = append(fc.Proto.DbgLocals, &DbgLocalInfo{Name: name, StartPc: fc.Code.LastPC() + 1}) 546 fc.SetRegTop(fc.RegTop() + 1) 547 return ret 548 } 549 550 func (fc *funcContext) FindLocalVarAndBlock(name string) (int, *codeBlock) { 551 for block := fc.Block; block != nil; block = block.Parent { 552 if index := block.LocalVars.Find(name); index > -1 { 553 return index, block 554 } 555 } 556 return -1, nil 557 } 558 559 func (fc *funcContext) FindLocalVar(name string) int { 560 idx, _ := fc.FindLocalVarAndBlock(name) 561 return idx 562 } 563 564 func (fc *funcContext) LocalVars() []varNamePoolValue { 565 result := make([]varNamePoolValue, 0, 32) 566 for _, block := range fc.Blocks { 567 result = append(result, block.LocalVars.List()...) 568 } 569 return result 570 } 571 572 func (fc *funcContext) EnterBlock(blabel int, pos ast.PositionHolder) { 573 fc.Block = newCodeBlock(newVarNamePool(fc.RegTop()), blabel, fc.Block, pos, fc.gotosCount) 574 fc.Blocks = append(fc.Blocks, fc.Block) 575 } 576 577 func (fc *funcContext) CloseUpvalues() int { 578 n := -1 579 if fc.Block.RefUpvalue { 580 n = fc.Block.Parent.LocalVars.LastIndex() 581 fc.Code.AddABC(OP_CLOSE, n, 0, 0, fc.Block.LastLine) 582 } 583 return n 584 } 585 586 func (fc *funcContext) LeaveBlock() int { 587 closed := fc.CloseUpvalues() 588 fc.EndScope() 589 590 if fc.Block.Parent != nil { 591 fc.ResolveCurrentBlockGotosWithParentBlock() 592 } 593 fc.Block = fc.Block.Parent 594 fc.SetRegTop(fc.Block.LocalVars.LastIndex()) 595 return closed 596 } 597 598 func (fc *funcContext) EndScope() { 599 for _, vr := range fc.Block.LocalVars.List() { 600 fc.Proto.DbgLocals[vr.Index].EndPc = fc.Code.LastPC() 601 } 602 } 603 604 func (fc *funcContext) SetRegTop(top int) { 605 if top > maxRegisters { 606 raiseCompileError(fc, fc.Proto.LineDefined, "too many local variables") 607 } 608 fc.regTop = top 609 } 610 611 func (fc *funcContext) RegTop() int { 612 return fc.regTop 613 } 614 615 /* FuncContext }}} */ 616 617 func compileChunk(context *funcContext, chunk []ast.Stmt, untilFollows bool) { // {{{ 618 for i, stmt := range chunk { 619 lastStmt := true 620 for j := i + 1; j < len(chunk); j++ { 621 _, ok := chunk[j].(*ast.LabelStmt) 622 if !ok { 623 lastStmt = false 624 break 625 } 626 } 627 compileStmt(context, stmt, lastStmt && !untilFollows) 628 } 629 } // }}} 630 631 func compileBlock(context *funcContext, chunk []ast.Stmt) { // {{{ 632 if len(chunk) == 0 { 633 return 634 } 635 ph := &ast.Node{} 636 ph.SetLine(sline(chunk[0])) 637 ph.SetLastLine(eline(chunk[len(chunk)-1])) 638 context.EnterBlock(labelNoJump, ph) 639 for i, stmt := range chunk { 640 lastStmt := true 641 for j := i + 1; j < len(chunk); j++ { 642 _, ok := chunk[j].(*ast.LabelStmt) 643 if !ok { 644 lastStmt = false 645 break 646 } 647 } 648 compileStmt(context, stmt, lastStmt) 649 } 650 context.LeaveBlock() 651 } // }}} 652 653 func compileStmt(context *funcContext, stmt ast.Stmt, isLastStmt bool) { // {{{ 654 switch st := stmt.(type) { 655 case *ast.AssignStmt: 656 compileAssignStmt(context, st) 657 case *ast.LocalAssignStmt: 658 compileLocalAssignStmt(context, st) 659 case *ast.FuncCallStmt: 660 compileFuncCallExpr(context, context.RegTop(), st.Expr.(*ast.FuncCallExpr), ecnone(-1)) 661 case *ast.DoBlockStmt: 662 context.EnterBlock(labelNoJump, st) 663 compileChunk(context, st.Stmts, false) 664 context.LeaveBlock() 665 case *ast.WhileStmt: 666 compileWhileStmt(context, st) 667 case *ast.RepeatStmt: 668 compileRepeatStmt(context, st) 669 case *ast.FuncDefStmt: 670 compileFuncDefStmt(context, st) 671 case *ast.ReturnStmt: 672 compileReturnStmt(context, st) 673 case *ast.IfStmt: 674 compileIfStmt(context, st) 675 case *ast.BreakStmt: 676 compileBreakStmt(context, st) 677 case *ast.NumberForStmt: 678 compileNumberForStmt(context, st) 679 case *ast.GenericForStmt: 680 compileGenericForStmt(context, st) 681 case *ast.LabelStmt: 682 compileLabelStmt(context, st, isLastStmt) 683 case *ast.GotoStmt: 684 compileGotoStmt(context, st) 685 } 686 } // }}} 687 688 func compileAssignStmtLeft(context *funcContext, stmt *ast.AssignStmt) (int, []*assigncontext) { // {{{ 689 reg := context.RegTop() 690 acs := make([]*assigncontext, 0, len(stmt.Lhs)) 691 for _, lhs := range stmt.Lhs { 692 switch st := lhs.(type) { 693 case *ast.IdentExpr: 694 identtype := getIdentRefType(context, context, st) 695 ec := &expcontext{identtype, regNotDefined, 0} 696 switch identtype { 697 case ecGlobal: 698 context.ConstIndex(LString(st.Value)) 699 case ecUpvalue: 700 context.Upvalues.RegisterUnique(st.Value) 701 case ecLocal: 702 ec.reg = context.FindLocalVar(st.Value) 703 } 704 acs = append(acs, &assigncontext{ec, 0, 0, false, false}) 705 case *ast.AttrGetExpr: 706 ac := &assigncontext{&expcontext{ecTable, regNotDefined, 0}, 0, 0, false, false} 707 compileExprWithKMVPropagation(context, st.Object, ®, &ac.ec.reg) 708 ac.keyrk = reg 709 reg += compileExpr(context, reg, st.Key, ecnone(0)) 710 if _, ok := st.Key.(*ast.StringExpr); ok { 711 ac.keyks = true 712 } 713 acs = append(acs, ac) 714 715 default: 716 panic("invalid left expression.") 717 } 718 } 719 return reg, acs 720 } // }}} 721 722 func compileAssignStmtRight(context *funcContext, stmt *ast.AssignStmt, reg int, acs []*assigncontext) (int, []*assigncontext) { // {{{ 723 lennames := len(stmt.Lhs) 724 lenexprs := len(stmt.Rhs) 725 namesassigned := 0 726 727 for namesassigned < lennames { 728 ac := acs[namesassigned] 729 ec := ac.ec 730 var expr ast.Expr = nil 731 if namesassigned >= lenexprs { 732 expr = &ast.NilExpr{} 733 expr.SetLine(sline(stmt.Lhs[namesassigned])) 734 expr.SetLastLine(eline(stmt.Lhs[namesassigned])) 735 } else if isVarArgReturnExpr(stmt.Rhs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 { 736 varargopt := lennames - namesassigned - 1 737 regstart := reg 738 reginc := compileExpr(context, reg, stmt.Rhs[namesassigned], ecnone(varargopt)) 739 reg += reginc 740 for i := namesassigned; i < namesassigned+int(reginc); i++ { 741 acs[i].needmove = true 742 if acs[i].ec.ctype == ecTable { 743 acs[i].valuerk = regstart + (i - namesassigned) 744 } 745 } 746 namesassigned = lennames 747 continue 748 } 749 750 if expr == nil { 751 expr = stmt.Rhs[namesassigned] 752 } 753 idx := reg 754 reginc := compileExpr(context, reg, expr, ec) 755 if ec.ctype == ecTable { 756 if _, ok := expr.(*ast.LogicalOpExpr); !ok { 757 context.Code.PropagateKMV(context.RegTop(), &ac.valuerk, ®, reginc) 758 } else { 759 ac.valuerk = idx 760 reg += reginc 761 } 762 } else { 763 ac.needmove = reginc != 0 764 reg += reginc 765 } 766 namesassigned += 1 767 } 768 769 rightreg := reg - 1 770 771 // extra right exprs 772 for i := namesassigned; i < lenexprs; i++ { 773 varargopt := -1 774 if i != lenexprs-1 { 775 varargopt = 0 776 } 777 reg += compileExpr(context, reg, stmt.Rhs[i], ecnone(varargopt)) 778 } 779 return rightreg, acs 780 } // }}} 781 782 func compileAssignStmt(context *funcContext, stmt *ast.AssignStmt) { // {{{ 783 code := context.Code 784 lennames := len(stmt.Lhs) 785 reg, acs := compileAssignStmtLeft(context, stmt) 786 reg, acs = compileAssignStmtRight(context, stmt, reg, acs) 787 788 for i := lennames - 1; i >= 0; i-- { 789 ex := stmt.Lhs[i] 790 switch acs[i].ec.ctype { 791 case ecLocal: 792 if acs[i].needmove { 793 code.AddABC(OP_MOVE, context.FindLocalVar(ex.(*ast.IdentExpr).Value), reg, 0, sline(ex)) 794 reg -= 1 795 } 796 case ecGlobal: 797 code.AddABx(OP_SETGLOBAL, reg, context.ConstIndex(LString(ex.(*ast.IdentExpr).Value)), sline(ex)) 798 reg -= 1 799 case ecUpvalue: 800 code.AddABC(OP_SETUPVAL, reg, context.Upvalues.RegisterUnique(ex.(*ast.IdentExpr).Value), 0, sline(ex)) 801 reg -= 1 802 case ecTable: 803 opcode := OP_SETTABLE 804 if acs[i].keyks { 805 opcode = OP_SETTABLEKS 806 } 807 code.AddABC(opcode, acs[i].ec.reg, acs[i].keyrk, acs[i].valuerk, sline(ex)) 808 if !opIsK(acs[i].valuerk) { 809 reg -= 1 810 } 811 } 812 } 813 } // }}} 814 815 func compileRegAssignment(context *funcContext, names []string, exprs []ast.Expr, reg int, nvars int, line int) { // {{{ 816 lennames := len(names) 817 lenexprs := len(exprs) 818 namesassigned := 0 819 ec := &expcontext{} 820 821 for namesassigned < lennames && namesassigned < lenexprs { 822 if isVarArgReturnExpr(exprs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 { 823 824 varargopt := nvars - namesassigned 825 ecupdate(ec, ecVararg, reg, varargopt-1) 826 compileExpr(context, reg, exprs[namesassigned], ec) 827 reg += varargopt 828 namesassigned = lennames 829 } else { 830 ecupdate(ec, ecLocal, reg, 0) 831 compileExpr(context, reg, exprs[namesassigned], ec) 832 reg += 1 833 namesassigned += 1 834 } 835 } 836 837 // extra left names 838 if lennames > namesassigned { 839 restleft := lennames - namesassigned - 1 840 context.Code.AddLoadNil(reg, reg+restleft, line) 841 reg += restleft 842 } 843 844 // extra right exprs 845 for i := namesassigned; i < lenexprs; i++ { 846 varargopt := -1 847 if i != lenexprs-1 { 848 varargopt = 0 849 } 850 ecupdate(ec, ecNone, reg, varargopt) 851 reg += compileExpr(context, reg, exprs[i], ec) 852 } 853 } // }}} 854 855 func compileLocalAssignStmt(context *funcContext, stmt *ast.LocalAssignStmt) { // {{{ 856 reg := context.RegTop() 857 if len(stmt.Names) == 1 && len(stmt.Exprs) == 1 { 858 if _, ok := stmt.Exprs[0].(*ast.FunctionExpr); ok { 859 context.RegisterLocalVar(stmt.Names[0]) 860 compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt)) 861 return 862 } 863 } 864 865 compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt)) 866 for _, name := range stmt.Names { 867 context.RegisterLocalVar(name) 868 } 869 } // }}} 870 871 func compileReturnStmt(context *funcContext, stmt *ast.ReturnStmt) { // {{{ 872 lenexprs := len(stmt.Exprs) 873 code := context.Code 874 reg := context.RegTop() 875 a := reg 876 lastisvaarg := false 877 878 if lenexprs == 1 { 879 switch ex := stmt.Exprs[0].(type) { 880 case *ast.IdentExpr: 881 if idx := context.FindLocalVar(ex.Value); idx > -1 { 882 code.AddABC(OP_RETURN, idx, 2, 0, sline(stmt)) 883 return 884 } 885 case *ast.FuncCallExpr: 886 if ex.AdjustRet { // return (func()) 887 reg += compileExpr(context, reg, ex, ecnone(0)) 888 } else { 889 reg += compileExpr(context, reg, ex, ecnone(-2)) 890 code.SetOpCode(code.LastPC(), OP_TAILCALL) 891 } 892 code.AddABC(OP_RETURN, a, 0, 0, sline(stmt)) 893 return 894 } 895 } 896 897 for i, expr := range stmt.Exprs { 898 if i == lenexprs-1 && isVarArgReturnExpr(expr) { 899 compileExpr(context, reg, expr, ecnone(-2)) 900 lastisvaarg = true 901 } else { 902 reg += compileExpr(context, reg, expr, ecnone(0)) 903 } 904 } 905 count := reg - a + 1 906 if lastisvaarg { 907 count = 0 908 } 909 context.Code.AddABC(OP_RETURN, a, count, 0, sline(stmt)) 910 } // }}} 911 912 func compileIfStmt(context *funcContext, stmt *ast.IfStmt) { // {{{ 913 thenlabel := context.NewLabel() 914 elselabel := context.NewLabel() 915 endlabel := context.NewLabel() 916 917 compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false) 918 context.SetLabelPc(thenlabel, context.Code.LastPC()) 919 compileBlock(context, stmt.Then) 920 if len(stmt.Else) > 0 { 921 context.Code.AddASbx(OP_JMP, 0, endlabel, sline(stmt)) 922 } 923 context.SetLabelPc(elselabel, context.Code.LastPC()) 924 if len(stmt.Else) > 0 { 925 compileBlock(context, stmt.Else) 926 context.SetLabelPc(endlabel, context.Code.LastPC()) 927 } 928 929 } // }}} 930 931 func compileBranchCondition(context *funcContext, reg int, expr ast.Expr, thenlabel, elselabel int, hasnextcond bool) { // {{{ 932 // TODO folding constants? 933 code := context.Code 934 flip := 0 935 jumplabel := elselabel 936 if hasnextcond { 937 flip = 1 938 jumplabel = thenlabel 939 } 940 941 switch ex := expr.(type) { 942 case *ast.FalseExpr, *ast.NilExpr: 943 if !hasnextcond { 944 code.AddASbx(OP_JMP, 0, elselabel, sline(expr)) 945 return 946 } 947 case *ast.TrueExpr, *ast.NumberExpr, *ast.StringExpr: 948 if !hasnextcond { 949 return 950 } 951 case *ast.UnaryNotOpExpr: 952 compileBranchCondition(context, reg, ex.Expr, elselabel, thenlabel, !hasnextcond) 953 return 954 case *ast.LogicalOpExpr: 955 switch ex.Operator { 956 case "and": 957 nextcondlabel := context.NewLabel() 958 compileBranchCondition(context, reg, ex.Lhs, nextcondlabel, elselabel, false) 959 context.SetLabelPc(nextcondlabel, context.Code.LastPC()) 960 compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond) 961 case "or": 962 nextcondlabel := context.NewLabel() 963 compileBranchCondition(context, reg, ex.Lhs, thenlabel, nextcondlabel, true) 964 context.SetLabelPc(nextcondlabel, context.Code.LastPC()) 965 compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond) 966 } 967 return 968 case *ast.RelationalOpExpr: 969 compileRelationalOpExprAux(context, reg, ex, flip, jumplabel) 970 return 971 } 972 973 a := reg 974 compileExprWithMVPropagation(context, expr, ®, &a) 975 code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr)) 976 code.AddASbx(OP_JMP, 0, jumplabel, sline(expr)) 977 } // }}} 978 979 func compileWhileStmt(context *funcContext, stmt *ast.WhileStmt) { // {{{ 980 thenlabel := context.NewLabel() 981 elselabel := context.NewLabel() 982 condlabel := context.NewLabel() 983 984 context.SetLabelPc(condlabel, context.Code.LastPC()) 985 compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false) 986 context.SetLabelPc(thenlabel, context.Code.LastPC()) 987 context.EnterBlock(elselabel, stmt) 988 compileChunk(context, stmt.Stmts, false) 989 context.CloseUpvalues() 990 context.Code.AddASbx(OP_JMP, 0, condlabel, eline(stmt)) 991 context.LeaveBlock() 992 context.SetLabelPc(elselabel, context.Code.LastPC()) 993 } // }}} 994 995 func compileRepeatStmt(context *funcContext, stmt *ast.RepeatStmt) { // {{{ 996 initlabel := context.NewLabel() 997 thenlabel := context.NewLabel() 998 elselabel := context.NewLabel() 999 1000 context.SetLabelPc(initlabel, context.Code.LastPC()) 1001 context.SetLabelPc(elselabel, context.Code.LastPC()) 1002 context.EnterBlock(thenlabel, stmt) 1003 compileChunk(context, stmt.Stmts, true) 1004 compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false) 1005 1006 context.SetLabelPc(thenlabel, context.Code.LastPC()) 1007 n := context.LeaveBlock() 1008 1009 if n > -1 { 1010 label := context.NewLabel() 1011 context.Code.AddASbx(OP_JMP, 0, label, eline(stmt)) 1012 context.SetLabelPc(elselabel, context.Code.LastPC()) 1013 context.Code.AddABC(OP_CLOSE, n, 0, 0, eline(stmt)) 1014 context.Code.AddASbx(OP_JMP, 0, initlabel, eline(stmt)) 1015 context.SetLabelPc(label, context.Code.LastPC()) 1016 } 1017 1018 } // }}} 1019 1020 func compileBreakStmt(context *funcContext, stmt *ast.BreakStmt) { // {{{ 1021 for block := context.Block; block != nil; block = block.Parent { 1022 if label := block.BreakLabel; label != labelNoJump { 1023 if block.RefUpvalue { 1024 context.Code.AddABC(OP_CLOSE, block.Parent.LocalVars.LastIndex(), 0, 0, sline(stmt)) 1025 } 1026 context.Code.AddASbx(OP_JMP, 0, label, sline(stmt)) 1027 return 1028 } 1029 } 1030 raiseCompileError(context, sline(stmt), "no loop to break") 1031 } // }}} 1032 1033 func compileFuncDefStmt(context *funcContext, stmt *ast.FuncDefStmt) { // {{{ 1034 if stmt.Name.Func == nil { 1035 reg := context.RegTop() 1036 var treg, kreg int 1037 compileExprWithKMVPropagation(context, stmt.Name.Receiver, ®, &treg) 1038 kreg = loadRk(context, ®, stmt.Func, LString(stmt.Name.Method)) 1039 compileExpr(context, reg, stmt.Func, ecfuncdef) 1040 context.Code.AddABC(OP_SETTABLE, treg, kreg, reg, sline(stmt.Name.Receiver)) 1041 } else { 1042 astmt := &ast.AssignStmt{Lhs: []ast.Expr{stmt.Name.Func}, Rhs: []ast.Expr{stmt.Func}} 1043 astmt.SetLine(sline(stmt.Func)) 1044 astmt.SetLastLine(eline(stmt.Func)) 1045 compileAssignStmt(context, astmt) 1046 } 1047 } // }}} 1048 1049 func compileNumberForStmt(context *funcContext, stmt *ast.NumberForStmt) { // {{{ 1050 code := context.Code 1051 endlabel := context.NewLabel() 1052 ec := &expcontext{} 1053 1054 context.EnterBlock(endlabel, stmt) 1055 reg := context.RegTop() 1056 rindex := context.RegisterLocalVar("(for index)") 1057 ecupdate(ec, ecLocal, rindex, 0) 1058 compileExpr(context, reg, stmt.Init, ec) 1059 1060 reg = context.RegTop() 1061 rlimit := context.RegisterLocalVar("(for limit)") 1062 ecupdate(ec, ecLocal, rlimit, 0) 1063 compileExpr(context, reg, stmt.Limit, ec) 1064 1065 reg = context.RegTop() 1066 rstep := context.RegisterLocalVar("(for step)") 1067 if stmt.Step == nil { 1068 stmt.Step = &ast.NumberExpr{Value: "1"} 1069 stmt.Step.SetLine(sline(stmt.Init)) 1070 } 1071 ecupdate(ec, ecLocal, rstep, 0) 1072 compileExpr(context, reg, stmt.Step, ec) 1073 1074 code.AddASbx(OP_FORPREP, rindex, 0, sline(stmt)) 1075 1076 context.RegisterLocalVar(stmt.Name) 1077 1078 bodypc := code.LastPC() 1079 compileChunk(context, stmt.Stmts, false) 1080 1081 context.LeaveBlock() 1082 1083 flpc := code.LastPC() 1084 code.AddASbx(OP_FORLOOP, rindex, bodypc-(flpc+1), sline(stmt)) 1085 1086 context.SetLabelPc(endlabel, code.LastPC()) 1087 code.SetSbx(bodypc, flpc-bodypc) 1088 1089 } // }}} 1090 1091 func compileGenericForStmt(context *funcContext, stmt *ast.GenericForStmt) { // {{{ 1092 code := context.Code 1093 endlabel := context.NewLabel() 1094 bodylabel := context.NewLabel() 1095 fllabel := context.NewLabel() 1096 nnames := len(stmt.Names) 1097 1098 context.EnterBlock(endlabel, stmt) 1099 rgen := context.RegisterLocalVar("(for generator)") 1100 context.RegisterLocalVar("(for state)") 1101 context.RegisterLocalVar("(for control)") 1102 1103 compileRegAssignment(context, stmt.Names, stmt.Exprs, context.RegTop()-3, 3, sline(stmt)) 1104 1105 code.AddASbx(OP_JMP, 0, fllabel, sline(stmt)) 1106 1107 for _, name := range stmt.Names { 1108 context.RegisterLocalVar(name) 1109 } 1110 1111 context.SetLabelPc(bodylabel, code.LastPC()) 1112 compileChunk(context, stmt.Stmts, false) 1113 1114 context.LeaveBlock() 1115 1116 context.SetLabelPc(fllabel, code.LastPC()) 1117 code.AddABC(OP_TFORLOOP, rgen, 0, nnames, sline(stmt)) 1118 code.AddASbx(OP_JMP, 0, bodylabel, sline(stmt)) 1119 1120 context.SetLabelPc(endlabel, code.LastPC()) 1121 } // }}} 1122 1123 func compileLabelStmt(context *funcContext, stmt *ast.LabelStmt, isLastStmt bool) { // {{{ 1124 labelId := context.NewLabel() 1125 label := newLabelDesc(labelId, stmt.Name, context.Code.LastPC(), sline(stmt), context.BlockLocalVarsCount()) 1126 context.AddNamedLabel(label) 1127 if isLastStmt { 1128 label.SetNumActiveLocalVars(context.Block.Parent.LocalVarsCount()) 1129 } 1130 context.ResolveForwardGoto(label) 1131 } // }}} 1132 1133 func compileGotoStmt(context *funcContext, stmt *ast.GotoStmt) { // {{{ 1134 context.Code.AddABC(OP_CLOSE, 0, 0, 0, sline(stmt)) 1135 context.Code.AddASbx(OP_JMP, 0, labelNoJump, sline(stmt)) 1136 label := newLabelDesc(-1, stmt.Label, context.Code.LastPC(), sline(stmt), context.BlockLocalVarsCount()) 1137 context.AddUnresolvedGoto(label) 1138 context.FindLabel(context.Block, label, context.gotosCount-1) 1139 } // }}} 1140 1141 func compileExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) int { // {{{ 1142 code := context.Code 1143 sreg := savereg(ec, reg) 1144 sused := 1 1145 if sreg < reg { 1146 sused = 0 1147 } 1148 1149 switch ex := expr.(type) { 1150 case *ast.StringExpr: 1151 code.AddABx(OP_LOADK, sreg, context.ConstIndex(LString(ex.Value)), sline(ex)) 1152 return sused 1153 case *ast.NumberExpr: 1154 num, err := parseNumber(ex.Value) 1155 if err != nil { 1156 num = LNumber(math.NaN()) 1157 } 1158 code.AddABx(OP_LOADK, sreg, context.ConstIndex(num), sline(ex)) 1159 return sused 1160 case *constLValueExpr: 1161 code.AddABx(OP_LOADK, sreg, context.ConstIndex(ex.Value), sline(ex)) 1162 return sused 1163 case *ast.NilExpr: 1164 code.AddLoadNil(sreg, sreg, sline(ex)) 1165 return sused 1166 case *ast.FalseExpr: 1167 code.AddABC(OP_LOADBOOL, sreg, 0, 0, sline(ex)) 1168 return sused 1169 case *ast.TrueExpr: 1170 code.AddABC(OP_LOADBOOL, sreg, 1, 0, sline(ex)) 1171 return sused 1172 case *ast.IdentExpr: 1173 switch getIdentRefType(context, context, ex) { 1174 case ecGlobal: 1175 code.AddABx(OP_GETGLOBAL, sreg, context.ConstIndex(LString(ex.Value)), sline(ex)) 1176 case ecUpvalue: 1177 code.AddABC(OP_GETUPVAL, sreg, context.Upvalues.RegisterUnique(ex.Value), 0, sline(ex)) 1178 case ecLocal: 1179 b := context.FindLocalVar(ex.Value) 1180 code.AddABC(OP_MOVE, sreg, b, 0, sline(ex)) 1181 } 1182 return sused 1183 case *ast.Comma3Expr: 1184 if context.Proto.IsVarArg == 0 { 1185 raiseCompileError(context, sline(ex), "cannot use '...' outside a vararg function") 1186 } 1187 context.Proto.IsVarArg &= ^VarArgNeedsArg 1188 code.AddABC(OP_VARARG, sreg, 2+ec.varargopt, 0, sline(ex)) 1189 if context.RegTop() > (sreg+2+ec.varargopt) || ec.varargopt < -1 { 1190 return 0 1191 } 1192 return (sreg + 1 + ec.varargopt) - reg 1193 case *ast.AttrGetExpr: 1194 a := sreg 1195 b := reg 1196 compileExprWithMVPropagation(context, ex.Object, ®, &b) 1197 c := reg 1198 compileExprWithKMVPropagation(context, ex.Key, ®, &c) 1199 opcode := OP_GETTABLE 1200 if _, ok := ex.Key.(*ast.StringExpr); ok { 1201 opcode = OP_GETTABLEKS 1202 } 1203 code.AddABC(opcode, a, b, c, sline(ex)) 1204 return sused 1205 case *ast.TableExpr: 1206 compileTableExpr(context, reg, ex, ec) 1207 return 1 1208 case *ast.ArithmeticOpExpr: 1209 compileArithmeticOpExpr(context, reg, ex, ec) 1210 return sused 1211 case *ast.StringConcatOpExpr: 1212 compileStringConcatOpExpr(context, reg, ex, ec) 1213 return sused 1214 case *ast.UnaryMinusOpExpr, *ast.UnaryNotOpExpr, *ast.UnaryLenOpExpr: 1215 compileUnaryOpExpr(context, reg, ex, ec) 1216 return sused 1217 case *ast.RelationalOpExpr: 1218 compileRelationalOpExpr(context, reg, ex, ec) 1219 return sused 1220 case *ast.LogicalOpExpr: 1221 compileLogicalOpExpr(context, reg, ex, ec) 1222 return sused 1223 case *ast.FuncCallExpr: 1224 return compileFuncCallExpr(context, reg, ex, ec) 1225 case *ast.FunctionExpr: 1226 childcontext := newFuncContext(context.Proto.SourceName, context) 1227 compileFunctionExpr(childcontext, ex, ec) 1228 protono := len(context.Proto.FunctionPrototypes) 1229 context.Proto.FunctionPrototypes = append(context.Proto.FunctionPrototypes, childcontext.Proto) 1230 code.AddABx(OP_CLOSURE, sreg, protono, sline(ex)) 1231 for _, upvalue := range childcontext.Upvalues.List() { 1232 localidx, block := context.FindLocalVarAndBlock(upvalue.Name) 1233 if localidx > -1 { 1234 code.AddABC(OP_MOVE, 0, localidx, 0, sline(ex)) 1235 block.RefUpvalue = true 1236 } else { 1237 upvalueidx := context.Upvalues.Find(upvalue.Name) 1238 if upvalueidx < 0 { 1239 upvalueidx = context.Upvalues.RegisterUnique(upvalue.Name) 1240 } 1241 code.AddABC(OP_GETUPVAL, 0, upvalueidx, 0, sline(ex)) 1242 } 1243 } 1244 return sused 1245 default: 1246 panic(fmt.Sprintf("expr %v not implemented.", reflect.TypeOf(ex).Elem().Name())) 1247 } 1248 1249 } // }}} 1250 1251 func compileExprWithPropagation(context *funcContext, expr ast.Expr, reg *int, save *int, propergator func(int, *int, *int, int)) { // {{{ 1252 reginc := compileExpr(context, *reg, expr, ecnone(0)) 1253 if _, ok := expr.(*ast.LogicalOpExpr); ok { 1254 *save = *reg 1255 *reg = *reg + reginc 1256 } else { 1257 propergator(context.RegTop(), save, reg, reginc) 1258 } 1259 } // }}} 1260 1261 func compileExprWithKMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{ 1262 compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateKMV) 1263 } // }}} 1264 1265 func compileExprWithMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{ 1266 compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateMV) 1267 } // }}} 1268 1269 func constFold(exp ast.Expr) ast.Expr { // {{{ 1270 switch expr := exp.(type) { 1271 case *ast.ArithmeticOpExpr: 1272 lvalue, lisconst := lnumberValue(constFold(expr.Lhs)) 1273 rvalue, risconst := lnumberValue(constFold(expr.Rhs)) 1274 if lisconst && risconst { 1275 switch expr.Operator { 1276 case "+": 1277 return &constLValueExpr{Value: lvalue + rvalue} 1278 case "-": 1279 return &constLValueExpr{Value: lvalue - rvalue} 1280 case "*": 1281 return &constLValueExpr{Value: lvalue * rvalue} 1282 case "/": 1283 return &constLValueExpr{Value: lvalue / rvalue} 1284 case "%": 1285 return &constLValueExpr{Value: luaModulo(lvalue, rvalue)} 1286 case "^": 1287 return &constLValueExpr{Value: LNumber(math.Pow(float64(lvalue), float64(rvalue)))} 1288 default: 1289 panic(fmt.Sprintf("unknown binop: %v", expr.Operator)) 1290 } 1291 } else { 1292 return expr 1293 } 1294 case *ast.UnaryMinusOpExpr: 1295 expr.Expr = constFold(expr.Expr) 1296 if value, ok := lnumberValue(expr.Expr); ok { 1297 return &constLValueExpr{Value: LNumber(-value)} 1298 } 1299 return expr 1300 default: 1301 1302 return exp 1303 } 1304 } // }}} 1305 1306 func compileFunctionExpr(context *funcContext, funcexpr *ast.FunctionExpr, ec *expcontext) { // {{{ 1307 context.Proto.LineDefined = sline(funcexpr) 1308 context.Proto.LastLineDefined = eline(funcexpr) 1309 if len(funcexpr.ParList.Names) > maxRegisters { 1310 raiseCompileError(context, context.Proto.LineDefined, "register overflow") 1311 } 1312 context.Proto.NumParameters = uint8(len(funcexpr.ParList.Names)) 1313 if ec.ctype == ecMethod { 1314 context.Proto.NumParameters += 1 1315 context.RegisterLocalVar("self") 1316 } 1317 for _, name := range funcexpr.ParList.Names { 1318 context.RegisterLocalVar(name) 1319 } 1320 if funcexpr.ParList.HasVargs { 1321 if CompatVarArg { 1322 context.Proto.IsVarArg = VarArgHasArg | VarArgNeedsArg 1323 if context.Parent != nil { 1324 context.RegisterLocalVar("arg") 1325 } 1326 } 1327 context.Proto.IsVarArg |= VarArgIsVarArg 1328 } 1329 1330 compileChunk(context, funcexpr.Stmts, false) 1331 1332 context.Code.AddABC(OP_RETURN, 0, 1, 0, eline(funcexpr)) 1333 context.EndScope() 1334 context.CheckUnresolvedGoto() 1335 context.Proto.Code = context.Code.List() 1336 context.Proto.DbgSourcePositions = context.Code.PosList() 1337 context.Proto.DbgUpvalues = context.Upvalues.Names() 1338 context.Proto.NumUpvalues = uint8(len(context.Proto.DbgUpvalues)) 1339 for _, clv := range context.Proto.Constants { 1340 sv := "" 1341 if slv, ok := clv.(LString); ok { 1342 sv = string(slv) 1343 } 1344 context.Proto.stringConstants = append(context.Proto.stringConstants, sv) 1345 } 1346 patchCode(context) 1347 } // }}} 1348 1349 func compileTableExpr(context *funcContext, reg int, ex *ast.TableExpr, ec *expcontext) { // {{{ 1350 code := context.Code 1351 /* 1352 tablereg := savereg(ec, reg) 1353 if tablereg == reg { 1354 reg += 1 1355 } 1356 */ 1357 tablereg := reg 1358 reg++ 1359 code.AddABC(OP_NEWTABLE, tablereg, 0, 0, sline(ex)) 1360 tablepc := code.LastPC() 1361 regbase := reg 1362 1363 arraycount := 0 1364 lastvararg := false 1365 for i, field := range ex.Fields { 1366 islast := i == len(ex.Fields)-1 1367 if field.Key == nil { 1368 if islast && isVarArgReturnExpr(field.Value) { 1369 reg += compileExpr(context, reg, field.Value, ecnone(-2)) 1370 lastvararg = true 1371 } else { 1372 reg += compileExpr(context, reg, field.Value, ecnone(0)) 1373 arraycount += 1 1374 } 1375 } else { 1376 regorg := reg 1377 b := reg 1378 compileExprWithKMVPropagation(context, field.Key, ®, &b) 1379 c := reg 1380 compileExprWithKMVPropagation(context, field.Value, ®, &c) 1381 opcode := OP_SETTABLE 1382 if _, ok := field.Key.(*ast.StringExpr); ok { 1383 opcode = OP_SETTABLEKS 1384 } 1385 code.AddABC(opcode, tablereg, b, c, sline(ex)) 1386 reg = regorg 1387 } 1388 flush := arraycount % FieldsPerFlush 1389 if (arraycount != 0 && (flush == 0 || islast)) || lastvararg { 1390 reg = regbase 1391 num := flush 1392 if num == 0 { 1393 num = FieldsPerFlush 1394 } 1395 c := (arraycount-1)/FieldsPerFlush + 1 1396 b := num 1397 if islast && isVarArgReturnExpr(field.Value) { 1398 b = 0 1399 } 1400 line := field.Value 1401 if field.Key != nil { 1402 line = field.Key 1403 } 1404 if c > 511 { 1405 c = 0 1406 } 1407 code.AddABC(OP_SETLIST, tablereg, b, c, sline(line)) 1408 if c == 0 { 1409 code.Add(uint32(c), sline(line)) 1410 } 1411 } 1412 } 1413 code.SetB(tablepc, int2Fb(arraycount)) 1414 code.SetC(tablepc, int2Fb(len(ex.Fields)-arraycount)) 1415 if shouldmove(ec, tablereg) { 1416 code.AddABC(OP_MOVE, ec.reg, tablereg, 0, sline(ex)) 1417 } 1418 } // }}} 1419 1420 func compileArithmeticOpExpr(context *funcContext, reg int, expr *ast.ArithmeticOpExpr, ec *expcontext) { // {{{ 1421 exp := constFold(expr) 1422 if ex, ok := exp.(*constLValueExpr); ok { 1423 exp.SetLine(sline(expr)) 1424 compileExpr(context, reg, ex, ec) 1425 return 1426 } 1427 expr, _ = exp.(*ast.ArithmeticOpExpr) 1428 a := savereg(ec, reg) 1429 b := reg 1430 compileExprWithKMVPropagation(context, expr.Lhs, ®, &b) 1431 c := reg 1432 compileExprWithKMVPropagation(context, expr.Rhs, ®, &c) 1433 1434 op := 0 1435 switch expr.Operator { 1436 case "+": 1437 op = OP_ADD 1438 case "-": 1439 op = OP_SUB 1440 case "*": 1441 op = OP_MUL 1442 case "/": 1443 op = OP_DIV 1444 case "%": 1445 op = OP_MOD 1446 case "^": 1447 op = OP_POW 1448 } 1449 context.Code.AddABC(op, a, b, c, sline(expr)) 1450 } // }}} 1451 1452 func compileStringConcatOpExpr(context *funcContext, reg int, expr *ast.StringConcatOpExpr, ec *expcontext) { // {{{ 1453 code := context.Code 1454 crange := 1 1455 for current := expr.Rhs; current != nil; { 1456 if ex, ok := current.(*ast.StringConcatOpExpr); ok { 1457 crange += 1 1458 current = ex.Rhs 1459 } else { 1460 current = nil 1461 } 1462 } 1463 a := savereg(ec, reg) 1464 basereg := reg 1465 reg += compileExpr(context, reg, expr.Lhs, ecnone(0)) 1466 reg += compileExpr(context, reg, expr.Rhs, ecnone(0)) 1467 for pc := code.LastPC(); pc != 0 && opGetOpCode(code.At(pc)) == OP_CONCAT; pc-- { 1468 code.Pop() 1469 } 1470 code.AddABC(OP_CONCAT, a, basereg, basereg+crange, sline(expr)) 1471 } // }}} 1472 1473 func compileUnaryOpExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) { // {{{ 1474 opcode := 0 1475 code := context.Code 1476 var operandexpr ast.Expr 1477 switch ex := expr.(type) { 1478 case *ast.UnaryMinusOpExpr: 1479 exp := constFold(ex) 1480 if lvexpr, ok := exp.(*constLValueExpr); ok { 1481 exp.SetLine(sline(expr)) 1482 compileExpr(context, reg, lvexpr, ec) 1483 return 1484 } 1485 ex, _ = exp.(*ast.UnaryMinusOpExpr) 1486 operandexpr = ex.Expr 1487 opcode = OP_UNM 1488 case *ast.UnaryNotOpExpr: 1489 switch ex.Expr.(type) { 1490 case *ast.TrueExpr: 1491 code.AddABC(OP_LOADBOOL, savereg(ec, reg), 0, 0, sline(expr)) 1492 return 1493 case *ast.FalseExpr, *ast.NilExpr: 1494 code.AddABC(OP_LOADBOOL, savereg(ec, reg), 1, 0, sline(expr)) 1495 return 1496 default: 1497 opcode = OP_NOT 1498 operandexpr = ex.Expr 1499 } 1500 case *ast.UnaryLenOpExpr: 1501 opcode = OP_LEN 1502 operandexpr = ex.Expr 1503 } 1504 1505 a := savereg(ec, reg) 1506 b := reg 1507 compileExprWithMVPropagation(context, operandexpr, ®, &b) 1508 code.AddABC(opcode, a, b, 0, sline(expr)) 1509 } // }}} 1510 1511 func compileRelationalOpExprAux(context *funcContext, reg int, expr *ast.RelationalOpExpr, flip int, label int) { // {{{ 1512 code := context.Code 1513 b := reg 1514 compileExprWithKMVPropagation(context, expr.Lhs, ®, &b) 1515 c := reg 1516 compileExprWithKMVPropagation(context, expr.Rhs, ®, &c) 1517 switch expr.Operator { 1518 case "<": 1519 code.AddABC(OP_LT, 0^flip, b, c, sline(expr)) 1520 case ">": 1521 code.AddABC(OP_LT, 0^flip, c, b, sline(expr)) 1522 case "<=": 1523 code.AddABC(OP_LE, 0^flip, b, c, sline(expr)) 1524 case ">=": 1525 code.AddABC(OP_LE, 0^flip, c, b, sline(expr)) 1526 case "==": 1527 code.AddABC(OP_EQ, 0^flip, b, c, sline(expr)) 1528 case "~=": 1529 code.AddABC(OP_EQ, 1^flip, b, c, sline(expr)) 1530 } 1531 code.AddASbx(OP_JMP, 0, label, sline(expr)) 1532 } // }}} 1533 1534 func compileRelationalOpExpr(context *funcContext, reg int, expr *ast.RelationalOpExpr, ec *expcontext) { // {{{ 1535 a := savereg(ec, reg) 1536 code := context.Code 1537 jumplabel := context.NewLabel() 1538 compileRelationalOpExprAux(context, reg, expr, 1, jumplabel) 1539 code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr)) 1540 context.SetLabelPc(jumplabel, code.LastPC()) 1541 code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr)) 1542 } // }}} 1543 1544 func compileLogicalOpExpr(context *funcContext, reg int, expr *ast.LogicalOpExpr, ec *expcontext) { // {{{ 1545 a := savereg(ec, reg) 1546 code := context.Code 1547 endlabel := context.NewLabel() 1548 lb := &lblabels{context.NewLabel(), context.NewLabel(), endlabel, false} 1549 nextcondlabel := context.NewLabel() 1550 if expr.Operator == "and" { 1551 compileLogicalOpExprAux(context, reg, expr.Lhs, ec, nextcondlabel, endlabel, false, lb) 1552 context.SetLabelPc(nextcondlabel, code.LastPC()) 1553 compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb) 1554 } else { 1555 compileLogicalOpExprAux(context, reg, expr.Lhs, ec, endlabel, nextcondlabel, true, lb) 1556 context.SetLabelPc(nextcondlabel, code.LastPC()) 1557 compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb) 1558 } 1559 1560 if lb.b { 1561 context.SetLabelPc(lb.f, code.LastPC()) 1562 code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr)) 1563 context.SetLabelPc(lb.t, code.LastPC()) 1564 code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr)) 1565 } 1566 1567 lastinst := code.Last() 1568 if opGetOpCode(lastinst) == OP_JMP && opGetArgSbx(lastinst) == endlabel { 1569 code.Pop() 1570 } 1571 1572 context.SetLabelPc(endlabel, code.LastPC()) 1573 } // }}} 1574 1575 func compileLogicalOpExprAux(context *funcContext, reg int, expr ast.Expr, ec *expcontext, thenlabel, elselabel int, hasnextcond bool, lb *lblabels) { // {{{ 1576 // TODO folding constants? 1577 code := context.Code 1578 flip := 0 1579 jumplabel := elselabel 1580 if hasnextcond { 1581 flip = 1 1582 jumplabel = thenlabel 1583 } 1584 1585 switch ex := expr.(type) { 1586 case *ast.FalseExpr: 1587 if elselabel == lb.e { 1588 code.AddASbx(OP_JMP, 0, lb.f, sline(expr)) 1589 lb.b = true 1590 } else { 1591 code.AddASbx(OP_JMP, 0, elselabel, sline(expr)) 1592 } 1593 return 1594 case *ast.NilExpr: 1595 if elselabel == lb.e { 1596 compileExpr(context, reg, expr, ec) 1597 code.AddASbx(OP_JMP, 0, lb.e, sline(expr)) 1598 } else { 1599 code.AddASbx(OP_JMP, 0, elselabel, sline(expr)) 1600 } 1601 return 1602 case *ast.TrueExpr: 1603 if thenlabel == lb.e { 1604 code.AddASbx(OP_JMP, 0, lb.t, sline(expr)) 1605 lb.b = true 1606 } else { 1607 code.AddASbx(OP_JMP, 0, thenlabel, sline(expr)) 1608 } 1609 return 1610 case *ast.NumberExpr, *ast.StringExpr: 1611 if thenlabel == lb.e { 1612 compileExpr(context, reg, expr, ec) 1613 code.AddASbx(OP_JMP, 0, lb.e, sline(expr)) 1614 } else { 1615 code.AddASbx(OP_JMP, 0, thenlabel, sline(expr)) 1616 } 1617 return 1618 case *ast.LogicalOpExpr: 1619 switch ex.Operator { 1620 case "and": 1621 nextcondlabel := context.NewLabel() 1622 compileLogicalOpExprAux(context, reg, ex.Lhs, ec, nextcondlabel, elselabel, false, lb) 1623 context.SetLabelPc(nextcondlabel, context.Code.LastPC()) 1624 compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb) 1625 case "or": 1626 nextcondlabel := context.NewLabel() 1627 compileLogicalOpExprAux(context, reg, ex.Lhs, ec, thenlabel, nextcondlabel, true, lb) 1628 context.SetLabelPc(nextcondlabel, context.Code.LastPC()) 1629 compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb) 1630 } 1631 return 1632 case *ast.RelationalOpExpr: 1633 if thenlabel == elselabel { 1634 flip ^= 1 1635 jumplabel = lb.t 1636 lb.b = true 1637 } else if thenlabel == lb.e { 1638 jumplabel = lb.t 1639 lb.b = true 1640 } else if elselabel == lb.e { 1641 jumplabel = lb.f 1642 lb.b = true 1643 } 1644 compileRelationalOpExprAux(context, reg, ex, flip, jumplabel) 1645 return 1646 } 1647 1648 a := reg 1649 sreg := savereg(ec, a) 1650 isLastAnd := elselabel == lb.e && thenlabel != elselabel 1651 isLastOr := thenlabel == lb.e && hasnextcond 1652 1653 if ident, ok := expr.(*ast.IdentExpr); ok && (isLastAnd || isLastOr) && getIdentRefType(context, context, ident) == ecLocal { 1654 b := context.FindLocalVar(ident.Value) 1655 op := OP_TESTSET 1656 if sreg == b { 1657 op = OP_TEST 1658 } 1659 code.AddABC(op, sreg, b, 0^flip, sline(expr)) 1660 } else if !hasnextcond && thenlabel == elselabel { 1661 reg += compileExpr(context, reg, expr, &expcontext{ec.ctype, intMax(a, sreg), ec.varargopt}) 1662 last := context.Code.Last() 1663 if opGetOpCode(last) == OP_MOVE && opGetArgA(last) == a { 1664 context.Code.SetA(context.Code.LastPC(), sreg) 1665 } else { 1666 context.Code.AddABC(OP_MOVE, sreg, a, 0, sline(expr)) 1667 } 1668 } else { 1669 reg += compileExpr(context, reg, expr, ecnone(0)) 1670 if !hasnextcond { 1671 code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr)) 1672 } else { 1673 code.AddABC(OP_TESTSET, sreg, a, 0^flip, sline(expr)) 1674 } 1675 } 1676 code.AddASbx(OP_JMP, 0, jumplabel, sline(expr)) 1677 } // }}} 1678 1679 func compileFuncCallExpr(context *funcContext, reg int, expr *ast.FuncCallExpr, ec *expcontext) int { // {{{ 1680 funcreg := reg 1681 if ec.ctype == ecLocal && ec.reg == (int(context.Proto.NumParameters)-1) { 1682 funcreg = ec.reg 1683 reg = ec.reg 1684 } 1685 argc := len(expr.Args) 1686 islastvararg := false 1687 name := "(anonymous)" 1688 1689 if expr.Func != nil { // hoge.func() 1690 reg += compileExpr(context, reg, expr.Func, ecnone(0)) 1691 name = getExprName(context, expr.Func) 1692 } else { // hoge:method() 1693 b := reg 1694 compileExprWithMVPropagation(context, expr.Receiver, ®, &b) 1695 c := loadRk(context, ®, expr, LString(expr.Method)) 1696 context.Code.AddABC(OP_SELF, funcreg, b, c, sline(expr)) 1697 // increments a register for an implicit "self" 1698 reg = b + 1 1699 reg2 := funcreg + 2 1700 if reg2 > reg { 1701 reg = reg2 1702 } 1703 argc += 1 1704 name = string(expr.Method) 1705 } 1706 1707 for i, ar := range expr.Args { 1708 islastvararg = (i == len(expr.Args)-1) && isVarArgReturnExpr(ar) 1709 if islastvararg { 1710 compileExpr(context, reg, ar, ecnone(-2)) 1711 } else { 1712 reg += compileExpr(context, reg, ar, ecnone(0)) 1713 } 1714 } 1715 b := argc + 1 1716 if islastvararg { 1717 b = 0 1718 } 1719 context.Code.AddABC(OP_CALL, funcreg, b, ec.varargopt+2, sline(expr)) 1720 context.Proto.DbgCalls = append(context.Proto.DbgCalls, DbgCall{Pc: context.Code.LastPC(), Name: name}) 1721 1722 if ec.varargopt == 0 && shouldmove(ec, funcreg) { 1723 context.Code.AddABC(OP_MOVE, ec.reg, funcreg, 0, sline(expr)) 1724 return 1 1725 } 1726 if context.RegTop() > (funcreg+2+ec.varargopt) || ec.varargopt < -1 { 1727 return 0 1728 } 1729 return ec.varargopt + 1 1730 } // }}} 1731 1732 func loadRk(context *funcContext, reg *int, expr ast.Expr, cnst LValue) int { // {{{ 1733 cindex := context.ConstIndex(cnst) 1734 if cindex <= opMaxIndexRk { 1735 return opRkAsk(cindex) 1736 } else { 1737 ret := *reg 1738 *reg++ 1739 context.Code.AddABx(OP_LOADK, ret, cindex, sline(expr)) 1740 return ret 1741 } 1742 } // }}} 1743 1744 func getIdentRefType(context *funcContext, current *funcContext, expr *ast.IdentExpr) expContextType { // {{{ 1745 if current == nil { 1746 return ecGlobal 1747 } else if current.FindLocalVar(expr.Value) > -1 { 1748 if current == context { 1749 return ecLocal 1750 } 1751 return ecUpvalue 1752 } 1753 return getIdentRefType(context, current.Parent, expr) 1754 } // }}} 1755 1756 func getExprName(context *funcContext, expr ast.Expr) string { // {{{ 1757 switch ex := expr.(type) { 1758 case *ast.IdentExpr: 1759 return ex.Value 1760 case *ast.AttrGetExpr: 1761 switch kex := ex.Key.(type) { 1762 case *ast.StringExpr: 1763 return kex.Value 1764 } 1765 return "?" 1766 } 1767 return "?" 1768 } // }}} 1769 1770 func patchCode(context *funcContext) { // {{{ 1771 maxreg := 1 1772 if np := int(context.Proto.NumParameters); np > 1 { 1773 maxreg = np 1774 } 1775 moven := 0 1776 code := context.Code.List() 1777 for pc := 0; pc < len(code); pc++ { 1778 inst := code[pc] 1779 curop := opGetOpCode(inst) 1780 switch curop { 1781 case OP_CLOSURE: 1782 pc += int(context.Proto.FunctionPrototypes[opGetArgBx(inst)].NumUpvalues) 1783 moven = 0 1784 continue 1785 case OP_SETGLOBAL, OP_SETUPVAL, OP_EQ, OP_LT, OP_LE, OP_TEST, 1786 OP_TAILCALL, OP_RETURN, OP_FORPREP, OP_FORLOOP, OP_TFORLOOP, 1787 OP_SETLIST, OP_CLOSE: 1788 /* nothing to do */ 1789 case OP_CALL: 1790 if reg := opGetArgA(inst) + opGetArgC(inst) - 2; reg > maxreg { 1791 maxreg = reg 1792 } 1793 case OP_VARARG: 1794 if reg := opGetArgA(inst) + opGetArgB(inst) - 1; reg > maxreg { 1795 maxreg = reg 1796 } 1797 case OP_SELF: 1798 if reg := opGetArgA(inst) + 1; reg > maxreg { 1799 maxreg = reg 1800 } 1801 case OP_LOADNIL: 1802 if reg := opGetArgB(inst); reg > maxreg { 1803 maxreg = reg 1804 } 1805 case OP_JMP: // jump to jump optimization 1806 distance := 0 1807 count := 0 // avoiding infinite loops 1808 for jmp := inst; opGetOpCode(jmp) == OP_JMP && count < 5; jmp = context.Code.At(pc + distance + 1) { 1809 d := context.GetLabelPc(opGetArgSbx(jmp)) - pc 1810 if d > opMaxArgSbx { 1811 if distance == 0 { 1812 raiseCompileError(context, context.Proto.LineDefined, "too long to jump.") 1813 } 1814 break 1815 } 1816 distance = d 1817 count++ 1818 } 1819 if distance == 0 { 1820 context.Code.SetOpCode(pc, OP_NOP) 1821 } else { 1822 context.Code.SetSbx(pc, distance) 1823 } 1824 default: 1825 if reg := opGetArgA(inst); reg > maxreg { 1826 maxreg = reg 1827 } 1828 } 1829 1830 // bulk move optimization(reducing op dipatch costs) 1831 if curop == OP_MOVE { 1832 moven++ 1833 } else { 1834 if moven > 1 { 1835 context.Code.SetOpCode(pc-moven, OP_MOVEN) 1836 context.Code.SetC(pc-moven, intMin(moven-1, opMaxArgsC)) 1837 } 1838 moven = 0 1839 } 1840 } 1841 maxreg++ 1842 if maxreg > maxRegisters { 1843 raiseCompileError(context, context.Proto.LineDefined, "register overflow(too many local variables)") 1844 } 1845 context.Proto.NumUsedRegisters = uint8(maxreg) 1846 } // }}} 1847 1848 func Compile(chunk []ast.Stmt, name string) (proto *FunctionProto, err error) { // {{{ 1849 defer func() { 1850 if rcv := recover(); rcv != nil { 1851 if _, ok := rcv.(*CompileError); ok { 1852 err = rcv.(error) 1853 } else { 1854 panic(rcv) 1855 } 1856 } 1857 }() 1858 err = nil 1859 parlist := &ast.ParList{HasVargs: true, Names: []string{}} 1860 funcexpr := &ast.FunctionExpr{ParList: parlist, Stmts: chunk} 1861 if len(chunk) > 0 { 1862 funcexpr.SetLastLine(sline(chunk[0])) 1863 funcexpr.SetLastLine(eline(chunk[len(chunk)-1]) + 1) 1864 } 1865 context := newFuncContext(name, nil) 1866 compileFunctionExpr(context, funcexpr, ecnone(0)) 1867 proto = context.Proto 1868 return 1869 } // }}}