123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869 |
- package lua
- import (
- "fmt"
- "math"
- "reflect"
- "github.com/yuin/gopher-lua/ast"
- )
- /* internal constants & structs {{{ */
- const maxRegisters = 200
- type expContextType int
- const (
- ecGlobal expContextType = iota
- ecUpvalue
- ecLocal
- ecTable
- ecVararg
- ecMethod
- ecNone
- )
- const regNotDefined = opMaxArgsA + 1
- const labelNoJump = 0
- type expcontext struct {
- ctype expContextType
- reg int
- // varargopt >= 0: wants varargopt+1 results, i.e a = func()
- // varargopt = -1: ignore results i.e func()
- // varargopt = -2: receive all results i.e a = {func()}
- varargopt int
- }
- type assigncontext struct {
- ec *expcontext
- keyrk int
- valuerk int
- keyks bool
- needmove bool
- }
- type lblabels struct {
- t int
- f int
- e int
- b bool
- }
- type constLValueExpr struct {
- ast.ExprBase
- Value LValue
- }
- // }}}
- /* utilities {{{ */
- var _ecnone0 = &expcontext{ecNone, regNotDefined, 0}
- var _ecnonem1 = &expcontext{ecNone, regNotDefined, -1}
- var _ecnonem2 = &expcontext{ecNone, regNotDefined, -2}
- var ecfuncdef = &expcontext{ecMethod, regNotDefined, 0}
- func ecupdate(ec *expcontext, ctype expContextType, reg, varargopt int) {
- if ec == _ecnone0 || ec == _ecnonem1 || ec == _ecnonem2 {
- panic("can not update ec cache")
- }
- ec.ctype = ctype
- ec.reg = reg
- ec.varargopt = varargopt
- }
- func ecnone(varargopt int) *expcontext {
- switch varargopt {
- case 0:
- return _ecnone0
- case -1:
- return _ecnonem1
- case -2:
- return _ecnonem2
- }
- return &expcontext{ecNone, regNotDefined, varargopt}
- }
- func shouldmove(ec *expcontext, reg int) bool {
- return ec.ctype == ecLocal && ec.reg != regNotDefined && ec.reg != reg
- }
- func sline(pos ast.PositionHolder) int {
- return pos.Line()
- }
- func eline(pos ast.PositionHolder) int {
- line := pos.LastLine()
- if line == 0 {
- return pos.Line()
- }
- return line
- }
- func savereg(ec *expcontext, reg int) int {
- if ec.ctype != ecLocal || ec.reg == regNotDefined {
- return reg
- }
- return ec.reg
- }
- func raiseCompileError(context *funcContext, line int, format string, args ...interface{}) {
- msg := fmt.Sprintf(format, args...)
- panic(&CompileError{context: context, Line: line, Message: msg})
- }
- func isVarArgReturnExpr(expr ast.Expr) bool {
- switch ex := expr.(type) {
- case *ast.FuncCallExpr:
- return !ex.AdjustRet
- case *ast.Comma3Expr:
- return !ex.AdjustRet
- }
- return false
- }
- func lnumberValue(expr ast.Expr) (LNumber, bool) {
- if ex, ok := expr.(*ast.NumberExpr); ok {
- lv, err := parseNumber(ex.Value)
- if err != nil {
- lv = LNumber(math.NaN())
- }
- return lv, true
- } else if ex, ok := expr.(*constLValueExpr); ok {
- return ex.Value.(LNumber), true
- }
- return 0, false
- }
- /* utilities }}} */
- type gotoLabelDesc struct { // {{{
- Id int
- Name string
- Pc int
- Line int
- NumActiveLocalVars int
- }
- func newLabelDesc(id int, name string, pc, line, n int) *gotoLabelDesc {
- return &gotoLabelDesc{
- Id: id,
- Name: name,
- Pc: pc,
- Line: line,
- NumActiveLocalVars: n,
- }
- }
- func (l *gotoLabelDesc) SetNumActiveLocalVars(n int) {
- l.NumActiveLocalVars = n
- } // }}}
- type CompileError struct { // {{{
- context *funcContext
- Line int
- Message string
- }
- func (e *CompileError) Error() string {
- return fmt.Sprintf("compile error near line(%v) %v: %v", e.Line, e.context.Proto.SourceName, e.Message)
- } // }}}
- type codeStore struct { // {{{
- codes []uint32
- lines []int
- pc int
- }
- func (cd *codeStore) Add(inst uint32, line int) {
- if l := len(cd.codes); l <= 0 || cd.pc == l {
- cd.codes = append(cd.codes, inst)
- cd.lines = append(cd.lines, line)
- } else {
- cd.codes[cd.pc] = inst
- cd.lines[cd.pc] = line
- }
- cd.pc++
- }
- func (cd *codeStore) AddABC(op int, a int, b int, c int, line int) {
- cd.Add(opCreateABC(op, a, b, c), line)
- }
- func (cd *codeStore) AddABx(op int, a int, bx int, line int) {
- cd.Add(opCreateABx(op, a, bx), line)
- }
- func (cd *codeStore) AddASbx(op int, a int, sbx int, line int) {
- cd.Add(opCreateASbx(op, a, sbx), line)
- }
- func (cd *codeStore) PropagateKMV(top int, save *int, reg *int, inc int) {
- lastinst := cd.Last()
- if opGetArgA(lastinst) >= top {
- switch opGetOpCode(lastinst) {
- case OP_LOADK:
- cindex := opGetArgBx(lastinst)
- if cindex <= opMaxIndexRk {
- cd.Pop()
- *save = opRkAsk(cindex)
- return
- }
- case OP_MOVE:
- cd.Pop()
- *save = opGetArgB(lastinst)
- return
- }
- }
- *save = *reg
- *reg = *reg + inc
- }
- func (cd *codeStore) PropagateMV(top int, save *int, reg *int, inc int) {
- lastinst := cd.Last()
- if opGetArgA(lastinst) >= top {
- switch opGetOpCode(lastinst) {
- case OP_MOVE:
- cd.Pop()
- *save = opGetArgB(lastinst)
- return
- }
- }
- *save = *reg
- *reg = *reg + inc
- }
- func (cd *codeStore) AddLoadNil(a, b, line int) {
- last := cd.Last()
- if opGetOpCode(last) == OP_LOADNIL && (opGetArgB(last)+1) == a {
- cd.SetB(cd.LastPC(), b)
- } else {
- cd.AddABC(OP_LOADNIL, a, b, 0, line)
- }
- }
- func (cd *codeStore) SetOpCode(pc int, v int) {
- opSetOpCode(&cd.codes[pc], v)
- }
- func (cd *codeStore) SetA(pc int, v int) {
- opSetArgA(&cd.codes[pc], v)
- }
- func (cd *codeStore) SetB(pc int, v int) {
- opSetArgB(&cd.codes[pc], v)
- }
- func (cd *codeStore) SetC(pc int, v int) {
- opSetArgC(&cd.codes[pc], v)
- }
- func (cd *codeStore) SetBx(pc int, v int) {
- opSetArgBx(&cd.codes[pc], v)
- }
- func (cd *codeStore) SetSbx(pc int, v int) {
- opSetArgSbx(&cd.codes[pc], v)
- }
- func (cd *codeStore) At(pc int) uint32 {
- return cd.codes[pc]
- }
- func (cd *codeStore) List() []uint32 {
- return cd.codes[:cd.pc]
- }
- func (cd *codeStore) PosList() []int {
- return cd.lines[:cd.pc]
- }
- func (cd *codeStore) LastPC() int {
- return cd.pc - 1
- }
- func (cd *codeStore) Last() uint32 {
- if cd.pc == 0 {
- return opInvalidInstruction
- }
- return cd.codes[cd.pc-1]
- }
- func (cd *codeStore) Pop() {
- cd.pc--
- } /* }}} Code */
- /* {{{ VarNamePool */
- type varNamePoolValue struct {
- Index int
- Name string
- }
- type varNamePool struct {
- names []string
- offset int
- }
- func newVarNamePool(offset int) *varNamePool {
- return &varNamePool{make([]string, 0, 16), offset}
- }
- func (vp *varNamePool) Names() []string {
- return vp.names
- }
- func (vp *varNamePool) List() []varNamePoolValue {
- result := make([]varNamePoolValue, len(vp.names), len(vp.names))
- for i, name := range vp.names {
- result[i].Index = i + vp.offset
- result[i].Name = name
- }
- return result
- }
- func (vp *varNamePool) LastIndex() int {
- return vp.offset + len(vp.names)
- }
- func (vp *varNamePool) Find(name string) int {
- for i := len(vp.names) - 1; i >= 0; i-- {
- if vp.names[i] == name {
- return i + vp.offset
- }
- }
- return -1
- }
- func (vp *varNamePool) RegisterUnique(name string) int {
- index := vp.Find(name)
- if index < 0 {
- return vp.Register(name)
- }
- return index
- }
- func (vp *varNamePool) Register(name string) int {
- vp.names = append(vp.names, name)
- return len(vp.names) - 1 + vp.offset
- }
- /* }}} VarNamePool */
- /* FuncContext {{{ */
- type codeBlock struct {
- LocalVars *varNamePool
- BreakLabel int
- Parent *codeBlock
- RefUpvalue bool
- LineStart int
- LastLine int
- labels map[string]*gotoLabelDesc
- firstGotoIndex int
- }
- func newCodeBlock(localvars *varNamePool, blabel int, parent *codeBlock, pos ast.PositionHolder, firstGotoIndex int) *codeBlock {
- bl := &codeBlock{localvars, blabel, parent, false, 0, 0, map[string]*gotoLabelDesc{}, firstGotoIndex}
- if pos != nil {
- bl.LineStart = pos.Line()
- bl.LastLine = pos.LastLine()
- }
- return bl
- }
- func (b *codeBlock) AddLabel(label *gotoLabelDesc) *gotoLabelDesc {
- if old, ok := b.labels[label.Name]; ok {
- return old
- }
- b.labels[label.Name] = label
- return nil
- }
- func (b *codeBlock) GetLabel(label string) *gotoLabelDesc {
- if v, ok := b.labels[label]; ok {
- return v
- }
- return nil
- }
- func (b *codeBlock) LocalVarsCount() int {
- count := 0
- for block := b; block != nil; block = block.Parent {
- count += len(block.LocalVars.Names())
- }
- return count
- }
- type funcContext struct {
- Proto *FunctionProto
- Code *codeStore
- Parent *funcContext
- Upvalues *varNamePool
- Block *codeBlock
- Blocks []*codeBlock
- regTop int
- labelId int
- labelPc map[int]int
- gotosCount int
- unresolvedGotos map[int]*gotoLabelDesc
- }
- func newFuncContext(sourcename string, parent *funcContext) *funcContext {
- fc := &funcContext{
- Proto: newFunctionProto(sourcename),
- Code: &codeStore{make([]uint32, 0, 1024), make([]int, 0, 1024), 0},
- Parent: parent,
- Upvalues: newVarNamePool(0),
- Block: newCodeBlock(newVarNamePool(0), labelNoJump, nil, nil, 0),
- regTop: 0,
- labelId: 1,
- labelPc: map[int]int{},
- gotosCount: 0,
- unresolvedGotos: map[int]*gotoLabelDesc{},
- }
- fc.Blocks = []*codeBlock{fc.Block}
- return fc
- }
- func (fc *funcContext) CheckUnresolvedGoto() {
- for i := fc.Block.firstGotoIndex; i < fc.gotosCount; i++ {
- gotoLabel, ok := fc.unresolvedGotos[i]
- if !ok {
- continue
- }
- raiseCompileError(fc, fc.Proto.LastLineDefined, "no visible label '%s' for <goto> at line %d", gotoLabel.Name, gotoLabel.Line)
- }
- }
- func (fc *funcContext) AddUnresolvedGoto(label *gotoLabelDesc) {
- fc.unresolvedGotos[fc.gotosCount] = label
- fc.gotosCount++
- }
- func (fc *funcContext) AddNamedLabel(label *gotoLabelDesc) {
- if old := fc.Block.AddLabel(label); old != nil {
- raiseCompileError(fc, label.Line+1, "label '%s' already defined on line %d", label.Name, old.Line)
- }
- fc.SetLabelPc(label.Id, label.Pc)
- }
- func (fc *funcContext) GetNamedLabel(name string) *gotoLabelDesc {
- return fc.Block.GetLabel(name)
- }
- func (fc *funcContext) ResolveGoto(from, to *gotoLabelDesc, index int) {
- if from.NumActiveLocalVars < to.NumActiveLocalVars {
- varName := fc.Block.LocalVars.Names()[len(fc.Block.LocalVars.Names())-1]
- raiseCompileError(fc, to.Line+1, "<goto %s> at line %d jumps into the scope of local '%s'", to.Name, from.Line, varName)
- }
- fc.Code.SetSbx(from.Pc, to.Id)
- delete(fc.unresolvedGotos, index)
- }
- func (fc *funcContext) FindLabel(block *codeBlock, gotoLabel *gotoLabelDesc, i int) bool {
- target := block.GetLabel(gotoLabel.Name)
- if target != nil {
- if gotoLabel.NumActiveLocalVars > target.NumActiveLocalVars && block.RefUpvalue {
- fc.Code.SetA(gotoLabel.Pc-1, target.NumActiveLocalVars)
- }
- fc.ResolveGoto(gotoLabel, target, i)
- return true
- }
- return false
- }
- func (fc *funcContext) ResolveCurrentBlockGotosWithParentBlock() {
- blockActiveLocalVars := fc.Block.Parent.LocalVarsCount()
- for i := fc.Block.firstGotoIndex; i < fc.gotosCount; i++ {
- gotoLabel, ok := fc.unresolvedGotos[i]
- if !ok {
- continue
- }
- if gotoLabel.NumActiveLocalVars > blockActiveLocalVars {
- if fc.Block.RefUpvalue {
- fc.Code.SetA(gotoLabel.Pc-1, blockActiveLocalVars)
- }
- gotoLabel.SetNumActiveLocalVars(blockActiveLocalVars)
- }
- fc.FindLabel(fc.Block.Parent, gotoLabel, i)
- }
- }
- func (fc *funcContext) ResolveForwardGoto(target *gotoLabelDesc) {
- for i := fc.Block.firstGotoIndex; i <= fc.gotosCount; i++ {
- gotoLabel, ok := fc.unresolvedGotos[i]
- if !ok {
- continue
- }
- if gotoLabel.Name == target.Name {
- fc.ResolveGoto(gotoLabel, target, i)
- }
- }
- }
- func (fc *funcContext) NewLabel() int {
- ret := fc.labelId
- fc.labelId++
- return ret
- }
- func (fc *funcContext) SetLabelPc(label int, pc int) {
- fc.labelPc[label] = pc
- }
- func (fc *funcContext) GetLabelPc(label int) int {
- return fc.labelPc[label]
- }
- func (fc *funcContext) ConstIndex(value LValue) int {
- ctype := value.Type()
- for i, lv := range fc.Proto.Constants {
- if lv.Type() == ctype && lv == value {
- return i
- }
- }
- fc.Proto.Constants = append(fc.Proto.Constants, value)
- v := len(fc.Proto.Constants) - 1
- if v > opMaxArgBx {
- raiseCompileError(fc, fc.Proto.LineDefined, "too many constants")
- }
- return v
- }
- func (fc *funcContext) BlockLocalVarsCount() int {
- count := 0
- for block := fc.Block; block != nil; block = block.Parent {
- count += len(block.LocalVars.Names())
- }
- return count
- }
- func (fc *funcContext) RegisterLocalVar(name string) int {
- ret := fc.Block.LocalVars.Register(name)
- fc.Proto.DbgLocals = append(fc.Proto.DbgLocals, &DbgLocalInfo{Name: name, StartPc: fc.Code.LastPC() + 1})
- fc.SetRegTop(fc.RegTop() + 1)
- return ret
- }
- func (fc *funcContext) FindLocalVarAndBlock(name string) (int, *codeBlock) {
- for block := fc.Block; block != nil; block = block.Parent {
- if index := block.LocalVars.Find(name); index > -1 {
- return index, block
- }
- }
- return -1, nil
- }
- func (fc *funcContext) FindLocalVar(name string) int {
- idx, _ := fc.FindLocalVarAndBlock(name)
- return idx
- }
- func (fc *funcContext) LocalVars() []varNamePoolValue {
- result := make([]varNamePoolValue, 0, 32)
- for _, block := range fc.Blocks {
- result = append(result, block.LocalVars.List()...)
- }
- return result
- }
- func (fc *funcContext) EnterBlock(blabel int, pos ast.PositionHolder) {
- fc.Block = newCodeBlock(newVarNamePool(fc.RegTop()), blabel, fc.Block, pos, fc.gotosCount)
- fc.Blocks = append(fc.Blocks, fc.Block)
- }
- func (fc *funcContext) CloseUpvalues() int {
- n := -1
- if fc.Block.RefUpvalue {
- n = fc.Block.Parent.LocalVars.LastIndex()
- fc.Code.AddABC(OP_CLOSE, n, 0, 0, fc.Block.LastLine)
- }
- return n
- }
- func (fc *funcContext) LeaveBlock() int {
- closed := fc.CloseUpvalues()
- fc.EndScope()
- if fc.Block.Parent != nil {
- fc.ResolveCurrentBlockGotosWithParentBlock()
- }
- fc.Block = fc.Block.Parent
- fc.SetRegTop(fc.Block.LocalVars.LastIndex())
- return closed
- }
- func (fc *funcContext) EndScope() {
- for _, vr := range fc.Block.LocalVars.List() {
- fc.Proto.DbgLocals[vr.Index].EndPc = fc.Code.LastPC()
- }
- }
- func (fc *funcContext) SetRegTop(top int) {
- if top > maxRegisters {
- raiseCompileError(fc, fc.Proto.LineDefined, "too many local variables")
- }
- fc.regTop = top
- }
- func (fc *funcContext) RegTop() int {
- return fc.regTop
- }
- /* FuncContext }}} */
- func compileChunk(context *funcContext, chunk []ast.Stmt, untilFollows bool) { // {{{
- for i, stmt := range chunk {
- lastStmt := true
- for j := i + 1; j < len(chunk); j++ {
- _, ok := chunk[j].(*ast.LabelStmt)
- if !ok {
- lastStmt = false
- break
- }
- }
- compileStmt(context, stmt, lastStmt && !untilFollows)
- }
- } // }}}
- func compileBlock(context *funcContext, chunk []ast.Stmt) { // {{{
- if len(chunk) == 0 {
- return
- }
- ph := &ast.Node{}
- ph.SetLine(sline(chunk[0]))
- ph.SetLastLine(eline(chunk[len(chunk)-1]))
- context.EnterBlock(labelNoJump, ph)
- for i, stmt := range chunk {
- lastStmt := true
- for j := i + 1; j < len(chunk); j++ {
- _, ok := chunk[j].(*ast.LabelStmt)
- if !ok {
- lastStmt = false
- break
- }
- }
- compileStmt(context, stmt, lastStmt)
- }
- context.LeaveBlock()
- } // }}}
- func compileStmt(context *funcContext, stmt ast.Stmt, isLastStmt bool) { // {{{
- switch st := stmt.(type) {
- case *ast.AssignStmt:
- compileAssignStmt(context, st)
- case *ast.LocalAssignStmt:
- compileLocalAssignStmt(context, st)
- case *ast.FuncCallStmt:
- compileFuncCallExpr(context, context.RegTop(), st.Expr.(*ast.FuncCallExpr), ecnone(-1))
- case *ast.DoBlockStmt:
- context.EnterBlock(labelNoJump, st)
- compileChunk(context, st.Stmts, false)
- context.LeaveBlock()
- case *ast.WhileStmt:
- compileWhileStmt(context, st)
- case *ast.RepeatStmt:
- compileRepeatStmt(context, st)
- case *ast.FuncDefStmt:
- compileFuncDefStmt(context, st)
- case *ast.ReturnStmt:
- compileReturnStmt(context, st)
- case *ast.IfStmt:
- compileIfStmt(context, st)
- case *ast.BreakStmt:
- compileBreakStmt(context, st)
- case *ast.NumberForStmt:
- compileNumberForStmt(context, st)
- case *ast.GenericForStmt:
- compileGenericForStmt(context, st)
- case *ast.LabelStmt:
- compileLabelStmt(context, st, isLastStmt)
- case *ast.GotoStmt:
- compileGotoStmt(context, st)
- }
- } // }}}
- func compileAssignStmtLeft(context *funcContext, stmt *ast.AssignStmt) (int, []*assigncontext) { // {{{
- reg := context.RegTop()
- acs := make([]*assigncontext, 0, len(stmt.Lhs))
- for _, lhs := range stmt.Lhs {
- switch st := lhs.(type) {
- case *ast.IdentExpr:
- identtype := getIdentRefType(context, context, st)
- ec := &expcontext{identtype, regNotDefined, 0}
- switch identtype {
- case ecGlobal:
- context.ConstIndex(LString(st.Value))
- case ecUpvalue:
- context.Upvalues.RegisterUnique(st.Value)
- case ecLocal:
- ec.reg = context.FindLocalVar(st.Value)
- }
- acs = append(acs, &assigncontext{ec, 0, 0, false, false})
- case *ast.AttrGetExpr:
- ac := &assigncontext{&expcontext{ecTable, regNotDefined, 0}, 0, 0, false, false}
- compileExprWithKMVPropagation(context, st.Object, ®, &ac.ec.reg)
- ac.keyrk = reg
- reg += compileExpr(context, reg, st.Key, ecnone(0))
- if _, ok := st.Key.(*ast.StringExpr); ok {
- ac.keyks = true
- }
- acs = append(acs, ac)
- default:
- panic("invalid left expression.")
- }
- }
- return reg, acs
- } // }}}
- func compileAssignStmtRight(context *funcContext, stmt *ast.AssignStmt, reg int, acs []*assigncontext) (int, []*assigncontext) { // {{{
- lennames := len(stmt.Lhs)
- lenexprs := len(stmt.Rhs)
- namesassigned := 0
- for namesassigned < lennames {
- ac := acs[namesassigned]
- ec := ac.ec
- var expr ast.Expr = nil
- if namesassigned >= lenexprs {
- expr = &ast.NilExpr{}
- expr.SetLine(sline(stmt.Lhs[namesassigned]))
- expr.SetLastLine(eline(stmt.Lhs[namesassigned]))
- } else if isVarArgReturnExpr(stmt.Rhs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 {
- varargopt := lennames - namesassigned - 1
- regstart := reg
- reginc := compileExpr(context, reg, stmt.Rhs[namesassigned], ecnone(varargopt))
- reg += reginc
- for i := namesassigned; i < namesassigned+int(reginc); i++ {
- acs[i].needmove = true
- if acs[i].ec.ctype == ecTable {
- acs[i].valuerk = regstart + (i - namesassigned)
- }
- }
- namesassigned = lennames
- continue
- }
- if expr == nil {
- expr = stmt.Rhs[namesassigned]
- }
- idx := reg
- reginc := compileExpr(context, reg, expr, ec)
- if ec.ctype == ecTable {
- if _, ok := expr.(*ast.LogicalOpExpr); !ok {
- context.Code.PropagateKMV(context.RegTop(), &ac.valuerk, ®, reginc)
- } else {
- ac.valuerk = idx
- reg += reginc
- }
- } else {
- ac.needmove = reginc != 0
- reg += reginc
- }
- namesassigned += 1
- }
- rightreg := reg - 1
- // extra right exprs
- for i := namesassigned; i < lenexprs; i++ {
- varargopt := -1
- if i != lenexprs-1 {
- varargopt = 0
- }
- reg += compileExpr(context, reg, stmt.Rhs[i], ecnone(varargopt))
- }
- return rightreg, acs
- } // }}}
- func compileAssignStmt(context *funcContext, stmt *ast.AssignStmt) { // {{{
- code := context.Code
- lennames := len(stmt.Lhs)
- reg, acs := compileAssignStmtLeft(context, stmt)
- reg, acs = compileAssignStmtRight(context, stmt, reg, acs)
- for i := lennames - 1; i >= 0; i-- {
- ex := stmt.Lhs[i]
- switch acs[i].ec.ctype {
- case ecLocal:
- if acs[i].needmove {
- code.AddABC(OP_MOVE, context.FindLocalVar(ex.(*ast.IdentExpr).Value), reg, 0, sline(ex))
- reg -= 1
- }
- case ecGlobal:
- code.AddABx(OP_SETGLOBAL, reg, context.ConstIndex(LString(ex.(*ast.IdentExpr).Value)), sline(ex))
- reg -= 1
- case ecUpvalue:
- code.AddABC(OP_SETUPVAL, reg, context.Upvalues.RegisterUnique(ex.(*ast.IdentExpr).Value), 0, sline(ex))
- reg -= 1
- case ecTable:
- opcode := OP_SETTABLE
- if acs[i].keyks {
- opcode = OP_SETTABLEKS
- }
- code.AddABC(opcode, acs[i].ec.reg, acs[i].keyrk, acs[i].valuerk, sline(ex))
- if !opIsK(acs[i].valuerk) {
- reg -= 1
- }
- }
- }
- } // }}}
- func compileRegAssignment(context *funcContext, names []string, exprs []ast.Expr, reg int, nvars int, line int) { // {{{
- lennames := len(names)
- lenexprs := len(exprs)
- namesassigned := 0
- ec := &expcontext{}
- for namesassigned < lennames && namesassigned < lenexprs {
- if isVarArgReturnExpr(exprs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 {
- varargopt := nvars - namesassigned
- ecupdate(ec, ecVararg, reg, varargopt-1)
- compileExpr(context, reg, exprs[namesassigned], ec)
- reg += varargopt
- namesassigned = lennames
- } else {
- ecupdate(ec, ecLocal, reg, 0)
- compileExpr(context, reg, exprs[namesassigned], ec)
- reg += 1
- namesassigned += 1
- }
- }
- // extra left names
- if lennames > namesassigned {
- restleft := lennames - namesassigned - 1
- context.Code.AddLoadNil(reg, reg+restleft, line)
- reg += restleft
- }
- // extra right exprs
- for i := namesassigned; i < lenexprs; i++ {
- varargopt := -1
- if i != lenexprs-1 {
- varargopt = 0
- }
- ecupdate(ec, ecNone, reg, varargopt)
- reg += compileExpr(context, reg, exprs[i], ec)
- }
- } // }}}
- func compileLocalAssignStmt(context *funcContext, stmt *ast.LocalAssignStmt) { // {{{
- reg := context.RegTop()
- if len(stmt.Names) == 1 && len(stmt.Exprs) == 1 {
- if _, ok := stmt.Exprs[0].(*ast.FunctionExpr); ok {
- context.RegisterLocalVar(stmt.Names[0])
- compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt))
- return
- }
- }
- compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt))
- for _, name := range stmt.Names {
- context.RegisterLocalVar(name)
- }
- } // }}}
- func compileReturnStmt(context *funcContext, stmt *ast.ReturnStmt) { // {{{
- lenexprs := len(stmt.Exprs)
- code := context.Code
- reg := context.RegTop()
- a := reg
- lastisvaarg := false
- if lenexprs == 1 {
- switch ex := stmt.Exprs[0].(type) {
- case *ast.IdentExpr:
- if idx := context.FindLocalVar(ex.Value); idx > -1 {
- code.AddABC(OP_RETURN, idx, 2, 0, sline(stmt))
- return
- }
- case *ast.FuncCallExpr:
- if ex.AdjustRet { // return (func())
- reg += compileExpr(context, reg, ex, ecnone(0))
- } else {
- reg += compileExpr(context, reg, ex, ecnone(-2))
- code.SetOpCode(code.LastPC(), OP_TAILCALL)
- }
- code.AddABC(OP_RETURN, a, 0, 0, sline(stmt))
- return
- }
- }
- for i, expr := range stmt.Exprs {
- if i == lenexprs-1 && isVarArgReturnExpr(expr) {
- compileExpr(context, reg, expr, ecnone(-2))
- lastisvaarg = true
- } else {
- reg += compileExpr(context, reg, expr, ecnone(0))
- }
- }
- count := reg - a + 1
- if lastisvaarg {
- count = 0
- }
- context.Code.AddABC(OP_RETURN, a, count, 0, sline(stmt))
- } // }}}
- func compileIfStmt(context *funcContext, stmt *ast.IfStmt) { // {{{
- thenlabel := context.NewLabel()
- elselabel := context.NewLabel()
- endlabel := context.NewLabel()
- compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
- context.SetLabelPc(thenlabel, context.Code.LastPC())
- compileBlock(context, stmt.Then)
- if len(stmt.Else) > 0 {
- context.Code.AddASbx(OP_JMP, 0, endlabel, sline(stmt))
- }
- context.SetLabelPc(elselabel, context.Code.LastPC())
- if len(stmt.Else) > 0 {
- compileBlock(context, stmt.Else)
- context.SetLabelPc(endlabel, context.Code.LastPC())
- }
- } // }}}
- func compileBranchCondition(context *funcContext, reg int, expr ast.Expr, thenlabel, elselabel int, hasnextcond bool) { // {{{
- // TODO folding constants?
- code := context.Code
- flip := 0
- jumplabel := elselabel
- if hasnextcond {
- flip = 1
- jumplabel = thenlabel
- }
- switch ex := expr.(type) {
- case *ast.FalseExpr, *ast.NilExpr:
- if !hasnextcond {
- code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
- return
- }
- case *ast.TrueExpr, *ast.NumberExpr, *ast.StringExpr:
- if !hasnextcond {
- return
- }
- case *ast.UnaryNotOpExpr:
- compileBranchCondition(context, reg, ex.Expr, elselabel, thenlabel, !hasnextcond)
- return
- case *ast.LogicalOpExpr:
- switch ex.Operator {
- case "and":
- nextcondlabel := context.NewLabel()
- compileBranchCondition(context, reg, ex.Lhs, nextcondlabel, elselabel, false)
- context.SetLabelPc(nextcondlabel, context.Code.LastPC())
- compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond)
- case "or":
- nextcondlabel := context.NewLabel()
- compileBranchCondition(context, reg, ex.Lhs, thenlabel, nextcondlabel, true)
- context.SetLabelPc(nextcondlabel, context.Code.LastPC())
- compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond)
- }
- return
- case *ast.RelationalOpExpr:
- compileRelationalOpExprAux(context, reg, ex, flip, jumplabel)
- return
- }
- a := reg
- compileExprWithMVPropagation(context, expr, ®, &a)
- code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr))
- code.AddASbx(OP_JMP, 0, jumplabel, sline(expr))
- } // }}}
- func compileWhileStmt(context *funcContext, stmt *ast.WhileStmt) { // {{{
- thenlabel := context.NewLabel()
- elselabel := context.NewLabel()
- condlabel := context.NewLabel()
- context.SetLabelPc(condlabel, context.Code.LastPC())
- compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
- context.SetLabelPc(thenlabel, context.Code.LastPC())
- context.EnterBlock(elselabel, stmt)
- compileChunk(context, stmt.Stmts, false)
- context.CloseUpvalues()
- context.Code.AddASbx(OP_JMP, 0, condlabel, eline(stmt))
- context.LeaveBlock()
- context.SetLabelPc(elselabel, context.Code.LastPC())
- } // }}}
- func compileRepeatStmt(context *funcContext, stmt *ast.RepeatStmt) { // {{{
- initlabel := context.NewLabel()
- thenlabel := context.NewLabel()
- elselabel := context.NewLabel()
- context.SetLabelPc(initlabel, context.Code.LastPC())
- context.SetLabelPc(elselabel, context.Code.LastPC())
- context.EnterBlock(thenlabel, stmt)
- compileChunk(context, stmt.Stmts, true)
- compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
- context.SetLabelPc(thenlabel, context.Code.LastPC())
- n := context.LeaveBlock()
- if n > -1 {
- label := context.NewLabel()
- context.Code.AddASbx(OP_JMP, 0, label, eline(stmt))
- context.SetLabelPc(elselabel, context.Code.LastPC())
- context.Code.AddABC(OP_CLOSE, n, 0, 0, eline(stmt))
- context.Code.AddASbx(OP_JMP, 0, initlabel, eline(stmt))
- context.SetLabelPc(label, context.Code.LastPC())
- }
- } // }}}
- func compileBreakStmt(context *funcContext, stmt *ast.BreakStmt) { // {{{
- for block := context.Block; block != nil; block = block.Parent {
- if label := block.BreakLabel; label != labelNoJump {
- if block.RefUpvalue {
- context.Code.AddABC(OP_CLOSE, block.Parent.LocalVars.LastIndex(), 0, 0, sline(stmt))
- }
- context.Code.AddASbx(OP_JMP, 0, label, sline(stmt))
- return
- }
- }
- raiseCompileError(context, sline(stmt), "no loop to break")
- } // }}}
- func compileFuncDefStmt(context *funcContext, stmt *ast.FuncDefStmt) { // {{{
- if stmt.Name.Func == nil {
- reg := context.RegTop()
- var treg, kreg int
- compileExprWithKMVPropagation(context, stmt.Name.Receiver, ®, &treg)
- kreg = loadRk(context, ®, stmt.Func, LString(stmt.Name.Method))
- compileExpr(context, reg, stmt.Func, ecfuncdef)
- context.Code.AddABC(OP_SETTABLE, treg, kreg, reg, sline(stmt.Name.Receiver))
- } else {
- astmt := &ast.AssignStmt{Lhs: []ast.Expr{stmt.Name.Func}, Rhs: []ast.Expr{stmt.Func}}
- astmt.SetLine(sline(stmt.Func))
- astmt.SetLastLine(eline(stmt.Func))
- compileAssignStmt(context, astmt)
- }
- } // }}}
- func compileNumberForStmt(context *funcContext, stmt *ast.NumberForStmt) { // {{{
- code := context.Code
- endlabel := context.NewLabel()
- ec := &expcontext{}
- context.EnterBlock(endlabel, stmt)
- reg := context.RegTop()
- rindex := context.RegisterLocalVar("(for index)")
- ecupdate(ec, ecLocal, rindex, 0)
- compileExpr(context, reg, stmt.Init, ec)
- reg = context.RegTop()
- rlimit := context.RegisterLocalVar("(for limit)")
- ecupdate(ec, ecLocal, rlimit, 0)
- compileExpr(context, reg, stmt.Limit, ec)
- reg = context.RegTop()
- rstep := context.RegisterLocalVar("(for step)")
- if stmt.Step == nil {
- stmt.Step = &ast.NumberExpr{Value: "1"}
- stmt.Step.SetLine(sline(stmt.Init))
- }
- ecupdate(ec, ecLocal, rstep, 0)
- compileExpr(context, reg, stmt.Step, ec)
- code.AddASbx(OP_FORPREP, rindex, 0, sline(stmt))
- context.RegisterLocalVar(stmt.Name)
- bodypc := code.LastPC()
- compileChunk(context, stmt.Stmts, false)
- context.LeaveBlock()
- flpc := code.LastPC()
- code.AddASbx(OP_FORLOOP, rindex, bodypc-(flpc+1), sline(stmt))
- context.SetLabelPc(endlabel, code.LastPC())
- code.SetSbx(bodypc, flpc-bodypc)
- } // }}}
- func compileGenericForStmt(context *funcContext, stmt *ast.GenericForStmt) { // {{{
- code := context.Code
- endlabel := context.NewLabel()
- bodylabel := context.NewLabel()
- fllabel := context.NewLabel()
- nnames := len(stmt.Names)
- context.EnterBlock(endlabel, stmt)
- rgen := context.RegisterLocalVar("(for generator)")
- context.RegisterLocalVar("(for state)")
- context.RegisterLocalVar("(for control)")
- compileRegAssignment(context, stmt.Names, stmt.Exprs, context.RegTop()-3, 3, sline(stmt))
- code.AddASbx(OP_JMP, 0, fllabel, sline(stmt))
- for _, name := range stmt.Names {
- context.RegisterLocalVar(name)
- }
- context.SetLabelPc(bodylabel, code.LastPC())
- compileChunk(context, stmt.Stmts, false)
- context.LeaveBlock()
- context.SetLabelPc(fllabel, code.LastPC())
- code.AddABC(OP_TFORLOOP, rgen, 0, nnames, sline(stmt))
- code.AddASbx(OP_JMP, 0, bodylabel, sline(stmt))
- context.SetLabelPc(endlabel, code.LastPC())
- } // }}}
- func compileLabelStmt(context *funcContext, stmt *ast.LabelStmt, isLastStmt bool) { // {{{
- labelId := context.NewLabel()
- label := newLabelDesc(labelId, stmt.Name, context.Code.LastPC(), sline(stmt), context.BlockLocalVarsCount())
- context.AddNamedLabel(label)
- if isLastStmt {
- label.SetNumActiveLocalVars(context.Block.Parent.LocalVarsCount())
- }
- context.ResolveForwardGoto(label)
- } // }}}
- func compileGotoStmt(context *funcContext, stmt *ast.GotoStmt) { // {{{
- context.Code.AddABC(OP_CLOSE, 0, 0, 0, sline(stmt))
- context.Code.AddASbx(OP_JMP, 0, labelNoJump, sline(stmt))
- label := newLabelDesc(-1, stmt.Label, context.Code.LastPC(), sline(stmt), context.BlockLocalVarsCount())
- context.AddUnresolvedGoto(label)
- context.FindLabel(context.Block, label, context.gotosCount-1)
- } // }}}
- func compileExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) int { // {{{
- code := context.Code
- sreg := savereg(ec, reg)
- sused := 1
- if sreg < reg {
- sused = 0
- }
- switch ex := expr.(type) {
- case *ast.StringExpr:
- code.AddABx(OP_LOADK, sreg, context.ConstIndex(LString(ex.Value)), sline(ex))
- return sused
- case *ast.NumberExpr:
- num, err := parseNumber(ex.Value)
- if err != nil {
- num = LNumber(math.NaN())
- }
- code.AddABx(OP_LOADK, sreg, context.ConstIndex(num), sline(ex))
- return sused
- case *constLValueExpr:
- code.AddABx(OP_LOADK, sreg, context.ConstIndex(ex.Value), sline(ex))
- return sused
- case *ast.NilExpr:
- code.AddLoadNil(sreg, sreg, sline(ex))
- return sused
- case *ast.FalseExpr:
- code.AddABC(OP_LOADBOOL, sreg, 0, 0, sline(ex))
- return sused
- case *ast.TrueExpr:
- code.AddABC(OP_LOADBOOL, sreg, 1, 0, sline(ex))
- return sused
- case *ast.IdentExpr:
- switch getIdentRefType(context, context, ex) {
- case ecGlobal:
- code.AddABx(OP_GETGLOBAL, sreg, context.ConstIndex(LString(ex.Value)), sline(ex))
- case ecUpvalue:
- code.AddABC(OP_GETUPVAL, sreg, context.Upvalues.RegisterUnique(ex.Value), 0, sline(ex))
- case ecLocal:
- b := context.FindLocalVar(ex.Value)
- code.AddABC(OP_MOVE, sreg, b, 0, sline(ex))
- }
- return sused
- case *ast.Comma3Expr:
- if context.Proto.IsVarArg == 0 {
- raiseCompileError(context, sline(ex), "cannot use '...' outside a vararg function")
- }
- context.Proto.IsVarArg &= ^VarArgNeedsArg
- code.AddABC(OP_VARARG, sreg, 2+ec.varargopt, 0, sline(ex))
- if context.RegTop() > (sreg+2+ec.varargopt) || ec.varargopt < -1 {
- return 0
- }
- return (sreg + 1 + ec.varargopt) - reg
- case *ast.AttrGetExpr:
- a := sreg
- b := reg
- compileExprWithMVPropagation(context, ex.Object, ®, &b)
- c := reg
- compileExprWithKMVPropagation(context, ex.Key, ®, &c)
- opcode := OP_GETTABLE
- if _, ok := ex.Key.(*ast.StringExpr); ok {
- opcode = OP_GETTABLEKS
- }
- code.AddABC(opcode, a, b, c, sline(ex))
- return sused
- case *ast.TableExpr:
- compileTableExpr(context, reg, ex, ec)
- return 1
- case *ast.ArithmeticOpExpr:
- compileArithmeticOpExpr(context, reg, ex, ec)
- return sused
- case *ast.StringConcatOpExpr:
- compileStringConcatOpExpr(context, reg, ex, ec)
- return sused
- case *ast.UnaryMinusOpExpr, *ast.UnaryNotOpExpr, *ast.UnaryLenOpExpr:
- compileUnaryOpExpr(context, reg, ex, ec)
- return sused
- case *ast.RelationalOpExpr:
- compileRelationalOpExpr(context, reg, ex, ec)
- return sused
- case *ast.LogicalOpExpr:
- compileLogicalOpExpr(context, reg, ex, ec)
- return sused
- case *ast.FuncCallExpr:
- return compileFuncCallExpr(context, reg, ex, ec)
- case *ast.FunctionExpr:
- childcontext := newFuncContext(context.Proto.SourceName, context)
- compileFunctionExpr(childcontext, ex, ec)
- protono := len(context.Proto.FunctionPrototypes)
- context.Proto.FunctionPrototypes = append(context.Proto.FunctionPrototypes, childcontext.Proto)
- code.AddABx(OP_CLOSURE, sreg, protono, sline(ex))
- for _, upvalue := range childcontext.Upvalues.List() {
- localidx, block := context.FindLocalVarAndBlock(upvalue.Name)
- if localidx > -1 {
- code.AddABC(OP_MOVE, 0, localidx, 0, sline(ex))
- block.RefUpvalue = true
- } else {
- upvalueidx := context.Upvalues.Find(upvalue.Name)
- if upvalueidx < 0 {
- upvalueidx = context.Upvalues.RegisterUnique(upvalue.Name)
- }
- code.AddABC(OP_GETUPVAL, 0, upvalueidx, 0, sline(ex))
- }
- }
- return sused
- default:
- panic(fmt.Sprintf("expr %v not implemented.", reflect.TypeOf(ex).Elem().Name()))
- }
- } // }}}
- func compileExprWithPropagation(context *funcContext, expr ast.Expr, reg *int, save *int, propergator func(int, *int, *int, int)) { // {{{
- reginc := compileExpr(context, *reg, expr, ecnone(0))
- if _, ok := expr.(*ast.LogicalOpExpr); ok {
- *save = *reg
- *reg = *reg + reginc
- } else {
- propergator(context.RegTop(), save, reg, reginc)
- }
- } // }}}
- func compileExprWithKMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{
- compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateKMV)
- } // }}}
- func compileExprWithMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{
- compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateMV)
- } // }}}
- func constFold(exp ast.Expr) ast.Expr { // {{{
- switch expr := exp.(type) {
- case *ast.ArithmeticOpExpr:
- lvalue, lisconst := lnumberValue(constFold(expr.Lhs))
- rvalue, risconst := lnumberValue(constFold(expr.Rhs))
- if lisconst && risconst {
- switch expr.Operator {
- case "+":
- return &constLValueExpr{Value: lvalue + rvalue}
- case "-":
- return &constLValueExpr{Value: lvalue - rvalue}
- case "*":
- return &constLValueExpr{Value: lvalue * rvalue}
- case "/":
- return &constLValueExpr{Value: lvalue / rvalue}
- case "%":
- return &constLValueExpr{Value: luaModulo(lvalue, rvalue)}
- case "^":
- return &constLValueExpr{Value: LNumber(math.Pow(float64(lvalue), float64(rvalue)))}
- default:
- panic(fmt.Sprintf("unknown binop: %v", expr.Operator))
- }
- } else {
- return expr
- }
- case *ast.UnaryMinusOpExpr:
- expr.Expr = constFold(expr.Expr)
- if value, ok := lnumberValue(expr.Expr); ok {
- return &constLValueExpr{Value: LNumber(-value)}
- }
- return expr
- default:
- return exp
- }
- } // }}}
- func compileFunctionExpr(context *funcContext, funcexpr *ast.FunctionExpr, ec *expcontext) { // {{{
- context.Proto.LineDefined = sline(funcexpr)
- context.Proto.LastLineDefined = eline(funcexpr)
- if len(funcexpr.ParList.Names) > maxRegisters {
- raiseCompileError(context, context.Proto.LineDefined, "register overflow")
- }
- context.Proto.NumParameters = uint8(len(funcexpr.ParList.Names))
- if ec.ctype == ecMethod {
- context.Proto.NumParameters += 1
- context.RegisterLocalVar("self")
- }
- for _, name := range funcexpr.ParList.Names {
- context.RegisterLocalVar(name)
- }
- if funcexpr.ParList.HasVargs {
- if CompatVarArg {
- context.Proto.IsVarArg = VarArgHasArg | VarArgNeedsArg
- if context.Parent != nil {
- context.RegisterLocalVar("arg")
- }
- }
- context.Proto.IsVarArg |= VarArgIsVarArg
- }
- compileChunk(context, funcexpr.Stmts, false)
- context.Code.AddABC(OP_RETURN, 0, 1, 0, eline(funcexpr))
- context.EndScope()
- context.CheckUnresolvedGoto()
- context.Proto.Code = context.Code.List()
- context.Proto.DbgSourcePositions = context.Code.PosList()
- context.Proto.DbgUpvalues = context.Upvalues.Names()
- context.Proto.NumUpvalues = uint8(len(context.Proto.DbgUpvalues))
- for _, clv := range context.Proto.Constants {
- sv := ""
- if slv, ok := clv.(LString); ok {
- sv = string(slv)
- }
- context.Proto.stringConstants = append(context.Proto.stringConstants, sv)
- }
- patchCode(context)
- } // }}}
- func compileTableExpr(context *funcContext, reg int, ex *ast.TableExpr, ec *expcontext) { // {{{
- code := context.Code
- /*
- tablereg := savereg(ec, reg)
- if tablereg == reg {
- reg += 1
- }
- */
- tablereg := reg
- reg++
- code.AddABC(OP_NEWTABLE, tablereg, 0, 0, sline(ex))
- tablepc := code.LastPC()
- regbase := reg
- arraycount := 0
- lastvararg := false
- for i, field := range ex.Fields {
- islast := i == len(ex.Fields)-1
- if field.Key == nil {
- if islast && isVarArgReturnExpr(field.Value) {
- reg += compileExpr(context, reg, field.Value, ecnone(-2))
- lastvararg = true
- } else {
- reg += compileExpr(context, reg, field.Value, ecnone(0))
- arraycount += 1
- }
- } else {
- regorg := reg
- b := reg
- compileExprWithKMVPropagation(context, field.Key, ®, &b)
- c := reg
- compileExprWithKMVPropagation(context, field.Value, ®, &c)
- opcode := OP_SETTABLE
- if _, ok := field.Key.(*ast.StringExpr); ok {
- opcode = OP_SETTABLEKS
- }
- code.AddABC(opcode, tablereg, b, c, sline(ex))
- reg = regorg
- }
- flush := arraycount % FieldsPerFlush
- if (arraycount != 0 && (flush == 0 || islast)) || lastvararg {
- reg = regbase
- num := flush
- if num == 0 {
- num = FieldsPerFlush
- }
- c := (arraycount-1)/FieldsPerFlush + 1
- b := num
- if islast && isVarArgReturnExpr(field.Value) {
- b = 0
- }
- line := field.Value
- if field.Key != nil {
- line = field.Key
- }
- if c > 511 {
- c = 0
- }
- code.AddABC(OP_SETLIST, tablereg, b, c, sline(line))
- if c == 0 {
- code.Add(uint32(c), sline(line))
- }
- }
- }
- code.SetB(tablepc, int2Fb(arraycount))
- code.SetC(tablepc, int2Fb(len(ex.Fields)-arraycount))
- if shouldmove(ec, tablereg) {
- code.AddABC(OP_MOVE, ec.reg, tablereg, 0, sline(ex))
- }
- } // }}}
- func compileArithmeticOpExpr(context *funcContext, reg int, expr *ast.ArithmeticOpExpr, ec *expcontext) { // {{{
- exp := constFold(expr)
- if ex, ok := exp.(*constLValueExpr); ok {
- exp.SetLine(sline(expr))
- compileExpr(context, reg, ex, ec)
- return
- }
- expr, _ = exp.(*ast.ArithmeticOpExpr)
- a := savereg(ec, reg)
- b := reg
- compileExprWithKMVPropagation(context, expr.Lhs, ®, &b)
- c := reg
- compileExprWithKMVPropagation(context, expr.Rhs, ®, &c)
- op := 0
- switch expr.Operator {
- case "+":
- op = OP_ADD
- case "-":
- op = OP_SUB
- case "*":
- op = OP_MUL
- case "/":
- op = OP_DIV
- case "%":
- op = OP_MOD
- case "^":
- op = OP_POW
- }
- context.Code.AddABC(op, a, b, c, sline(expr))
- } // }}}
- func compileStringConcatOpExpr(context *funcContext, reg int, expr *ast.StringConcatOpExpr, ec *expcontext) { // {{{
- code := context.Code
- crange := 1
- for current := expr.Rhs; current != nil; {
- if ex, ok := current.(*ast.StringConcatOpExpr); ok {
- crange += 1
- current = ex.Rhs
- } else {
- current = nil
- }
- }
- a := savereg(ec, reg)
- basereg := reg
- reg += compileExpr(context, reg, expr.Lhs, ecnone(0))
- reg += compileExpr(context, reg, expr.Rhs, ecnone(0))
- for pc := code.LastPC(); pc != 0 && opGetOpCode(code.At(pc)) == OP_CONCAT; pc-- {
- code.Pop()
- }
- code.AddABC(OP_CONCAT, a, basereg, basereg+crange, sline(expr))
- } // }}}
- func compileUnaryOpExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) { // {{{
- opcode := 0
- code := context.Code
- var operandexpr ast.Expr
- switch ex := expr.(type) {
- case *ast.UnaryMinusOpExpr:
- exp := constFold(ex)
- if lvexpr, ok := exp.(*constLValueExpr); ok {
- exp.SetLine(sline(expr))
- compileExpr(context, reg, lvexpr, ec)
- return
- }
- ex, _ = exp.(*ast.UnaryMinusOpExpr)
- operandexpr = ex.Expr
- opcode = OP_UNM
- case *ast.UnaryNotOpExpr:
- switch ex.Expr.(type) {
- case *ast.TrueExpr:
- code.AddABC(OP_LOADBOOL, savereg(ec, reg), 0, 0, sline(expr))
- return
- case *ast.FalseExpr, *ast.NilExpr:
- code.AddABC(OP_LOADBOOL, savereg(ec, reg), 1, 0, sline(expr))
- return
- default:
- opcode = OP_NOT
- operandexpr = ex.Expr
- }
- case *ast.UnaryLenOpExpr:
- opcode = OP_LEN
- operandexpr = ex.Expr
- }
- a := savereg(ec, reg)
- b := reg
- compileExprWithMVPropagation(context, operandexpr, ®, &b)
- code.AddABC(opcode, a, b, 0, sline(expr))
- } // }}}
- func compileRelationalOpExprAux(context *funcContext, reg int, expr *ast.RelationalOpExpr, flip int, label int) { // {{{
- code := context.Code
- b := reg
- compileExprWithKMVPropagation(context, expr.Lhs, ®, &b)
- c := reg
- compileExprWithKMVPropagation(context, expr.Rhs, ®, &c)
- switch expr.Operator {
- case "<":
- code.AddABC(OP_LT, 0^flip, b, c, sline(expr))
- case ">":
- code.AddABC(OP_LT, 0^flip, c, b, sline(expr))
- case "<=":
- code.AddABC(OP_LE, 0^flip, b, c, sline(expr))
- case ">=":
- code.AddABC(OP_LE, 0^flip, c, b, sline(expr))
- case "==":
- code.AddABC(OP_EQ, 0^flip, b, c, sline(expr))
- case "~=":
- code.AddABC(OP_EQ, 1^flip, b, c, sline(expr))
- }
- code.AddASbx(OP_JMP, 0, label, sline(expr))
- } // }}}
- func compileRelationalOpExpr(context *funcContext, reg int, expr *ast.RelationalOpExpr, ec *expcontext) { // {{{
- a := savereg(ec, reg)
- code := context.Code
- jumplabel := context.NewLabel()
- compileRelationalOpExprAux(context, reg, expr, 1, jumplabel)
- code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr))
- context.SetLabelPc(jumplabel, code.LastPC())
- code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr))
- } // }}}
- func compileLogicalOpExpr(context *funcContext, reg int, expr *ast.LogicalOpExpr, ec *expcontext) { // {{{
- a := savereg(ec, reg)
- code := context.Code
- endlabel := context.NewLabel()
- lb := &lblabels{context.NewLabel(), context.NewLabel(), endlabel, false}
- nextcondlabel := context.NewLabel()
- if expr.Operator == "and" {
- compileLogicalOpExprAux(context, reg, expr.Lhs, ec, nextcondlabel, endlabel, false, lb)
- context.SetLabelPc(nextcondlabel, code.LastPC())
- compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb)
- } else {
- compileLogicalOpExprAux(context, reg, expr.Lhs, ec, endlabel, nextcondlabel, true, lb)
- context.SetLabelPc(nextcondlabel, code.LastPC())
- compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb)
- }
- if lb.b {
- context.SetLabelPc(lb.f, code.LastPC())
- code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr))
- context.SetLabelPc(lb.t, code.LastPC())
- code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr))
- }
- lastinst := code.Last()
- if opGetOpCode(lastinst) == OP_JMP && opGetArgSbx(lastinst) == endlabel {
- code.Pop()
- }
- context.SetLabelPc(endlabel, code.LastPC())
- } // }}}
- func compileLogicalOpExprAux(context *funcContext, reg int, expr ast.Expr, ec *expcontext, thenlabel, elselabel int, hasnextcond bool, lb *lblabels) { // {{{
- // TODO folding constants?
- code := context.Code
- flip := 0
- jumplabel := elselabel
- if hasnextcond {
- flip = 1
- jumplabel = thenlabel
- }
- switch ex := expr.(type) {
- case *ast.FalseExpr:
- if elselabel == lb.e {
- code.AddASbx(OP_JMP, 0, lb.f, sline(expr))
- lb.b = true
- } else {
- code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
- }
- return
- case *ast.NilExpr:
- if elselabel == lb.e {
- compileExpr(context, reg, expr, ec)
- code.AddASbx(OP_JMP, 0, lb.e, sline(expr))
- } else {
- code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
- }
- return
- case *ast.TrueExpr:
- if thenlabel == lb.e {
- code.AddASbx(OP_JMP, 0, lb.t, sline(expr))
- lb.b = true
- } else {
- code.AddASbx(OP_JMP, 0, thenlabel, sline(expr))
- }
- return
- case *ast.NumberExpr, *ast.StringExpr:
- if thenlabel == lb.e {
- compileExpr(context, reg, expr, ec)
- code.AddASbx(OP_JMP, 0, lb.e, sline(expr))
- } else {
- code.AddASbx(OP_JMP, 0, thenlabel, sline(expr))
- }
- return
- case *ast.LogicalOpExpr:
- switch ex.Operator {
- case "and":
- nextcondlabel := context.NewLabel()
- compileLogicalOpExprAux(context, reg, ex.Lhs, ec, nextcondlabel, elselabel, false, lb)
- context.SetLabelPc(nextcondlabel, context.Code.LastPC())
- compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb)
- case "or":
- nextcondlabel := context.NewLabel()
- compileLogicalOpExprAux(context, reg, ex.Lhs, ec, thenlabel, nextcondlabel, true, lb)
- context.SetLabelPc(nextcondlabel, context.Code.LastPC())
- compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb)
- }
- return
- case *ast.RelationalOpExpr:
- if thenlabel == elselabel {
- flip ^= 1
- jumplabel = lb.t
- lb.b = true
- } else if thenlabel == lb.e {
- jumplabel = lb.t
- lb.b = true
- } else if elselabel == lb.e {
- jumplabel = lb.f
- lb.b = true
- }
- compileRelationalOpExprAux(context, reg, ex, flip, jumplabel)
- return
- }
- a := reg
- sreg := savereg(ec, a)
- isLastAnd := elselabel == lb.e && thenlabel != elselabel
- isLastOr := thenlabel == lb.e && hasnextcond
- if ident, ok := expr.(*ast.IdentExpr); ok && (isLastAnd || isLastOr) && getIdentRefType(context, context, ident) == ecLocal {
- b := context.FindLocalVar(ident.Value)
- op := OP_TESTSET
- if sreg == b {
- op = OP_TEST
- }
- code.AddABC(op, sreg, b, 0^flip, sline(expr))
- } else if !hasnextcond && thenlabel == elselabel {
- reg += compileExpr(context, reg, expr, &expcontext{ec.ctype, intMax(a, sreg), ec.varargopt})
- last := context.Code.Last()
- if opGetOpCode(last) == OP_MOVE && opGetArgA(last) == a {
- context.Code.SetA(context.Code.LastPC(), sreg)
- } else {
- context.Code.AddABC(OP_MOVE, sreg, a, 0, sline(expr))
- }
- } else {
- reg += compileExpr(context, reg, expr, ecnone(0))
- if !hasnextcond {
- code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr))
- } else {
- code.AddABC(OP_TESTSET, sreg, a, 0^flip, sline(expr))
- }
- }
- code.AddASbx(OP_JMP, 0, jumplabel, sline(expr))
- } // }}}
- func compileFuncCallExpr(context *funcContext, reg int, expr *ast.FuncCallExpr, ec *expcontext) int { // {{{
- funcreg := reg
- if ec.ctype == ecLocal && ec.reg == (int(context.Proto.NumParameters)-1) {
- funcreg = ec.reg
- reg = ec.reg
- }
- argc := len(expr.Args)
- islastvararg := false
- name := "(anonymous)"
- if expr.Func != nil { // hoge.func()
- reg += compileExpr(context, reg, expr.Func, ecnone(0))
- name = getExprName(context, expr.Func)
- } else { // hoge:method()
- b := reg
- compileExprWithMVPropagation(context, expr.Receiver, ®, &b)
- c := loadRk(context, ®, expr, LString(expr.Method))
- context.Code.AddABC(OP_SELF, funcreg, b, c, sline(expr))
- // increments a register for an implicit "self"
- reg = b + 1
- reg2 := funcreg + 2
- if reg2 > reg {
- reg = reg2
- }
- argc += 1
- name = string(expr.Method)
- }
- for i, ar := range expr.Args {
- islastvararg = (i == len(expr.Args)-1) && isVarArgReturnExpr(ar)
- if islastvararg {
- compileExpr(context, reg, ar, ecnone(-2))
- } else {
- reg += compileExpr(context, reg, ar, ecnone(0))
- }
- }
- b := argc + 1
- if islastvararg {
- b = 0
- }
- context.Code.AddABC(OP_CALL, funcreg, b, ec.varargopt+2, sline(expr))
- context.Proto.DbgCalls = append(context.Proto.DbgCalls, DbgCall{Pc: context.Code.LastPC(), Name: name})
- if ec.varargopt == 0 && shouldmove(ec, funcreg) {
- context.Code.AddABC(OP_MOVE, ec.reg, funcreg, 0, sline(expr))
- return 1
- }
- if context.RegTop() > (funcreg+2+ec.varargopt) || ec.varargopt < -1 {
- return 0
- }
- return ec.varargopt + 1
- } // }}}
- func loadRk(context *funcContext, reg *int, expr ast.Expr, cnst LValue) int { // {{{
- cindex := context.ConstIndex(cnst)
- if cindex <= opMaxIndexRk {
- return opRkAsk(cindex)
- } else {
- ret := *reg
- *reg++
- context.Code.AddABx(OP_LOADK, ret, cindex, sline(expr))
- return ret
- }
- } // }}}
- func getIdentRefType(context *funcContext, current *funcContext, expr *ast.IdentExpr) expContextType { // {{{
- if current == nil {
- return ecGlobal
- } else if current.FindLocalVar(expr.Value) > -1 {
- if current == context {
- return ecLocal
- }
- return ecUpvalue
- }
- return getIdentRefType(context, current.Parent, expr)
- } // }}}
- func getExprName(context *funcContext, expr ast.Expr) string { // {{{
- switch ex := expr.(type) {
- case *ast.IdentExpr:
- return ex.Value
- case *ast.AttrGetExpr:
- switch kex := ex.Key.(type) {
- case *ast.StringExpr:
- return kex.Value
- }
- return "?"
- }
- return "?"
- } // }}}
- func patchCode(context *funcContext) { // {{{
- maxreg := 1
- if np := int(context.Proto.NumParameters); np > 1 {
- maxreg = np
- }
- moven := 0
- code := context.Code.List()
- for pc := 0; pc < len(code); pc++ {
- inst := code[pc]
- curop := opGetOpCode(inst)
- switch curop {
- case OP_CLOSURE:
- pc += int(context.Proto.FunctionPrototypes[opGetArgBx(inst)].NumUpvalues)
- moven = 0
- continue
- case OP_SETGLOBAL, OP_SETUPVAL, OP_EQ, OP_LT, OP_LE, OP_TEST,
- OP_TAILCALL, OP_RETURN, OP_FORPREP, OP_FORLOOP, OP_TFORLOOP,
- OP_SETLIST, OP_CLOSE:
- /* nothing to do */
- case OP_CALL:
- if reg := opGetArgA(inst) + opGetArgC(inst) - 2; reg > maxreg {
- maxreg = reg
- }
- case OP_VARARG:
- if reg := opGetArgA(inst) + opGetArgB(inst) - 1; reg > maxreg {
- maxreg = reg
- }
- case OP_SELF:
- if reg := opGetArgA(inst) + 1; reg > maxreg {
- maxreg = reg
- }
- case OP_LOADNIL:
- if reg := opGetArgB(inst); reg > maxreg {
- maxreg = reg
- }
- case OP_JMP: // jump to jump optimization
- distance := 0
- count := 0 // avoiding infinite loops
- for jmp := inst; opGetOpCode(jmp) == OP_JMP && count < 5; jmp = context.Code.At(pc + distance + 1) {
- d := context.GetLabelPc(opGetArgSbx(jmp)) - pc
- if d > opMaxArgSbx {
- if distance == 0 {
- raiseCompileError(context, context.Proto.LineDefined, "too long to jump.")
- }
- break
- }
- distance = d
- count++
- }
- if distance == 0 {
- context.Code.SetOpCode(pc, OP_NOP)
- } else {
- context.Code.SetSbx(pc, distance)
- }
- default:
- if reg := opGetArgA(inst); reg > maxreg {
- maxreg = reg
- }
- }
- // bulk move optimization(reducing op dipatch costs)
- if curop == OP_MOVE {
- moven++
- } else {
- if moven > 1 {
- context.Code.SetOpCode(pc-moven, OP_MOVEN)
- context.Code.SetC(pc-moven, intMin(moven-1, opMaxArgsC))
- }
- moven = 0
- }
- }
- maxreg++
- if maxreg > maxRegisters {
- raiseCompileError(context, context.Proto.LineDefined, "register overflow(too many local variables)")
- }
- context.Proto.NumUsedRegisters = uint8(maxreg)
- } // }}}
- func Compile(chunk []ast.Stmt, name string) (proto *FunctionProto, err error) { // {{{
- defer func() {
- if rcv := recover(); rcv != nil {
- if _, ok := rcv.(*CompileError); ok {
- err = rcv.(error)
- } else {
- panic(rcv)
- }
- }
- }()
- err = nil
- parlist := &ast.ParList{HasVargs: true, Names: []string{}}
- funcexpr := &ast.FunctionExpr{ParList: parlist, Stmts: chunk}
- if len(chunk) > 0 {
- funcexpr.SetLastLine(sline(chunk[0]))
- funcexpr.SetLastLine(eline(chunk[len(chunk)-1]) + 1)
- }
- context := newFuncContext(name, nil)
- compileFunctionExpr(context, funcexpr, ecnone(0))
- proto = context.Proto
- return
- } // }}}
|