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