官术网_书友最值得收藏!

2.5 測試本章代碼

前面我們詳細討論了二進制chunk格式,定義了函數(shù)原型相關的結構體,并且完成了解析代碼。本節(jié)我們將通過實現(xiàn)一個簡化版的Lua反編譯器來進一步加深讀者對二進制chunk格式的認識。請讀者打開$LUAGO/go/ch02/src/luago/main.go文件(這個文件是從第1章復制過來的),把里面的代碼改成如下代碼。

        package main
        import "fmt"
        import "io/ioutil"
        import "os"
        import "luago/binchunk"

        func main() {
            if len(os.Args) > 1 {
                data, err := ioutil.ReadFile(os.Args[1])
                if err ! = nil { panic(err) }
                proto := binchunk.Undump(data)
                list(proto)
            }
        }

我們通過命令行參數(shù)把需要反編譯的二進制chunk文件傳遞給main()函數(shù),然后用main()函數(shù)讀取該文件數(shù)據(jù),調用Undump()函數(shù)把它解析為函數(shù)原型,最后通過list()函數(shù)把函數(shù)原型的信息打印到控制臺。下面是list()函數(shù)的代碼。

        func list(f *binchunk.Prototype) {
            printHeader(f)
            printCode(f)
            printDetail(f)
            for _, p := range f.Protos {
                list(p)
            }
        }

list()函數(shù)先打印出函數(shù)的基本信息,然后打印指令表和其他詳細信息,最后遞歸調用自己把子函數(shù)信息打印出來。printHeader()函數(shù)的代碼如下所示。

        func printHeader(f *binchunk.Prototype) {
            funcType := "main"
            if f.LineDefined > 0 { funcType = "function" }

            varargFlag := ""
            if f.IsVararg > 0 { varargFlag = "+" }

            fmt.Printf("\n%s <%s:%d, %d> (%d instructions)\n", funcType,
                f.Source, f.LineDefined, f.LastLineDefined, len(f.Code))

            fmt.Printf("%d%s params, %d slots, %d upvalues, ",
                f.NumParams, varargFlag, f.MaxStackSize, len(f.Upvalues))

            fmt.Printf("%d locals, %d constants, %d functions\n",
                len(f.LocVars), len(f.Constants), len(f.Protos))
        }

我們在第3章會詳細討論Lua虛擬機指令格式,這里printCode()函數(shù)只打印出指令的序號、行號和十六進制表示,代碼如下所示。

        func printCode(f *binchunk.Prototype) {
            for pc, c := range f.Code {
                line := "-"
                if len(f.LineInfo) > 0 {
                    line = fmt.Sprintf("%d", f.LineInfo[pc])
                }
                fmt.Printf("\t%d\t[%s]\t0x%08X\n", pc+1, line, c)
            }
        }

printDetail()函數(shù)打印常量表、局部變量表和Upvalue表,代碼如下所示。

        func printDetail(f *binchunk.Prototype) {
            fmt.Printf("constants (%d):\n", len(f.Constants))
            for i, k := range f.Constants {
                fmt.Printf("\t%d\t%s\n", i+1, constantToString(k))
            }

            fmt.Printf("locals (%d):\n", len(f.LocVars))
            for i, locVar := range f.LocVars {
                fmt.Printf("\t%d\t%s\t%d\t%d\n",
                    i, locVar.VarName, locVar.StartPC+1, locVar.EndPC+1)
            }

            fmt.Printf("upvalues (%d):\n", len(f.Upvalues))
            for i, upval := range f.Upvalues {
                fmt.Printf("\t%d\t%s\t%d\t%d\n",
                    i, upvalName(f, i), upval.Instack, upval.Idx)
            }
        }

constantToString()函數(shù)把常量表里的常量轉換成字符串,代碼如下所示。

        func constantToString(k interface{}) string {
            switch k.(type) {
            case nil:      return "nil"
            case bool:     return fmt.Sprintf("%t", k)
            case float64:  return fmt.Sprintf("%g", k)
            case int64:   return fmt.Sprintf("%d", k)
            case string:  return fmt.Sprintf("%q", k)
            default:       return "? "
            }
        }

upvalName()函數(shù)根據(jù)Upvalue索引從調試信息里找出Upvalue的名字,代碼如下所示。

        func upvalName(f *binchunk.Prototype, idx int) string {
            if len(f.UpvalueNames) > 0 {
                return f.UpvalueNames[idx]
            }
            return "-"
        }

現(xiàn)在一切準備就緒,請讀者在命令行里執(zhí)行如下命令,編譯本章測試代碼。

        $ cd $LUAGO/go/
        $ export GOPATH=$PWD/ch02
        $ go install luago

如果沒有任何輸出,那就表示編譯成功了,在ch02/bin目錄下會出現(xiàn)可執(zhí)行文件luago。再編譯一下“Hello, World! ”程序,把輸出文件當作參數(shù)傳遞給luago,反編譯輸出如下。

        $ luac ../lua/ch02/hello_world.lua
        $ ./ch02/bin/luago luac.out

        main <@../lua/ch02/hello_world.lua:0,0> (4 instructions)
        0+ params, 2 slots, 1 upvalues, 0 locals, 2 constants, 0 functions
            1       [1]     0x00400006
            2       [1]     0x00004041
            3       [1]     0x01004024
            4       [1]     0x00800026
        constants (2):
            1       "print"
            2       "Hello, World! "
        locals (0):
        upvalues (1):
            0       _ENV    1        0

這樣就算完成了,看起來還挺像模像樣的。

主站蜘蛛池模板: 正阳县| 定日县| 玛沁县| 莱阳市| 青海省| 霍邱县| 阳曲县| 华容县| 兴和县| 文山县| 枣阳市| 安西县| 从化市| 洮南市| 德安县| 甘德县| 玉门市| 阿荣旗| 乾安县| 荣昌县| 松江区| 祁门县| 平谷区| 元江| 莫力| 金华市| 台前县| 邹平县| 茌平县| 从化市| 永嘉县| 浏阳市| 水富县| 钦州市| 湖北省| SHOW| 南澳县| 元朗区| 简阳市| 临安市| 剑川县|