- 自己動手實現(xiàn)Lua:虛擬機、編譯器和標準庫
- 張秀宏
- 836字
- 2019-01-03 15:00:09
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
這樣就算完成了,看起來還挺像模像樣的。
- Mastering ServiceStack
- 高效微控制器C語言編程
- Xcode 7 Essentials(Second Edition)
- Scala Design Patterns
- 算法基礎:打開程序設計之門
- Swift語言實戰(zhàn)精講
- 軟件工程基礎與實訓教程
- 深入理解BootLoader
- Qt 4開發(fā)實踐
- Arduino電子設計實戰(zhàn)指南:零基礎篇
- 分布式數(shù)據(jù)庫HBase案例教程
- C/C++程序設計教程
- Access 2016數(shù)據(jù)庫應用與開發(fā):實戰(zhàn)從入門到精通(視頻教學版)
- 編譯原理學習與實踐指導
- Netty 4核心原理與手寫RPC框架實戰(zhàn)