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