Alex Roitman b55300b729 Lua scripting feature. (#224)
* Start on lua scripting

* Implement evalsha, script load, script exists, and script flush

* Type conversions from lua to resp/json.
Refactor to make luastate and luascripts persistent in the controller.

* Change controller.command and all underlying commands to return resp.Value.
Serialize only during the ouput.

* First stab at tile38 call from lua

* Change tile38 into tile38.call in Lua

* Property return errors from scripts

* Minor refactoring.  No locking on script run

* Cleanup/refactoring

* Create a pool of 5 lua states, allow for more as needed. Refactor.

* Use safe map for scripts.  Add a limit for max number of lua states.  Refactor.

* Refactor

* Refactor script commands into atomic, read-only, and non-atomic classes.
Proper locking for all three classes.
Add tests for scripts

* More tests for scripts

* Properly escape newlines in lua-produced errors

* Better test for readonly failure

* Correctly convert ok/err messages between lua and resp.
Add pcall, sha1hex, error_reply, status_reply functions to tile38 namespace in lua.

* Add pcall test. Change writeErr to work with string argument

* Make sure eval/evalsha never attempt to write AOF

* Add eval-set and eval-get to benchmarks

* Fix eval benchmark tests, add more

* Improve benchmarks

* Optimizations and refactoring.

* Add lua memtest

* Typo

* Add dependency

* golint fixes

* gofmt fixes

* Add scripting commands to the core/commands.json

* Use ARGV for args inside lua
2017-10-05 08:20:40 -07:00

194 lines
4.0 KiB
Go

package lua
import (
"fmt"
"strings"
)
const (
VarArgHasArg uint8 = 1
VarArgIsVarArg uint8 = 2
VarArgNeedsArg uint8 = 4
)
type DbgLocalInfo struct {
Name string
StartPc int
EndPc int
}
type DbgCall struct {
Name string
Pc int
}
type FunctionProto struct {
SourceName string
LineDefined int
LastLineDefined int
NumUpvalues uint8
NumParameters uint8
IsVarArg uint8
NumUsedRegisters uint8
Code []uint32
Constants []LValue
FunctionPrototypes []*FunctionProto
DbgSourcePositions []int
DbgLocals []*DbgLocalInfo
DbgCalls []DbgCall
DbgUpvalues []string
stringConstants []string
}
/* Upvalue {{{ */
type Upvalue struct {
next *Upvalue
reg *registry
index int
value LValue
closed bool
}
func (uv *Upvalue) Value() LValue {
//if uv.IsClosed() {
if uv.closed || uv.reg == nil {
return uv.value
}
//return uv.reg.Get(uv.index)
return uv.reg.array[uv.index]
}
func (uv *Upvalue) SetValue(value LValue) {
if uv.IsClosed() {
uv.value = value
} else {
uv.reg.Set(uv.index, value)
}
}
func (uv *Upvalue) Close() {
value := uv.Value()
uv.closed = true
uv.value = value
}
func (uv *Upvalue) IsClosed() bool {
return uv.closed || uv.reg == nil
}
func UpvalueIndex(i int) int {
return GlobalsIndex - i
}
/* }}} */
/* FunctionProto {{{ */
func newFunctionProto(name string) *FunctionProto {
return &FunctionProto{
SourceName: name,
LineDefined: 0,
LastLineDefined: 0,
NumUpvalues: 0,
NumParameters: 0,
IsVarArg: 0,
NumUsedRegisters: 2,
Code: make([]uint32, 0, 128),
Constants: make([]LValue, 0, 32),
FunctionPrototypes: make([]*FunctionProto, 0, 16),
DbgSourcePositions: make([]int, 0, 128),
DbgLocals: make([]*DbgLocalInfo, 0, 16),
DbgCalls: make([]DbgCall, 0, 128),
DbgUpvalues: make([]string, 0, 16),
stringConstants: make([]string, 0, 32),
}
}
func (fp *FunctionProto) String() string {
return fp.str(1, 0)
}
func (fp *FunctionProto) str(level int, count int) string {
indent := strings.Repeat(" ", level-1)
buf := []string{}
buf = append(buf, fmt.Sprintf("%v; function [%v] definition (level %v)\n",
indent, count, level))
buf = append(buf, fmt.Sprintf("%v; %v upvalues, %v params, %v stacks\n",
indent, fp.NumUpvalues, fp.NumParameters, fp.NumUsedRegisters))
for reg, linfo := range fp.DbgLocals {
buf = append(buf, fmt.Sprintf("%v.local %v ; %v\n", indent, linfo.Name, reg))
}
for reg, upvalue := range fp.DbgUpvalues {
buf = append(buf, fmt.Sprintf("%v.upvalue %v ; %v\n", indent, upvalue, reg))
}
for reg, conzt := range fp.Constants {
buf = append(buf, fmt.Sprintf("%v.const %v ; %v\n", indent, conzt.String(), reg))
}
buf = append(buf, "\n")
protono := 0
for no, code := range fp.Code {
inst := opGetOpCode(code)
if inst == OP_CLOSURE {
buf = append(buf, "\n")
buf = append(buf, fp.FunctionPrototypes[protono].str(level+1, protono))
buf = append(buf, "\n")
protono++
}
buf = append(buf, fmt.Sprintf("%v[%03d] %v (line:%v)\n",
indent, no+1, opToString(code), fp.DbgSourcePositions[no]))
}
buf = append(buf, fmt.Sprintf("%v; end of function\n", indent))
return strings.Join(buf, "")
}
/* }}} */
/* LFunction {{{ */
func newLFunctionL(proto *FunctionProto, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: false,
Env: env,
Proto: proto,
GFunction: nil,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func newLFunctionG(gfunc LGFunction, env *LTable, nupvalue int) *LFunction {
return &LFunction{
IsG: true,
Env: env,
Proto: nil,
GFunction: gfunc,
Upvalues: make([]*Upvalue, nupvalue),
}
}
func (fn *LFunction) LocalName(regno, pc int) (string, bool) {
if fn.IsG {
return "", false
}
p := fn.Proto
for i := 0; i < len(p.DbgLocals) && p.DbgLocals[i].StartPc < pc; i++ {
if pc < p.DbgLocals[i].EndPc {
regno--
if regno == 0 {
return p.DbgLocals[i].Name, true
}
}
}
return "", false
}
/* }}} */