runningwater 1 년 전
커밋
c300d4032b
11개의 변경된 파일424개의 추가작업 그리고 0개의 파일을 삭제
  1. 0 0
      .gitignore
  2. 8 0
      .idea/.gitignore
  3. 9 0
      .idea/luago.iml
  4. 8 0
      .idea/modules.xml
  5. 93 0
      binchunk/binary_chunk.go
  6. 189 0
      binchunk/reader.go
  7. 3 0
      go.mod
  8. 7 0
      lua/foo_bar.lua
  9. 1 0
      lua/hello_world.lua
  10. BIN
      lua/luac.out
  11. 106 0
      main.go

+ 0 - 0
.gitignore


+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 9 - 0
.idea/luago.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/luago.iml" filepath="$PROJECT_DIR$/.idea/luago.iml" />
+    </modules>
+  </component>
+</project>

+ 93 - 0
binchunk/binary_chunk.go

@@ -0,0 +1,93 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2024/4/5 15:52
+// Desc:
+// (base) ➜  lua xxd -u -g 1 luac.out
+// 00000000: 1B 4C 75 61 53 00 19 93 0D 0A 1A 0A 04 08 04 08  .LuaS...........
+// 00000010: 08 78 56 00 00 00 00 00 00 00 00 00 00 00 28 77  .xV...........(w
+// 00000020: 40 01 11 40 68 65 6C 6C 6F 5F 77 6F 72 6C 64 2E  @..@hello_world.
+// 00000030: 6C 75 61 00 00 00 00 00 00 00 00 00 01 02 04 00  lua.............
+// 00000040: 00 00 06 00 40 00 41 40 00 00 24 40 00 01 26 00  ....@.A@..$@..&.
+// 00000050: 80 00 02 00 00 00 04 06 70 72 69 6E 74 04 0D 48  ........print..H
+// 00000060: 65 6C 6C 6F 2C 20 57 6F 72 6C 64 01 00 00 00 01  ello, World.....
+// 00000070: 00 00 00 00 00 04 00 00 00 01 00 00 00 01 00 00  ................
+// 00000080: 00 01 00 00 00 01 00 00 00 00 00 00 00 01 00 00  ................
+// 00000090: 00 05 5F 45 4E 56                                .._ENV
+
+package binchunk
+
+const (
+    LUA_SIGNATURE    = "\x1bLua"
+    LUAC_VERSION     = 0x53
+    LUAC_FORMAT      = 0
+    LUAC_DATA        = "\x19\x93\r\n\x1a\n"
+    CINT_SIZE        = 4
+    CSZIET_SIZE      = 8
+    INSTRUCTION_SIZE = 4
+    LUA_INTEGER_SIZE = 8
+    LUA_NUMBER_SIZE  = 8
+    LUAC_INT         = 0X5678
+    LUAC_NUM         = 370.5
+
+    TAG_NIL       = 0X00
+    TAG_BOOLEAN   = 0X01
+    TAG_NUMBER    = 0X03
+    TAG_INTEGER   = 0X13
+    TAG_SHORT_STR = 0X04
+    TAG_LONG_STR  = 0X14
+)
+
+// Undump 解析二进制 chunk
+func Undump(data []byte) *Prototype {
+    reader := &reader{data}
+    reader.checkHeader()        // 校验头部
+    reader.readByte()           // 跳过 Upvalue 数量
+    return reader.readProto("") // 读取函数原型
+}
+
+type binaryChunk struct {
+    header                  // 头部
+    sizeUpvalues byte       // 主函数 upvalue 数量 01
+    mainFunc     *Prototype // 主函数原型
+}
+
+type header struct {
+    signature       [4]byte // 签名 1B 4C 75 61(ESC L u a 的 ASCII 码)
+    version         byte    // 版本号  大版本号 小版本号 发布版本号 Lua 5.3.6 5X16+4=83(0X53)
+    format          byte    // 格式号  0
+    luacData        [6]byte // LUAC_DATA 0x1993 回车(0X0D) 换行(0X0A) 替换(0X1A) 换行(0X0A)
+    cintSize        byte    // cint 在二进制 chunk 里占用的字节数 04
+    sizetSize       byte    // size_t 在二进制 chunk 里占用的字节数 08
+    instructionSize byte    // Lua 虚拟机指令在二进制 chunk 里占用的字节数 04
+    luaIntegerSize  byte    // Lua 整数在二进制 chunk 里占用的字节数 08
+    luaNumberSize   byte    // Lua 浮点数在二进制 chunk 里占用的字节数 08
+    luacInt         int64   // 78 56 00 00 00 00 00 00
+    luacNum         float64 // 00 00 00 00 00 28 77 40
+}
+
+type Prototype struct {
+    Source          string        // 源文件名
+    LineDefined     uint32        // 起止行号
+    LastLineDefined uint32        // 起止行号
+    NumParams       byte          // 固定参数个数
+    IsVararg        byte          // 是否有变长参数
+    MaxStackSize    byte          // 寄存器数量
+    Code            []uint32      // 指令表
+    Constants       []interface{} // 常量表
+    Upvalues        []Upvalue     // Upvalue 表,每个元素占用 2 个字节
+    Protos          []*Prototype  // 函数原型
+    LineInfo        []uint32
+    LocVars         []LocVar // 局部变量表
+    UpvalueNames    []string
+}
+
+type Upvalue struct {
+    Instack byte
+    Idx     byte
+}
+
+type LocVar struct {
+    VarName string
+    StartPC uint32
+    EndPC   uint32
+}

+ 189 - 0
binchunk/reader.go

@@ -0,0 +1,189 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2024/4/5 17:02
+// Desc:
+
+package binchunk
+
+import (
+    "encoding/binary"
+    "math"
+)
+
+type reader struct {
+    data []byte
+}
+
+func (r *reader) checkHeader() {
+    if string(r.readBytes(4)) != LUA_SIGNATURE {
+        panic("not a precompiled chunk!")
+    } else if r.readByte() != LUAC_VERSION {
+        panic("version mismatch!")
+    } else if r.readByte() != LUAC_FORMAT {
+        panic("format mismatch!")
+    } else if string(r.readBytes(6)) != LUAC_DATA {
+        panic("corrupted!")
+    } else if r.readByte() != CINT_SIZE {
+        panic("int size mismatch!")
+    } else if r.readByte() != CSZIET_SIZE {
+        panic("size_t size mismatch!")
+    } else if r.readByte() != INSTRUCTION_SIZE {
+        panic("instruction size mismatch!")
+    } else if r.readByte() != LUA_INTEGER_SIZE {
+        panic("lua_Integer size mismatch!")
+    } else if r.readByte() != LUA_NUMBER_SIZE {
+        panic("lua_number size mismatch!")
+    } else if r.readLuaInteger() != LUAC_INT {
+        panic("endianness mismatch!")
+    } else if r.readLuaNumber() != LUAC_NUM {
+        panic("float format mismatch")
+    }
+}
+
+func (r *reader) readByte() byte {
+    b := r.data[0]
+    r.data = r.data[1:]
+    return b
+}
+func (r *reader) readUint32() uint32 {
+    i := binary.LittleEndian.Uint32(r.data)
+    r.data = r.data[4:]
+    return i
+}
+func (r *reader) readUint64() uint64 {
+    i := binary.LittleEndian.Uint64(r.data)
+    r.data = r.data[8:]
+    return i
+}
+func (r *reader) readLuaInteger() int64 {
+    return int64(r.readUint64())
+}
+func (r *reader) readLuaNumber() float64 {
+    return math.Float64frombits(r.readUint64())
+}
+func (r *reader) readString() string {
+    size := uint(r.readByte())
+    if size == 0 {
+        // NUll 字符串
+        return ""
+    }
+    if size == 0XFF {
+        // 长字符串
+        size = uint(r.readUint64())
+    }
+    bytes := r.readBytes(size - 1)
+    return string(bytes)
+}
+func (r *reader) readBytes(n uint) []byte {
+    bytes := r.data[:n]
+    r.data = r.data[n:]
+    return bytes
+}
+
+func (r *reader) readProto(parentSource string) *Prototype {
+    source := r.readString()
+    if source == "" {
+        source = parentSource
+    }
+    return &Prototype{
+        Source:          source,
+        LineDefined:     r.readUint32(),
+        LastLineDefined: r.readUint32(),
+        NumParams:       r.readByte(),
+        IsVararg:        r.readByte(),
+        MaxStackSize:    r.readByte(),
+        Code:            r.readCode(),
+        Constants:       r.readConstants(),
+        Upvalues:        r.readUpvalues(),
+        Protos:          r.readProtos(source),
+        LineInfo:        r.readLineInfo(),
+        LocVars:         r.readLocVars(),
+        UpvalueNames:    r.readUpvalueNames(),
+    }
+}
+
+func (r *reader) readCode() []uint32 {
+    code := make([]uint32, r.readUint32())
+    for i := range code {
+        code[i] = r.readUint32()
+    }
+    return code
+}
+
+func (r *reader) readConstants() []interface{} {
+    constants := make([]interface{}, r.readUint32())
+    for i := range constants {
+        constants[i] = r.readConstant()
+    }
+    return constants
+}
+
+func (r *reader) readConstant() interface{} {
+    switch r.readByte() { // tag
+    case TAG_NIL:
+        return nil
+    case TAG_BOOLEAN:
+        return r.readByte() != 0
+    case TAG_INTEGER:
+        return r.readLuaInteger()
+    case TAG_NUMBER:
+        return r.readLuaNumber()
+    case TAG_SHORT_STR:
+        return r.readString()
+    case TAG_LONG_STR:
+        return r.readString()
+    default:
+        panic("corrupted!")
+    }
+}
+
+func (r *reader) readUpvalues() []Upvalue {
+    upvalues := make([]Upvalue, r.readUint32())
+    for i := range upvalues {
+        upvalues[i] = Upvalue{
+            Instack: r.readByte(),
+            Idx:     r.readByte(),
+        }
+    }
+
+    return upvalues
+}
+
+func (r *reader) readProtos(parentSource string) []*Prototype {
+    protos := make([]*Prototype, r.readUint32())
+    for i := range protos {
+        protos[i] = r.readProto(parentSource)
+    }
+    return protos
+}
+
+func (r *reader) readLineInfo() []uint32 {
+    lineInfo := make([]uint32, r.readUint32())
+    for i := range lineInfo {
+        lineInfo[i] = r.readUint32()
+    }
+
+    return lineInfo
+}
+
+func (r *reader) readLocVars() []LocVar {
+    locVars := make([]LocVar, r.readUint32())
+    for i := range locVars {
+        locVars[i] = LocVar{
+            VarName: r.readString(),
+            StartPC: r.readUint32(),
+            EndPC:   r.readUint32(),
+        }
+    }
+
+    return locVars
+}
+
+func (r *reader) readUpvalueNames() []string {
+    names := make([]string, r.readUint32())
+    for i := range names {
+        names[i] = r.readString()
+    }
+
+    return names
+}

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module luago
+
+go 1.21

+ 7 - 0
lua/foo_bar.lua

@@ -0,0 +1,7 @@
+function foo()
+    function bar() end
+end
+
+function bar()
+    print("Hello, World")
+end

+ 1 - 0
lua/hello_world.lua

@@ -0,0 +1 @@
+print("Hello, World")

BIN
lua/luac.out


+ 106 - 0
main.go

@@ -0,0 +1,106 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2024/4/13 17:14
+// Desc:
+
+package main
+
+import (
+    "fmt"
+    "luago/binchunk"
+    "os"
+)
+
+func main() {
+    if len(os.Args) > 1 {
+        data, err := os.ReadFile(os.Args[1])
+        if err != nil {
+            panic(err)
+        }
+        proto := binchunk.Undump(data)
+        list(proto)
+
+    }
+}
+
+func list(f *binchunk.Prototype) {
+    printHeader(f)
+    printCode(f)
+    printDetail(f)
+    for _, p := range f.Protos {
+        list(p)
+    }
+}
+
+// 打印常量表、局部变量表和 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)
+    }
+
+}
+
+// 根据 Upvalue 索引从调试信息里找出 Upvalue 的名字
+func upvalName(f *binchunk.Prototype, idx int) string {
+    if len(f.UpvalueNames) > 0 {
+        return f.UpvalueNames[idx]
+    }
+    return "-"
+}
+
+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 "?"
+    }
+}
+
+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)
+    }
+}
+
+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))
+}