mirror of
				https://github.com/sammy-ette/Hilbish
				synced 2025-08-10 02:52:03 +00:00 
			
		
		
		
	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 dot
This commit is contained in:
		
							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 | 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 | account for runners wanting to prompt for continued input, and to add it | ||||||
| properly to history. | 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. | - All `fs` module functions which take paths now implicitly expand ~ to home. | ||||||
| 
 | 
 | ||||||
| ### Fixed | ### 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 := timers.create(timerTimeout, interval, cb) | ||||||
| 	timer.start() | 	timer.start() | ||||||
| 	 | 	 | ||||||
| 	return c.PushingNext1(t.Runtime, timer.lua()), nil | 	return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // interval(cb, time) | // 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 := timers.create(timerInterval, interval, cb) | ||||||
| 	timer.start() | 	timer.start() | ||||||
| 
 | 
 | ||||||
| 	return c.PushingNext1(t.Runtime, timer.lua()), nil | 	return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // complete(scope, cb) | // 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. | - `disown(id)`: Removes a job by ID from the job table. | ||||||
| 
 | 
 | ||||||
| # Job Object | # Job Object | ||||||
| A job object on the Lua side is a table with some functions. | A job object is a piece of `userdata`. All the functions of a job require | ||||||
| On the under side it represents a job in the job table. | 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, | You can still have a job object for a disowned job, | ||||||
| it just won't be *working* anywhere. :^) | it just won't be *working* anywhere. :^) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,9 +16,17 @@ a timer via ID. | |||||||
| when the timer is triggered. | when the timer is triggered. | ||||||
| 
 | 
 | ||||||
| # Timer Object | # Timer Object | ||||||
| Those previously mentioned functions return a `timer` object, to which you can | All those previously mentioned functions return a `timer` object, to which | ||||||
| stop and start a timer again. The functions of the timers interface also | you can stop and start a timer again. | ||||||
| return a timer object. | 
 | ||||||
|  | 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 | ## Properties | ||||||
| - `duration`: amount of time the timer runs for in milliseconds | - `duration`: amount of time the timer runs for in milliseconds | ||||||
|  | |||||||
							
								
								
									
										144
									
								
								job.go
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								job.go
									
									
									
									
									
								
							| @ -3,6 +3,7 @@ package main | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/exec" | 	"os/exec" | ||||||
| @ -15,6 +16,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var jobs *jobHandler | var jobs *jobHandler | ||||||
|  | var jobMetaKey = rt.StringValue("hshjob") | ||||||
| 
 | 
 | ||||||
| type job struct { | type job struct { | ||||||
| 	cmd string | 	cmd string | ||||||
| @ -32,6 +34,7 @@ type job struct { | |||||||
| 	cmderr io.Writer | 	cmderr io.Writer | ||||||
| 	stdout *bytes.Buffer | 	stdout *bytes.Buffer | ||||||
| 	stderr *bytes.Buffer | 	stderr *bytes.Buffer | ||||||
|  | 	ud *rt.UserData | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (j *job) start() error { | func (j *job) start() error { | ||||||
| @ -64,7 +67,7 @@ func (j *job) start() error { | |||||||
| 	j.pid = proc.Pid | 	j.pid = proc.Pid | ||||||
| 	j.running = true | 	j.running = true | ||||||
| 
 | 
 | ||||||
| 	hooks.Em.Emit("job.start", j.lua()) | 	hooks.Em.Emit("job.start", rt.UserDataValue(j.ud)) | ||||||
| 
 | 
 | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| @ -79,7 +82,7 @@ func (j *job) stop() { | |||||||
| 
 | 
 | ||||||
| func (j *job) finish() { | func (j *job) finish() { | ||||||
| 	j.running = false | 	j.running = false | ||||||
| 	hooks.Em.Emit("job.done", j.lua()) | 	hooks.Em.Emit("job.done", rt.UserDataValue(j.ud)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (j *job) wait() { | func (j *job) wait() { | ||||||
| @ -107,28 +110,16 @@ func (j *job) getProc() *os.Process { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (j *job) lua() rt.Value { | func luaStartJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	jobFuncs := map[string]util.LuaExport{ | 	if err := c.Check1Arg(); err != nil { | ||||||
| 		"stop": {j.luaStop, 0, false}, | 		return nil, err | ||||||
| 		"start": {j.luaStart, 0, false}, |  | ||||||
| 		"foreground": {j.luaForeground, 0, false}, |  | ||||||
| 		"background": {j.luaBackground, 0, false}, |  | ||||||
| 	} | 	} | ||||||
| 	luaJob := rt.NewTable() |  | ||||||
| 	util.SetExports(l, luaJob, jobFuncs) |  | ||||||
| 
 | 
 | ||||||
| 	luaJob.Set(rt.StringValue("cmd"), rt.StringValue(j.cmd)) | 	j, err := jobArg(c, 0) | ||||||
| 	luaJob.Set(rt.StringValue("running"), rt.BoolValue(j.running)) | 	if err != nil { | ||||||
| 	luaJob.Set(rt.StringValue("id"), rt.IntValue(int64(j.id))) | 		return nil, err | ||||||
| 	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()))) |  | ||||||
| 
 | 
 | ||||||
| 	return rt.TableValue(luaJob) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { |  | ||||||
| 	if !j.running { | 	if !j.running { | ||||||
| 		err := j.start() | 		err := j.start() | ||||||
| 		exit := handleExecErr(err) | 		exit := handleExecErr(err) | ||||||
| @ -139,7 +130,16 @@ func (j *job) luaStart(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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 { | 	if j.running { | ||||||
| 		j.stop() | 		j.stop() | ||||||
| 		j.finish() | 		j.finish() | ||||||
| @ -148,7 +148,16 @@ func (j *job) luaStop(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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 { | 	if !j.running { | ||||||
| 		return nil, errors.New("job not 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 | 	jobs.foreground = true | ||||||
| 	// this is kinda funny | 	// this is kinda funny | ||||||
| 	// background continues the process incase it got suspended | 	// background continues the process incase it got suspended | ||||||
| 	err := j.background() | 	err = j.background() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -171,12 +180,21 @@ func (j *job) luaForeground(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	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 { | 	if !j.running { | ||||||
| 		return nil, errors.New("job not running") | 		return nil, errors.New("job not running") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := j.background() | 	err = j.background() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -215,8 +233,10 @@ func (j *jobHandler) add(cmd string, args []string, path string) *job { | |||||||
| 		stdout: &bytes.Buffer{}, | 		stdout: &bytes.Buffer{}, | ||||||
| 		stderr: &bytes.Buffer{}, | 		stderr: &bytes.Buffer{}, | ||||||
| 	} | 	} | ||||||
|  | 	jb.ud = jobUserData(jb) | ||||||
|  | 
 | ||||||
| 	j.jobs[j.latestID] = jb | 	j.jobs[j.latestID] = jb | ||||||
| 	hooks.Em.Emit("job.add", jb.lua()) | 	hooks.Em.Emit("job.add", rt.UserDataValue(jb.ud)) | ||||||
| 
 | 
 | ||||||
| 	return jb | 	return jb | ||||||
| } | } | ||||||
| @ -257,6 +277,44 @@ func (j *jobHandler) stopAll() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { | 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{ | 	jobFuncs := map[string]util.LuaExport{ | ||||||
| 		"all": {j.luaAllJobs, 0, false}, | 		"all": {j.luaAllJobs, 0, false}, | ||||||
| 		"last": {j.luaLastJob, 0, false}, | 		"last": {j.luaLastJob, 0, false}, | ||||||
| @ -271,6 +329,30 @@ func (j *jobHandler) loader(rtm *rt.Runtime) *rt.Table { | |||||||
| 	return luaJob | 	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) { | func (j *jobHandler) luaGetJob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	j.mu.RLock() | 	j.mu.RLock() | ||||||
| 	defer j.mu.RUnlock() | 	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.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) { | 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) | 	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) { | 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() | 	jobTbl := rt.NewTable() | ||||||
| 	for id, job := range j.jobs { | 	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 | 	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.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" | 	"os" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"hilbish/util" |  | ||||||
| 	 |  | ||||||
| 	rt "github.com/arnodel/golua/runtime" | 	rt "github.com/arnodel/golua/runtime" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -25,6 +23,7 @@ type timer struct{ | |||||||
| 	fun *rt.Closure | 	fun *rt.Closure | ||||||
| 	th *timerHandler | 	th *timerHandler | ||||||
| 	ticker *time.Ticker | 	ticker *time.Ticker | ||||||
|  | 	ud *rt.UserData | ||||||
| 	channel chan struct{} | 	channel chan struct{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -74,8 +73,17 @@ func (t *timer) stop() error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	err := t.start() | 	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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @ -83,26 +91,20 @@ func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 	return c.Next(), nil | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *timer) luaStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | ||||||
| 	err := t.stop() | 	if err := c.Check1Arg(); err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t, err := timerArg(c, 0) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	 | 
 | ||||||
|  | 	err = t.stop() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return c.Next(), nil | 	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 | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| @ -10,6 +11,8 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var timers *timerHandler | var timers *timerHandler | ||||||
|  | var timerMetaKey = rt.StringValue("hshtimer") | ||||||
|  | 
 | ||||||
| type timerHandler struct { | type timerHandler struct { | ||||||
| 	mu *sync.RWMutex | 	mu *sync.RWMutex | ||||||
| 	wg *sync.WaitGroup | 	wg *sync.WaitGroup | ||||||
| @ -44,6 +47,8 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure | |||||||
| 		th: th, | 		th: th, | ||||||
| 		id: th.latestID, | 		id: th.latestID, | ||||||
| 	} | 	} | ||||||
|  | 	t.ud = timerUserData(t) | ||||||
|  | 
 | ||||||
| 	th.timers[th.latestID] = t | 	th.timers[th.latestID] = t | ||||||
| 	 | 	 | ||||||
| 	return t | 	return t | ||||||
| @ -75,7 +80,7 @@ func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { | |||||||
| 
 | 
 | ||||||
| 	timerTyp := timerType(timerTypInt) | 	timerTyp := timerType(timerTypInt) | ||||||
| 	tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb) | 	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) { | 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)) | 	t := th.get(int(id)) | ||||||
| 	if t != nil { | 	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 | 	return c.Next(), nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table { | 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{ | 	thExports := map[string]util.LuaExport{ | ||||||
| 		"create": {th.luaCreate, 3, false}, | 		"create": {th.luaCreate, 3, false}, | ||||||
| 		"get": {th.luaGet, 1, false}, | 		"get": {th.luaGet, 1, false}, | ||||||
| @ -106,3 +143,27 @@ func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table { | |||||||
| 
 | 
 | ||||||
| 	return luaTh | 	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user