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