Permalink
Browse files

Implement a shared library loader with go1.8(experimental)

  • Loading branch information...
1 parent 7692488 commit 571b031ebefafde258037fa84fc1ff6c6f12227b @yuin committed Dec 16, 2016
Showing with 93 additions and 3 deletions.
  1. +6 −0 config.go
  2. +87 −3 loadlib.go
View
@@ -20,15 +20,21 @@ var LuaPath = "LUA_PATH"
var LuaLDir string
var LuaPathDefault string
var LuaOS string
+var LuaCDir string
+var LuaCPathDefault string
func init() {
if os.PathSeparator == '/' { // unix-like
LuaOS = "unix"
LuaLDir = "/usr/local/share/lua/5.1"
LuaPathDefault = "./?.lua;" + LuaLDir + "/?.lua;" + LuaLDir + "/?/init.lua"
+ LuaCDir = "/usr/local/lib/glua/5.1"
+ LuaCPathDefault = "./?.so;" + LuaCDir + "/?.so;" + LuaCDir + "/loadall.so;"
} else { // windows
LuaOS = "windows"
LuaLDir = "!\\lua"
LuaPathDefault = ".\\?.lua;" + LuaLDir + "\\?.lua;" + LuaLDir + "\\?\\init.lua"
+ LuaCDir = "!\\"
+ LuaPathDefault = ".\\?.dll;" + LuaCDir + "\\?.dll;" + LuaCDir + "\\?\\loadall.dll"
}
}
View
@@ -4,12 +4,16 @@ import (
"fmt"
"os"
"path/filepath"
+ "plugin"
+ "regexp"
"strings"
)
/* load lib {{{ */
-var loLoaders = []LGFunction{loLoaderPreload, loLoaderLua}
+const loRegPrefix = "LOADLIB: "
+
+var loLoaders = []LGFunction{loLoaderPreload, loLoaderLua, loLoaderC}
func loGetPath(env string, defpath string) string {
path := os.Getenv(env)
@@ -46,6 +50,58 @@ func loFindFile(L *LState, name, pname string) (string, string) {
return "", strings.Join(messages, "\n\t")
}
+func loRegister(L *LState, path string) *LUserData {
+ rname := loRegPrefix + path
+ reg := L.Get(RegistryIndex).(*LTable)
+ r := reg.RawGetString(rname)
+ if r != LNil {
+ return r.(*LUserData)
+ }
+ plib := L.NewUserData()
+ plib.Value = nil
+ L.SetMetatable(plib, L.GetMetatable(reg))
+ reg.RawSetString(rname, plib)
+ return plib
+}
+
+func loSym(L *LState, ud *LUserData, sym string) (*LFunction, error) {
+ p := ud.Value.(*plugin.Plugin)
+ f, err := p.Lookup(sym)
+ if err != nil {
+ return nil, err
+ }
+ return L.NewFunction(f.(func(*LState) int)), nil
+}
+
+func loLoadFunc(L *LState, path, sym string) (*LFunction, error) {
+ ud := loRegister(L, path)
+ if ud.Value == nil {
+ p, err := plugin.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ ud.Value = p
+ }
+
+ f, err := loSym(L, ud, sym)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+func loMakeFuncName(s string) string {
+ re := regexp.MustCompile("^[^\\-]*\\-")
+ name := re.ReplaceAllString(s, "")
+
+ re = regexp.MustCompile("\\.(\\w)")
+
+ funcname := re.ReplaceAllStringFunc(name, func(s string) string {
+ return strings.ToUpper(re.FindStringSubmatch(s)[1])
+ })
+ return "LuaOpen" + strings.Title(funcname)
+}
+
func OpenPackage(L *LState) int {
packagemod := L.RegisterModule(LoadLibName, loFuncs)
@@ -104,9 +160,37 @@ func loLoaderLua(L *LState) int {
return 1
}
+func loLoaderC(L *LState) int {
+ name := L.CheckString(1)
+ path, msg := loFindFile(L, name, "cpath")
+ if len(path) == 0 {
+ L.Push(LString(msg))
+ return 1
+ }
+ f, err := loLoadFunc(L, path, loMakeFuncName(name))
+ if err != nil {
+ L.RaiseError(err.Error())
+ }
+ L.Push(f)
+ return 1
+}
+
func loLoadLib(L *LState) int {
- L.RaiseError("loadlib is not supported")
- return 0
+ path := L.CheckString(1)
+ init := L.CheckString(2)
+ f, err := loLoadFunc(L, path, init)
+ if err != nil {
+ L.Push(LNil)
+ L.Push(LString(err.Error()))
+ if strings.Contains(err.Error(), "plugin.Open") {
+ L.Push(LString("open"))
+ } else {
+ L.Push(LString("init"))
+ }
+ return 3
+ }
+ L.Push(f)
+ return 1
}
func loSeeAll(L *LState) int {

2 comments on commit 571b031

@FZambia

@yuin this is an interesting commit, but afaik using plugin means using CGO - so it will break cross compilation?

@yuin
Owner
yuin commented on 571b031 Dec 16, 2016 edited

Hi @FZambia .
Yes, plugins break the cross compilation and work only on Linux at least now. (MacOS was dropped in Go1.8 beta2)

I have tested this commit on my Linux machine and the following plugin worked fine.

 package main

 import (
     "C"
     "github.com/yuin/gopher-lua"
 )

 func Add(L *lua.LState) int {
     v1 := L.CheckInt(1)
     v2 := L.CheckInt(2)
     L.Push(lua.LNumber(v1 + v2))
     return 1
 }

 func LuaOpenPlugin(L *lua.LState) int {
     L.Push(
             L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
                     "add": Add,
             }))
     return 1
 }
Please sign in to comment.