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