| 当然,除此之外,也存在 Go 调用 Lua 模块,但个人感觉后者是没啥必要的,所以在这里并没有涉及后者的内容。 package main  import (      "fmt"      lua "github.com/yuin/gopher-lua"  )  const source = `  local m = require("gomodule")  m.goFunc()  print(m.name)  ` func main() {      L := lua.NewState()      defer L.Close()      L.PreloadModule("gomodule", load)      if err := L.DoString(source); err != nil {          panic(err)      }  }  func load(L *lua.LState) int {      mod := L.SetFuncs(L.NewTable(), exports)      L.SetField(mod, "name", lua.LString("gomodule"))      L.Push(mod)      return 1  }  var exports = map[string]lua.LGFunction{      "goFunc": goFunc,  }  func goFunc(L *lua.LState) int {      fmt.Println("golang")      return 0  }  // golang  // gomodule 
 变量污染 当我们使用实例池减少开销时,会引入另一个棘手的问题:由于同一个虚拟机可能会被多次执行同样的 Lua 代码,进而变动了其中的全局变量。如果代码逻辑依赖于全局变量,那么可能会出现难以预测的运行结果(这有点数据库隔离性中的“不可重复读”的味道)。 全局变量 如果我们需要限制 Lua 代码只能使用局部变量,那么站在这个出发点上,我们需要对全局变量做出限制。那问题来了,该如何实现呢? 我们知道,Lua 是编译成字节码,再被解释执行的。那么,我们可以在编译字节码的阶段中,对全局变量的使用作出限制。在查阅完 Lua 虚拟机指令后,发现涉及到全局变量的指令有两条:GETGLOBAL(Opcode 5)和 SETGLOBAL(Opcode 7)。 到这里,已经有了大致的思路:我们可通过判断字节码是否含有 GETGLOBAL 和 SETGLOBAL 进而限制代码的全局变量的使用。至于字节码的获取,可通过调用 CompileString(...) 和 CompileFile(...) ,得到 Lua 代码的 FunctionProto ,而其中的 Code 属性即为字节码 slice,,类型为 []uint32 。 在虚拟机实现代码中,我们可以找到一个根据字节码输出对应 OpCode 的工具函数。 // 获取对应指令的 OpCode  func opGetOpCode(inst uint32) int {      return int(inst >> 26)  } 
 有了这个工具函数,我们即可实现对全局变量的检查。 package main  // ...  func CheckGlobal(proto *lua.FunctionProto) error {      for _, code := range proto.Code {          switch opGetOpCode(code) {          case lua.OP_GETGLOBAL:              return errors.New("not allow to access global")          case lua.OP_SETGLOBAL:              return errors.New("not allow to set global")          }      }      // 对嵌套函数进行全局变量的检查      for _, nestedProto := range proto.FunctionPrototypes {          if err := CheckGlobal(nestedProto); err != nil {              return err          }      }      return nil  }  func TestCheckGetGlobal(t *testing.T) {      l := lua.NewState()      proto, _ := CompileString(`print(_G)`)      if err := CheckGlobal(proto); err == nil {          t.Fail()      }      l.Close()  }  func TestCheckSetGlobal(t *testing.T) {      l := lua.NewState()      proto, _ := CompileString(`_G = {}`)      if err := CheckGlobal(proto); err == nil {          t.Fail()      }      l.Close()  } 
 模块 除变量可能被污染外,导入的 Go 模块也有可能在运行期间被篡改。因此,我们需要一种机制,确保导入到虚拟机的模块不被篡改,即导入的对象是只读的。 (编辑:南平站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |