mirror of https://github.com/Hilbis/Hilbish
refactor!: use userdata where appropriate (#157)
any interface which has lua side objects that are from go side for its api (namely jobs) they will use userdata instead of always creating a table to represent the object. this might or might not bring lower ram usage since there is now only 1 single reference to an object instead of always creating a table to expose on lua breaking change since methods need to be called with a colon instead of dotrunner-prompt
parent
e3c25586e4
commit
b28a2c24c1
|
@ -56,6 +56,9 @@ This is probably one of (if not the) biggest things in this release.
|
|||
user input, exit code, and error. User input has been added to the return to
|
||||
account for runners wanting to prompt for continued input, and to add it
|
||||
properly to history.
|
||||
- **Breaking Change:** Job objects and timers are now Lua userdata instead
|
||||
of a table, so their functions require you to call them with a colon instead
|
||||
of a dot. (ie. `job.stop()` -> `job:stop()`)
|
||||
- All `fs` module functions which take paths now implicitly expand ~ to home.
|
||||
|
||||
### Fixed
|
||||
|
|
4
api.go
4
api.go
|
@ -476,7 +476,7 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
timer := timers.create(timerTimeout, interval, cb)
|
||||
timer.start()
|
||||
|
||||
return c.PushingNext1(t.Runtime, timer.lua()), nil
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
||||
}
|
||||
|
||||
// interval(cb, time)
|
||||
|
@ -502,7 +502,7 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
timer := timers.create(timerInterval, interval, cb)
|
||||
timer.start()
|
||||
|
||||
return c.PushingNext1(t.Runtime, timer.lua()), nil
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
||||
}
|
||||
|
||||
// complete(scope, cb)
|
||||
|
|
|
@ -20,8 +20,11 @@ and `execPath` is an absolute path for the command executable.
|
|||
- `disown(id)`: Removes a job by ID from the job table.
|
||||
|
||||
# Job Object
|
||||
A job object on the Lua side is a table with some functions.
|
||||
On the under side it represents a job in the job table.
|
||||
A job object is a piece of `userdata`. All the functions of a job require
|
||||
you to call them with a colon, since they are *methods* for the job object.
|
||||
Example: hilbish.jobs.last():foreground()
|
||||
Which will foreground the last job.
|
||||
|
||||
You can still have a job object for a disowned job,
|
||||
it just won't be *working* anywhere. :^)
|
||||
|
||||
|
|
|
@ -16,9 +16,17 @@ a timer via ID.
|
|||
when the timer is triggered.
|
||||
|
||||
# Timer Object
|
||||
Those previously mentioned functions return a `timer` object, to which you can
|
||||
stop and start a timer again. The functions of the timers interface also
|
||||
return a timer object.
|
||||
All those previously mentioned functions return a `timer` object, to which
|
||||
you can stop and start a timer again.
|
||||
|
||||
An example of usage:
|
||||
local t = hilbish.timers.create(1, 5000, function()
|
||||
print 'hello!'
|
||||
end)
|
||||
|
||||
t:stop()
|
||||
print(t.running, t.duration, t.type)
|
||||
t:start()
|
||||
|
||||
## Properties
|
||||
- `duration`: amount of time the timer runs for in milliseconds
|
||||
|
|
144
job.go
144
job.go
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -15,6 +16,7 @@ import (
|
|||
)
|
||||
|
||||
var jobs *jobHandler
|
||||
var jobMetaKey = rt.StringValue("hshjob")
|
||||
|
||||
type job struct {
|
||||
cmd string
|
||||
|
@ -32,6 +34,7 @@ type job struct {
|
|||
cmderr io.Writer
|
||||
stdout *bytes.Buffer
|
||||
stderr *bytes.Buffer
|
||||
ud *rt.UserData
|
||||
}
|
||||
|
||||
func (j *job) start() error {
|
||||
|
@ -64,7 +67,7 @@ func (j *job) start() error {
|
|||
j.pid = proc.Pid
|
||||
j.running = true
|
||||
|
||||
hooks.Em.Emit("job.start", j.lua())
|
||||
hooks.Em.Emit("job.start", rt.UserDataValue(j.ud))
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -79,7 +82,7 @@ func (j *job) stop() {
|
|||
|
||||
func (j *job) finish() {
|
||||
j.running = false
|
||||
hooks.Em.Emit("job.done", j.lua())
|
||||
hooks.Em.Emit("job.done", rt.UserDataValue(j.ud))
|
||||
}
|
||||
|
||||
func (j *job) wait() {
|
||||
|
@ -107,28 +110,16 @@ func (j *job) getProc() *os.Process {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (j *job) lua() rt.Value {
|
||||
jobFuncs := map[string]util.LuaExport{
|
||||
"stop": {j.luaStop, 0, false},
|
||||
"start": {j.luaStart, 0, false},
|
||||
"foreground": {j.luaForeground, 0, false},
|
||||
"background": {j.luaBackground, 0, false},
|
||||
func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
luaJob := rt.NewTable()
|
||||
util.SetExports(l, luaJob, jobFuncs)
|
||||
|
||||
luaJob.Set(rt.StringValue("cmd"), rt.StringValue(j.cmd))
|
||||
luaJob.Set(rt.StringValue("running"), rt.BoolValue(j.running))
|
||||
luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id)))
|
||||
luaJob.Set(rt.StringValue("pid"), rt.IntValue(int64(j.pid)))
|
||||
luaJob.Set(rt.StringValue("exitCode"), rt.IntValue(int64(j.exitCode)))
|
||||
luaJob.Set(rt.StringValue("stdout"), rt.StringValue(string(j.stdout.Bytes())))
|
||||
luaJob.Set(rt.StringValue("stderr"), rt.StringValue(string(j.stderr.Bytes())))
|
||||
j, err := jobArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rt.TableValue(luaJob)
|
||||
}
|
||||
|
||||
func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if !j.running {
|
||||
err := j.start()
|
||||
exit := handleExecErr(err)
|
||||
|
@ -139,7 +130,16 @@ func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
func luaStopJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
j, err := jobArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if j.running {
|
||||
j.stop()
|
||||
j.finish()
|
||||
|
@ -148,7 +148,16 @@ func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (j *job) luaForeground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
func luaForegroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
j, err := jobArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !j.running {
|
||||
return nil, errors.New("job not running")
|
||||
}
|
||||
|
@ -157,7 +166,7 @@ func (j *job) luaForeground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
jobs.foreground = true
|
||||
// this is kinda funny
|
||||
// background continues the process incase it got suspended
|
||||
err := j.background()
|
||||
err = j.background()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -171,12 +180,21 @@ func (j *job) luaForeground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (j *job) luaBackground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
func luaBackgroundJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
j, err := jobArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !j.running {
|
||||
return nil, errors.New("job not running")
|
||||
}
|
||||
|
||||
err := j.background()
|
||||
err = j.background()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -215,8 +233,10 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job {
|
|||
stdout: &bytes.Buffer{},
|
||||
stderr: &bytes.Buffer{},
|
||||
}
|
||||
jb.ud = jobUserData(jb)
|
||||
|
||||
j.jobs[j.latestID] = jb
|
||||
hooks.Em.Emit("job.add", jb.lua())
|
||||
hooks.Em.Emit("job.add", rt.UserDataValue(jb.ud))
|
||||
|
||||
return jb
|
||||
}
|
||||
|
@ -257,6 +277,44 @@ func (j *jobHandler) stopAll() {
|
|||
}
|
||||
|
||||
func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||
jobMethods := rt.NewTable()
|
||||
jFuncs := map[string]util.LuaExport{
|
||||
"stop": {luaStopJob, 1, false},
|
||||
"start": {luaStartJob, 1, false},
|
||||
"foreground": {luaForegroundJob, 1, false},
|
||||
"background": {luaBackgroundJob, 1, false},
|
||||
}
|
||||
util.SetExports(l, jobMethods, jFuncs)
|
||||
|
||||
jobMeta := rt.NewTable()
|
||||
jobIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
j, _ := jobArg(c, 0)
|
||||
|
||||
arg := c.Arg(1)
|
||||
val := jobMethods.Get(arg)
|
||||
|
||||
if val != rt.NilValue {
|
||||
return c.PushingNext1(t.Runtime, val), nil
|
||||
}
|
||||
|
||||
keyStr, _ := arg.TryString()
|
||||
|
||||
switch keyStr {
|
||||
case "cmd": val = rt.StringValue(j.cmd)
|
||||
case "running": val = rt.BoolValue(j.running)
|
||||
case "id": val = rt.IntValue(int64(j.id))
|
||||
case "pid": val = rt.IntValue(int64(j.pid))
|
||||
case "exitCode": val = rt.IntValue(int64(j.exitCode))
|
||||
case "stdout": val = rt.StringValue(string(j.stdout.Bytes()))
|
||||
case "stderr": val = rt.StringValue(string(j.stderr.Bytes()))
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, val), nil
|
||||
}
|
||||
|
||||
jobMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(jobIndex, "__index", 2, false)))
|
||||
l.SetRegistry(jobMetaKey, rt.TableValue(jobMeta))
|
||||
|
||||
jobFuncs := map[string]util.LuaExport{
|
||||
"all": {j.luaAllJobs, 0, false},
|
||||
"last": {j.luaLastJob, 0, false},
|
||||
|
@ -271,6 +329,30 @@ func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table {
|
|||
return luaJob
|
||||
}
|
||||
|
||||
func jobArg(c *rt.GoCont, arg int) (*job, error) {
|
||||
j, ok := valueToJob(c.Arg(arg))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("#%d must be a job", arg + 1)
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
func valueToJob(val rt.Value) (*job, bool) {
|
||||
u, ok := val.TryUserData()
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
j, ok := u.Value().(*job)
|
||||
return j, ok
|
||||
}
|
||||
|
||||
func jobUserData(j *job) *rt.UserData {
|
||||
jobMeta := l.Registry(jobMetaKey)
|
||||
return rt.NewUserData(j, jobMeta.AsTable())
|
||||
}
|
||||
|
||||
func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
|
@ -288,7 +370,7 @@ func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, job.lua()), nil
|
||||
return c.PushingNext(t.Runtime, rt.UserDataValue(job.ud)), nil
|
||||
}
|
||||
|
||||
func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
|
@ -317,7 +399,7 @@ func (j *jobHandler) luaAddJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
jb := j.add(cmd, args, execPath)
|
||||
|
||||
return c.PushingNext1(t.Runtime, jb.lua()), nil
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(jb.ud)), nil
|
||||
}
|
||||
|
||||
func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
|
@ -326,7 +408,7 @@ func (j *jobHandler) luaAllJobs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
jobTbl := rt.NewTable()
|
||||
for id, job := range j.jobs {
|
||||
jobTbl.Set(rt.IntValue(int64(id)), job.lua())
|
||||
jobTbl.Set(rt.IntValue(int64(id)), rt.UserDataValue(job.ud))
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(jobTbl)), nil
|
||||
|
@ -358,5 +440,5 @@ func (j *jobHandler) luaLastJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, job.lua()), nil
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(job.ud)), nil
|
||||
}
|
||||
|
|
46
timer.go
46
timer.go
|
@ -6,8 +6,6 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
|
@ -25,6 +23,7 @@ type timer struct{
|
|||
fun *rt.Closure
|
||||
th *timerHandler
|
||||
ticker *time.Ticker
|
||||
ud *rt.UserData
|
||||
channel chan struct{}
|
||||
}
|
||||
|
||||
|
@ -74,8 +73,17 @@ func (t *timer) stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
err := t.start()
|
||||
func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := timerArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = t.start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -83,26 +91,20 @@ func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (t *timer) luaStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
err := t.stop()
|
||||
func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := timerArg(c, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
err = t.stop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (t *timer) lua() rt.Value {
|
||||
tExports := map[string]util.LuaExport{
|
||||
"start": {t.luaStart, 0, false},
|
||||
"stop": {t.luaStop, 0, false},
|
||||
}
|
||||
luaTimer := rt.NewTable()
|
||||
util.SetExports(l, luaTimer, tExports)
|
||||
|
||||
luaTimer.Set(rt.StringValue("type"), rt.IntValue(int64(t.typ)))
|
||||
luaTimer.Set(rt.StringValue("running"), rt.BoolValue(t.running))
|
||||
luaTimer.Set(rt.StringValue("duration"), rt.IntValue(int64(t.dur / time.Millisecond)))
|
||||
|
||||
return rt.TableValue(luaTimer)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -10,6 +11,8 @@ import (
|
|||
)
|
||||
|
||||
var timers *timerHandler
|
||||
var timerMetaKey = rt.StringValue("hshtimer")
|
||||
|
||||
type timerHandler struct {
|
||||
mu *sync.RWMutex
|
||||
wg *sync.WaitGroup
|
||||
|
@ -44,6 +47,8 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure
|
|||
th: th,
|
||||
id: th.latestID,
|
||||
}
|
||||
t.ud = timerUserData(t)
|
||||
|
||||
th.timers[th.latestID] = t
|
||||
|
||||
return t
|
||||
|
@ -75,7 +80,7 @@ func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
timerTyp := timerType(timerTypInt)
|
||||
tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb)
|
||||
return c.PushingNext1(t.Runtime, tmr.lua()), nil
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(tmr.ud)), nil
|
||||
}
|
||||
|
||||
func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
|
@ -89,13 +94,45 @@ func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
t := th.get(int(id))
|
||||
if t != nil {
|
||||
return c.PushingNext1(thr.Runtime, t.lua()), nil
|
||||
return c.PushingNext1(thr.Runtime, rt.UserDataValue(t.ud)), nil
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
|
||||
timerMethods := rt.NewTable()
|
||||
timerFuncs := map[string]util.LuaExport{
|
||||
"start": {timerStart, 1, false},
|
||||
"stop": {timerStop, 1, false},
|
||||
}
|
||||
util.SetExports(rtm, timerMethods, timerFuncs)
|
||||
|
||||
timerMeta := rt.NewTable()
|
||||
timerIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
ti, _ := timerArg(c, 0)
|
||||
|
||||
arg := c.Arg(1)
|
||||
val := timerMethods.Get(arg)
|
||||
|
||||
if val != rt.NilValue {
|
||||
return c.PushingNext1(t.Runtime, val), nil
|
||||
}
|
||||
|
||||
keyStr, _ := arg.TryString()
|
||||
|
||||
switch keyStr {
|
||||
case "type": val = rt.IntValue(int64(ti.typ))
|
||||
case "running": val = rt.BoolValue(ti.running)
|
||||
case "duration": val = rt.IntValue(int64(ti.dur / time.Millisecond))
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, val), nil
|
||||
}
|
||||
|
||||
timerMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(timerIndex, "__index", 2, false)))
|
||||
l.SetRegistry(timerMetaKey, rt.TableValue(timerMeta))
|
||||
|
||||
thExports := map[string]util.LuaExport{
|
||||
"create": {th.luaCreate, 3, false},
|
||||
"get": {th.luaGet, 1, false},
|
||||
|
@ -106,3 +143,27 @@ func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
|
|||
|
||||
return luaTh
|
||||
}
|
||||
|
||||
func timerArg(c *rt.GoCont, arg int) (*timer, error) {
|
||||
j, ok := valueToTimer(c.Arg(arg))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("#%d must be a timer", arg + 1)
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
func valueToTimer(val rt.Value) (*timer, bool) {
|
||||
u, ok := val.TryUserData()
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
j, ok := u.Value().(*timer)
|
||||
return j, ok
|
||||
}
|
||||
|
||||
func timerUserData(j *timer) *rt.UserData {
|
||||
timerMeta := l.Registry(timerMetaKey)
|
||||
return rt.NewUserData(j, timerMeta.AsTable())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue