Permalink
Browse files

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

  • Loading branch information...
yuin committed Dec 16, 2016
1 parent 7692488 commit 571b031ebefafde258037fa84fc1ff6c6f12227b
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

This comment has been minimized.

Show comment
Hide comment
@FZambia

FZambia Dec 16, 2016

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

FZambia replied Dec 16, 2016

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

@yuin

This comment has been minimized.

Show comment
Hide comment
@yuin

yuin Dec 16, 2016

Owner

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
 }
Owner

yuin replied Dec 16, 2016

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.