Skip to content

Commit

Permalink
Implement a shared library loader with go1.8(experimental)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuin committed Dec 16, 2016
1 parent 7692488 commit 571b031
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 3 deletions.
6 changes: 6 additions & 0 deletions config.go
Expand Up @@ -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"
}
}
90 changes: 87 additions & 3 deletions loadlib.go
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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 {
Expand Down

2 comments on commit 571b031

@FZambia
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@yuin
Copy link
Owner Author

@yuin yuin commented on 571b031 Dec 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.