张金坤 8 سال پیش
والد
کامیت
3a68c1da2f

+ 1 - 0
common/src/github.com/go-gomail/gomail

@@ -0,0 +1 @@
+Subproject commit 81ebce5c23dfd25c6c67194b37d3dd3f338c98b1

+ 4 - 2
common/src/github.com/yuin/gopher-lua/.travis.yml

@@ -1,13 +1,15 @@
 language: go
 
 go:
-  - 1.4
-  - 1.5
   - 1.6
+  - 1.7
+  - 1.8
 
 before_install:
   - go get github.com/axw/gocov/gocov
   - go get github.com/mattn/goveralls
   - if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
+install:
+  - go get -u -v $(go list -f '{{join .Imports "\n"}}{{"\n"}}{{join .TestImports "\n"}}' ./... | sort | uniq | grep -v gopher-lua)
 script:
     - $HOME/gopath/bin/goveralls -service=travis-ci

+ 78 - 7
common/src/github.com/yuin/gopher-lua/README.rst

@@ -17,6 +17,7 @@ GopherLua: VM and compiler for Lua in Go.
 
 |
 
+
 GopherLua is a Lua5.1 VM and compiler written in Go. GopherLua has a same goal
 with Lua: **Be a scripting language with extensible semantics** . It provides
 Go APIs that allow you to easily embed a scripting language to your Go host
@@ -42,6 +43,8 @@ How about performance?
 ----------------------------------------------------------------
 GopherLua is not fast but not too slow, I think.
 
+GopherLua has almost equivalent ( or little bit better ) performance as Python3 on micro benchmarks.
+
 There are some benchmarks on the `wiki page <https://github.com/yuin/gopher-lua/wiki/Benchmarks>`_ .
 
 ----------------------------------------------------------------
@@ -52,7 +55,7 @@ Installation
 
    go get github.com/yuin/gopher-lua
 
-GopherLua supports >= Go1.4.
+GopherLua supports >= Go1.6.
 
 ----------------------------------------------------------------
 Usage
@@ -147,10 +150,10 @@ To test ``LNilType`` and ``LBool``, You **must** use pre-defined constants.
 
    lv := L.Get(-1) // get the value at the top of the stack
 
-   if lv == LTrue { // correct
+   if lv == lua.LTrue { // correct
    }
 
-   if bl, ok == lv.(lua.LBool); ok && bool(bl) { // wrong
+   if bl, ok := lv.(lua.LBool); ok && bool(bl) { // wrong
    }
 
 In Lua, both ``nil`` and ``false`` make a condition false. ``LVIsFalse`` and ``LVAsBool`` implement this specification.
@@ -158,10 +161,10 @@ In Lua, both ``nil`` and ``false`` make a condition false. ``LVIsFalse`` and ``L
 .. code-block:: go
 
    lv := L.Get(-1) // get the value at the top of the stack
-   if LVIsFalse(lv) { // lv is nil or false
+   if lua.LVIsFalse(lv) { // lv is nil or false
    }
 
-   if LVAsBool(lv) { // lv is neither nil nor false
+   if lua.LVAsBool(lv) { // lv is neither nil nor false
    }
 
 Objects that based on go structs(``LFunction``. ``LUserData``, ``LTable``)
@@ -244,7 +247,7 @@ Working with coroutines.
 
 .. code-block:: go
 
-   co := L.NewThread() /* create a new thread */
+   co, _ := L.NewThread() /* create a new thread */
    fn := L.GetGlobal("coro").(*lua.LFunction) /* get function from lua */
    for {
        st, err, values := L.Resume(co, fn)
@@ -456,6 +459,72 @@ You can extend GopherLua with new types written in Go.
         }
     }
 
++++++++++++++++++++++++++++++++++++++++++
+Terminating a running LState
++++++++++++++++++++++++++++++++++++++++++
+GopherLua supports the `Go Concurrency Patterns: Context <https://blog.golang.org/context>`_ .
+
+
+.. code-block:: go
+
+    L := lua.NewState()
+    defer L.Close()
+    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+    defer cancel()
+    // set the context to our LState
+    L.SetContext(ctx)
+    err := L.DoString(`
+      local clock = os.clock
+      function sleep(n)  -- seconds
+        local t0 = clock()
+        while clock() - t0 <= n do end
+      end
+      sleep(3)
+    `)
+    // err.Error() contains "context deadline exceeded"
+
+With coroutines
+
+.. code-block:: go
+
+	L := lua.NewState()
+	defer L.Close()
+	ctx, cancel := context.WithCancel(context.Background())
+	L.SetContext(ctx)
+	defer cancel()
+	L.DoString(`
+	    function coro()
+		  local i = 0
+		  while true do
+		    coroutine.yield(i)
+			i = i+1
+		  end
+		  return i
+	    end
+	`)
+	co, cocancel := L.NewThread()
+	defer cocancel()
+	fn := L.GetGlobal("coro").(*LFunction)
+    
+	_, err, values := L.Resume(co, fn) // err is nil
+    
+	cancel() // cancel the parent context
+    
+	_, err, values = L.Resume(co, fn) // err is NOT nil : child context was canceled
+
+**Note that using a context causes performance degradation.**
+
+.. code-block::
+
+    time ./glua-with-context.exe fib.lua
+    9227465
+    0.01s user 0.11s system 1% cpu 7.505 total
+
+    time ./glua-without-context.exe fib.lua
+    9227465
+    0.01s user 0.01s system 0% cpu 5.306 total
+
+
 +++++++++++++++++++++++++++++++++++++++++
 Goroutines
 +++++++++++++++++++++++++++++++++++++++++
@@ -670,7 +739,6 @@ Unsupported functions
 
 - ``string.dump``
 - ``os.setlocale``
-- ``collectgarbage``
 - ``lua_Debug.namewhat``
 - ``package.loadlib``
 - debug hooks
@@ -679,6 +747,7 @@ Unsupported functions
 Miscellaneous notes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+- ``collectgarbage`` does not take any arguments and runs the garbage collector for the entire Go program.
 - ``file:setvbuf`` does not support a line buffering.
 - Daylight saving time is not supported.
 - GopherLua has a function to set an environment variable : ``os.setenv(name, value)``
@@ -711,6 +780,8 @@ Libraries for GopherLua
 - `gluayaml <https://github.com/kohkimakimoto/gluayaml>`_ : Yaml parser for gopher-lua
 - `glua-lfs <https://github.com/layeh/gopher-lfs>`_ : Partially implements the luafilesystem module for gopher-lua
 - `gluaurl <https://github.com/cjoudrey/gluaurl>`_ : A url parser/builder module for gopher-lua
+- `gluahttpscrape <https://github.com/felipejfc/gluahttpscrape>`_ : A simple HTML scraper module for gopher-lua
+- `gluaxmlpath <https://github.com/ailncode/gluaxmlpath>`_ : An xmlpath module for gopher-lua
 
 ----------------------------------------------------------------
 Donation

+ 51 - 0
common/src/github.com/yuin/gopher-lua/_glua-tests/issues.lua

@@ -114,3 +114,54 @@ stack traceback:
 @TAB@issues.lua:103: in main chunk
 @TAB@[G]: ?]], "@TAB@", "\t"))
 
+-- issue 81
+local tbl = {
+        [-1] = "a",
+        [0] = "b",
+        [1] = "c",
+}
+local a, b = next(tbl, nil)
+assert( a == -1 and b == "a" or a == 0 and b == "b" or a == 1 and b == "c")
+local a, b = next(tbl, a)
+assert( a == -1 and b == "a" or a == 0 and b == "b" or a == 1 and b == "c")
+local a, b = next(tbl, a)
+assert( a == -1 and b == "a" or a == 0 and b == "b" or a == 1 and b == "c")
+local a, b = next(tbl, a)
+assert( a == nil and b == nil)
+
+local tbl = {'a', 'b'}
+local a, b = next(tbl, nil)
+assert(a == 1 and b == "a")
+local a, b = next(tbl, a)
+assert(a == 2 and b == "b")
+local a, b = next(tbl, a)
+assert(a == nil and b == nil)
+
+-- issue 82
+local cr = function()
+        return coroutine.wrap(function()
+                coroutine.yield(1, "a")
+                coroutine.yield(2, "b")
+        end)
+end
+
+local f = cr()
+local a, b = f()
+assert(a == 1 and b == "a")
+local a, b = f()
+assert(a == 2 and b == "b")
+
+-- issue 91, 92
+local url = "www.aaa.bbb_abc123-321-cba_abc123"
+assert(string.match(url, ".-([%w-]*)[.]*") == "www")
+
+local s = "hello.world"
+assert(s:match("([^.]+).world") == "hello")
+
+local s = "hello-world"
+assert(s:match("([^-]+)-world") == "hello")
+
+-- issue 93
+local t = {}
+local ok, msg = pcall(function() t.notfound() end)
+assert(not ok and string.find(msg, "attempt to call a non-function object", 1, true))

+ 36 - 6
common/src/github.com/yuin/gopher-lua/_state.go

@@ -3,6 +3,7 @@ package lua
 import (
 	"fmt"
 	"github.com/yuin/gopher-lua/parse"
+	"golang.org/x/net/context"
 	"io"
 	"math"
 	"os"
@@ -331,6 +332,8 @@ func newLState(options Options) *LState {
 		wrapped:      false,
 		uvcache:      nil,
 		hasErrorFunc: false,
+		mainLoop:     mainLoop,
+		ctx:          nil,
 	}
 	ls.Env = ls.G.Global
 	return ls
@@ -783,9 +786,9 @@ func (ls *LState) callR(nargs, nret, rbase int) {
 	if ls.G.MainThread == nil {
 		ls.G.MainThread = ls
 		ls.G.CurrentThread = ls
-		mainLoop(ls, nil)
+		ls.mainLoop(ls, nil)
 	} else {
-		mainLoop(ls, ls.currentFrame)
+		ls.mainLoop(ls, ls.currentFrame)
 	}
 	if nret != MultRet {
 		ls.reg.SetTop(rbase + nret)
@@ -1107,19 +1110,25 @@ func (ls *LState) Remove(index int) {
 /* object allocation {{{ */
 
 func (ls *LState) NewTable() *LTable {
-	// TODO change size
-	return newLTable(32, 32)
+	return newLTable(defaultArrayCap, defaultHashCap)
 }
 
 func (ls *LState) CreateTable(acap, hcap int) *LTable {
 	return newLTable(acap, hcap)
 }
 
-func (ls *LState) NewThread() *LState {
+// NewThread returns a new LState that shares with the original state all global objects.
+// If the original state has context.Context, the new state has a new child context of the original state and this function returns its cancel function.
+func (ls *LState) NewThread() (*LState, context.CancelFunc) {
 	thread := newLState(ls.Options)
 	thread.G = ls.G
 	thread.Env = ls.Env
-	return thread
+	var f context.CancelFunc = nil
+	if ls.ctx != nil {
+		thread.mainLoop = mainLoopWithContext
+		thread.ctx, f = context.WithCancel(ls.ctx)
+	}
+	return thread, f
 }
 
 func (ls *LState) NewUserData() *LUserData {
@@ -1566,6 +1575,8 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) {
 			} else if len(err.(*ApiError).StackTrace) == 0 {
 				err.(*ApiError).StackTrace = ls.stackTrace(0)
 			}
+			ls.stack.SetSp(sp)
+			ls.currentFrame = ls.stack.Last()
 			ls.reg.SetTop(base)
 		}
 		ls.stack.SetSp(sp)
@@ -1742,6 +1753,25 @@ func (ls *LState) SetMx(mx int) {
 	}()
 }
 
+// SetContext set a context ctx to this LState. The provided ctx must be non-nil.
+func (ls *LState) SetContext(ctx context.Context) {
+	ls.mainLoop = mainLoopWithContext
+	ls.ctx = ctx
+}
+
+// Context returns the LState's context. To change the context, use WithContext.
+func (ls *LState) Context() context.Context {
+	return ls.ctx
+}
+
+// RemoveContext removes the context associated with this LState and returns this context.
+func (ls *LState) RemoveContext() context.Context {
+	oldctx := ls.ctx
+	ls.mainLoop = mainLoop
+	ls.ctx = nil
+	return oldctx
+}
+
 // Converts the Lua value at the given acceptable index to the chan LValue.
 func (ls *LState) ToChannel(n int) chan LValue {
 	if lv, ok := ls.Get(n).(LChannel); ok {

+ 41 - 5
common/src/github.com/yuin/gopher-lua/_vm.go

@@ -30,6 +30,36 @@ func mainLoop(L *LState, baseframe *callFrame) {
 	}
 }
 
+func mainLoopWithContext(L *LState, baseframe *callFrame) {
+	var inst uint32
+	var cf *callFrame
+
+	if L.stack.IsEmpty() {
+		return
+	}
+
+	L.currentFrame = L.stack.Last()
+	if L.currentFrame.Fn.IsG {
+		callGFunction(L, false)
+		return
+	}
+
+	for {
+		cf = L.currentFrame
+		inst = cf.Fn.Proto.Code[cf.Pc]
+		cf.Pc++
+		select {
+		case <-L.ctx.Done():
+			L.RaiseError(L.ctx.Err().Error())
+			return
+		default:
+			if jumpTable[int(inst>>26)](L, inst, baseframe) == 1 {
+				return
+			}
+		}
+	}
+}
+
 func copyReturnValues(L *LState, regv, start, n, b int) { // +inline-start
 	if b == 1 {
 		// +inline-call L.reg.FillNil  regv n
@@ -118,7 +148,7 @@ func threadRun(L *LState) {
 			}
 		}
 	}()
-	mainLoop(L, nil)
+	L.mainLoop(L, nil)
 }
 
 type instFunc func(*LState, uint32, *callFrame) int
@@ -548,6 +578,9 @@ func init() {
 			} else {
 				callable, meta = L.metaCall(lv)
 			}
+			if callable == nil {
+				L.RaiseError("attempt to call a non-function object")
+			}
 			// +inline-call L.closeUpvalues lbase
 			if callable.IsG {
 				luaframe := cf
@@ -676,14 +709,17 @@ func init() {
 			RA := lbase + A
 			C := int(inst>>9) & 0x1ff //GETC
 			nret := C
-			reg.SetTop(RA + 3)
+			reg.SetTop(RA + 3 + 2)
+			reg.Set(RA+3+2, reg.Get(RA+2))
+			reg.Set(RA+3+1, reg.Get(RA+1))
+			reg.Set(RA+3, reg.Get(RA))
 			L.callR(2, nret, RA+3)
-			reg.SetTop(RA + 2 + C + 1)
 			if value := reg.Get(RA + 3); value != LNil {
 				reg.Set(RA+2, value)
-			} else {
-				cf.Pc++
+				pc := cf.Fn.Proto.Code[cf.Pc]
+				cf.Pc += int(pc&0x3ffff) - opMaxArgSbx
 			}
+			cf.Pc++
 			return 0
 		},
 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETLIST

+ 1 - 1
common/src/github.com/yuin/gopher-lua/auxlib.go

@@ -239,7 +239,7 @@ func (ls *LState) ArgError(n int, message string) {
 }
 
 func (ls *LState) TypeError(n int, typ LValueType) {
-	//ls.RaiseError("bad argument #%v to %v (%v expected, got %v)", n, ls.rawFrameFuncName(ls.currentFrame), typ.String(), ls.Get(n).Type().String())
+	ls.RaiseError("bad argument #%v to %v (%v expected, got %v)", n, ls.rawFrameFuncName(ls.currentFrame), typ.String(), ls.Get(n).Type().String())
 }
 
 /* }}} */

+ 1 - 1
common/src/github.com/yuin/gopher-lua/auxlib_test.go

@@ -109,7 +109,7 @@ func TestCheckThread(t *testing.T) {
 	L := NewState()
 	defer L.Close()
 	errorIfGFuncNotFail(t, L, func(L *LState) int {
-		th := L.NewThread()
+		th, _ := L.NewThread()
 		L.Push(th)
 		errorIfNotEqual(t, th, L.CheckThread(2))
 		L.Push(LNumber(10))

+ 5 - 4
common/src/github.com/yuin/gopher-lua/compile.go

@@ -99,7 +99,7 @@ func savereg(ec *expcontext, reg int) int {
 
 func raiseCompileError(context *funcContext, line int, format string, args ...interface{}) {
 	msg := fmt.Sprintf(format, args...)
-	panic(&CompileError{Context: context, Line: line, Message: msg})
+	panic(&CompileError{context: context, Line: line, Message: msg})
 }
 
 func isVarArgReturnExpr(expr ast.Expr) bool {
@@ -128,13 +128,13 @@ func lnumberValue(expr ast.Expr) (LNumber, bool) {
 /* utilities }}} */
 
 type CompileError struct { // {{{
-	Context *funcContext
+	context *funcContext
 	Line    int
 	Message string
 }
 
 func (e *CompileError) Error() string {
-	return fmt.Sprintf("compile error near line(%v) %v: %v", e.Line, e.Context.Proto.SourceName, e.Message)
+	return fmt.Sprintf("compile error near line(%v) %v: %v", e.Line, e.context.Proto.SourceName, e.Message)
 } // }}}
 
 type codeStore struct { // {{{
@@ -709,6 +709,7 @@ func compileReturnStmt(context *funcContext, stmt *ast.ReturnStmt) { // {{{
 			reg += compileExpr(context, reg, ex, ecnone(-2))
 			code.SetOpCode(code.LastPC(), OP_TAILCALL)
 			code.AddABC(OP_RETURN, a, 0, 0, sline(stmt))
+			return
 		}
 	}
 
@@ -1088,7 +1089,7 @@ func constFold(exp ast.Expr) ast.Expr { // {{{
 			case "^":
 				return &constLValueExpr{Value: LNumber(math.Pow(float64(lvalue), float64(rvalue)))}
 			default:
-				panic(fmt.Sprintf("unknwon binop: %v", expr.Operator))
+				panic(fmt.Sprintf("unknown binop: %v", expr.Operator))
 			}
 		} else {
 			retexpr := *expr

+ 1 - 1
common/src/github.com/yuin/gopher-lua/coroutinelib.go

@@ -18,7 +18,7 @@ var coFuncs = map[string]LGFunction{
 
 func coCreate(L *LState) int {
 	fn := L.CheckFunction(1)
-	newthread := L.NewThread()
+	newthread, _ := L.NewThread()
 	base := 0
 	newthread.stack.Push(callFrame{
 		Fn:         fn,

+ 0 - 47
common/src/github.com/yuin/gopher-lua/disablelib.go

@@ -1,47 +0,0 @@
-// disablelib 过滤函数,重写lua函数
-package lua
-
-func Disablelib(dismap map[string]map[string]bool) {
-	//baselib
-	baselib := dismap["baselib"]
-	if baselib != nil {
-		for k, v := range baseFuncs {
-			if baselib[k] == true {
-				v = baseDisable
-				baseFuncs[k] = v
-			}
-		}
-	}
-
-	//oslib
-	oslib := dismap["oslib"]
-	if oslib != nil {
-		for k, v := range osFuncs {
-			if oslib[k] == true {
-				v = iosDisable
-				osFuncs[k] = v
-			}
-		}
-	}
-
-	//iolib
-	iolib := dismap["iolib"]
-	if iolib != nil {
-		for k, v := range ioFuncs {
-			if iolib[k] == true {
-				v = iosDisable
-				ioFuncs[k] = v
-			}
-		}
-	}
-}
-
-//baselib
-func baseDisable(L *LState) int {
-	return 0
-}
-
-//iolib,oslib
-func iosDisable(L *LState) int {
-	return 1
-}

+ 1 - 2
common/src/github.com/yuin/gopher-lua/iolib.go

@@ -9,7 +9,6 @@ import (
 	"os"
 	"os/exec"
 	"syscall"
-	"unsafe"
 )
 
 var ioFuncs = map[string]LGFunction{
@@ -242,7 +241,7 @@ func fileWriteAux(L *LState, file *lFile, idx int) int {
 	for i := idx; i <= top; i++ {
 		L.CheckTypes(i, LTNumber, LTString)
 		s := LVAsString(L.Get(i))
-		if _, err = out.Write(*(*[]byte)(unsafe.Pointer(&s))); err != nil {
+		if _, err = out.Write(unsafeFastStringToReadOnlyBytes(s)); err != nil {
 			goto errreturn
 		}
 	}

+ 34 - 30
common/src/github.com/yuin/gopher-lua/pm/pm.go

@@ -5,7 +5,7 @@ import (
 	"fmt"
 )
 
-const _EOS = -1
+const EOS = -1
 const _UNKNOWN = -2
 
 /* Error {{{ */
@@ -24,7 +24,7 @@ func newError(pos int, message string, args ...interface{}) *Error {
 
 func (e *Error) Error() string {
 	switch e.Pos {
-	case _EOS:
+	case EOS:
 		return fmt.Sprintf("%s at EOS", e.Message)
 	case _UNKNOWN:
 		return fmt.Sprintf("%s", e.Message)
@@ -104,13 +104,13 @@ func (sc *scanner) Next() int {
 	if !sc.State.started {
 		sc.State.started = true
 		if len(sc.src) == 0 {
-			sc.State.Pos = _EOS
+			sc.State.Pos = EOS
 		}
 	} else {
 		sc.State.Pos = sc.NextPos()
 	}
-	if sc.State.Pos == _EOS {
-		return _EOS
+	if sc.State.Pos == EOS {
+		return EOS
 	}
 	return int(sc.src[sc.State.Pos])
 }
@@ -120,8 +120,8 @@ func (sc *scanner) CurrentPos() int {
 }
 
 func (sc *scanner) NextPos() int {
-	if sc.State.Pos == _EOS || sc.State.Pos >= len(sc.src)-1 {
-		return _EOS
+	if sc.State.Pos == EOS || sc.State.Pos >= len(sc.src)-1 {
+		return EOS
 	}
 	if !sc.State.started {
 		return 0
@@ -131,10 +131,10 @@ func (sc *scanner) NextPos() int {
 }
 
 func (sc *scanner) Peek() int {
-	cureof := sc.State.Pos == _EOS
+	cureof := sc.State.Pos == EOS
 	ch := sc.Next()
 	if !cureof {
-		if sc.State.Pos == _EOS {
+		if sc.State.Pos == EOS {
 			sc.State.Pos = len(sc.src) - 1
 		} else {
 			sc.State.Pos--
@@ -310,7 +310,11 @@ func parseClass(sc *scanner, allowset bool) class {
 	case '%':
 		return &singleClass{sc.Next()}
 	case '.':
-		return &dotClass{}
+		if allowset {
+			return &dotClass{}
+		} else {
+			return &charClass{ch}
+		}
 	case '[':
 		if !allowset {
 			panic(newError(sc.CurrentPos(), "invalid '['"))
@@ -318,7 +322,7 @@ func parseClass(sc *scanner, allowset bool) class {
 		return parseClassSet(sc)
 	//case '^' '$', '(', ')', ']', '*', '+', '-', '?':
 	//	panic(newError(sc.CurrentPos(), "invalid %c", ch))
-	case _EOS:
+	case EOS:
 		panic(newError(sc.CurrentPos(), "unexpected EOS"))
 	default:
 		return &charClass{ch}
@@ -335,20 +339,20 @@ func parseClassSet(sc *scanner) class {
 	for {
 		ch := sc.Peek()
 		switch ch {
-		case '-':
-			if isrange {
-				panic(newError(sc.CurrentPos(), "invalid range"))
-			}
-			sc.Next()
-			isrange = true
-			continue
 		case '[':
 			panic(newError(sc.CurrentPos(), "'[' can not be nested"))
 		case ']':
 			sc.Next()
 			goto exit
-		case _EOS:
+		case EOS:
 			panic(newError(sc.CurrentPos(), "unexpected EOS"))
+		case '-':
+			if len(set.Classes) > 0 {
+				sc.Next()
+				isrange = true
+				continue
+			}
+			fallthrough
 		default:
 			set.Classes = append(set.Classes, parseClass(sc, false))
 		}
@@ -362,7 +366,7 @@ func parseClassSet(sc *scanner) class {
 	}
 exit:
 	if isrange {
-		panic(newError(sc.CurrentPos(), "unfinished range"))
+		set.Classes = append(set.Classes, &charClass{'-'})
 	}
 
 	return set
@@ -418,23 +422,23 @@ func parsePattern(sc *scanner, toplevel bool) *seqPattern {
 			}
 		case '*', '+', '-', '?':
 			sc.Next()
-			if len(pat.Patterns) == 0 {
-				panic(newError(sc.CurrentPos(), "no charcter class before '%c'", ch))
-			}
-			spat, ok := pat.Patterns[len(pat.Patterns)-1].(*singlePattern)
-			if !ok {
-				panic(newError(sc.CurrentPos(), "invalid charcter class before '%c'", ch))
+			if len(pat.Patterns) > 0 {
+				spat, ok := pat.Patterns[len(pat.Patterns)-1].(*singlePattern)
+				if ok {
+					pat.Patterns = pat.Patterns[0 : len(pat.Patterns)-1]
+					pat.Patterns = append(pat.Patterns, &repeatPattern{ch, spat.Class})
+					continue
+				}
 			}
-			pat.Patterns = pat.Patterns[0 : len(pat.Patterns)-1]
-			pat.Patterns = append(pat.Patterns, &repeatPattern{ch, spat.Class})
+			pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
 		case '$':
-			if toplevel && (sc.NextPos() == sc.Length()-1 || sc.NextPos() == _EOS) {
+			if toplevel && (sc.NextPos() == sc.Length()-1 || sc.NextPos() == EOS) {
 				pat.MustTail = true
 			} else {
 				pat.Patterns = append(pat.Patterns, &singlePattern{&charClass{ch}})
 			}
 			sc.Next()
-		case _EOS:
+		case EOS:
 			sc.Next()
 			goto exit
 		default:

+ 62 - 49
common/src/github.com/yuin/gopher-lua/state.go

@@ -6,16 +6,15 @@ package lua
 
 import (
 	"fmt"
+	"github.com/yuin/gopher-lua/parse"
+	"golang.org/x/net/context"
 	"io"
-	"log"
 	"math"
 	"os"
 	"runtime"
 	"strings"
 	"sync/atomic"
 	"time"
-
-	"github.com/yuin/gopher-lua/parse"
 )
 
 const MultRet = -1
@@ -337,6 +336,8 @@ func newLState(options Options) *LState {
 		wrapped:      false,
 		uvcache:      nil,
 		hasErrorFunc: false,
+		mainLoop:     mainLoop,
+		ctx:          nil,
 	}
 	ls.Env = ls.G.Global
 	return ls
@@ -442,35 +443,32 @@ func (ls *LState) where(level int, skipg bool) string {
 }
 
 func (ls *LState) stackTrace(level int) string {
-	return ls.ScriptFileName + "出错!"
-	/*
-		buf := []string{}
-		header := "stack traceback:"
-		if ls.currentFrame != nil {
-			i := 0
-			for dbg, ok := ls.GetStack(i); ok; dbg, ok = ls.GetStack(i) {
-				cf := dbg.frame
-				buf = append(buf, fmt.Sprintf("\t%v in %v", ls.Where(i), ls.formattedFrameFuncName(cf)))
-				if !cf.Fn.IsG && cf.TailCall > 0 {
-					for tc := cf.TailCall; tc > 0; tc-- {
-						buf = append(buf, "\t(tailcall): ?")
-						i++
-					}
+	buf := []string{}
+	header := "stack traceback:"
+	if ls.currentFrame != nil {
+		i := 0
+		for dbg, ok := ls.GetStack(i); ok; dbg, ok = ls.GetStack(i) {
+			cf := dbg.frame
+			buf = append(buf, fmt.Sprintf("\t%v in %v", ls.Where(i), ls.formattedFrameFuncName(cf)))
+			if !cf.Fn.IsG && cf.TailCall > 0 {
+				for tc := cf.TailCall; tc > 0; tc-- {
+					buf = append(buf, "\t(tailcall): ?")
+					i++
 				}
-				i++
 			}
+			i++
 		}
-		buf = append(buf, fmt.Sprintf("\t%v: %v", "[G]", "?"))
-		buf = buf[intMax(0, intMin(level, len(buf))):len(buf)]
-		if len(buf) > 20 {
-			newbuf := make([]string, 0, 20)
-			newbuf = append(newbuf, buf[0:7]...)
-			newbuf = append(newbuf, "\t...")
-			newbuf = append(newbuf, buf[len(buf)-7:len(buf)]...)
-			buf = newbuf
-		}
-		return fmt.Sprintf("%s\n%s", header, strings.Join(buf, "\n"))
-	*/
+	}
+	buf = append(buf, fmt.Sprintf("\t%v: %v", "[G]", "?"))
+	buf = buf[intMax(0, intMin(level, len(buf))):len(buf)]
+	if len(buf) > 20 {
+		newbuf := make([]string, 0, 20)
+		newbuf = append(newbuf, buf[0:7]...)
+		newbuf = append(newbuf, "\t...")
+		newbuf = append(newbuf, buf[len(buf)-7:len(buf)]...)
+		buf = newbuf
+	}
+	return fmt.Sprintf("%s\n%s", header, strings.Join(buf, "\n"))
 }
 
 func (ls *LState) formattedFrameFuncName(fr *callFrame) string {
@@ -873,9 +871,9 @@ func (ls *LState) callR(nargs, nret, rbase int) {
 	if ls.G.MainThread == nil {
 		ls.G.MainThread = ls
 		ls.G.CurrentThread = ls
-		mainLoop(ls, nil)
+		ls.mainLoop(ls, nil)
 	} else {
-		mainLoop(ls, ls.currentFrame)
+		ls.mainLoop(ls, ls.currentFrame)
 	}
 	if nret != MultRet {
 		ls.reg.SetTop(rbase + nret)
@@ -1152,19 +1150,8 @@ func (ls *LState) Pop(n int) {
 	for i := 0; i < n; i++ {
 		if ls.GetTop() == 0 {
 			ls.RaiseError("register underflow")
-		} else {
-			ls.reg.Pop()
-		}
-	}
-}
-
-func (ls *LState) PopArgs(n int, info interface{}) {
-	for i := 0; i < n; i++ {
-		if ls.GetTop() == 0 {
-			ls.RaiseError("register underflow", info)
-		} else {
-			ls.reg.Pop()
 		}
+		ls.reg.Pop()
 	}
 }
 
@@ -1208,19 +1195,25 @@ func (ls *LState) Remove(index int) {
 /* object allocation {{{ */
 
 func (ls *LState) NewTable() *LTable {
-	// TODO change size
-	return newLTable(32, 32)
+	return newLTable(defaultArrayCap, defaultHashCap)
 }
 
 func (ls *LState) CreateTable(acap, hcap int) *LTable {
 	return newLTable(acap, hcap)
 }
 
-func (ls *LState) NewThread() *LState {
+// NewThread returns a new LState that shares with the original state all global objects.
+// If the original state has context.Context, the new state has a new child context of the original state and this function returns its cancel function.
+func (ls *LState) NewThread() (*LState, context.CancelFunc) {
 	thread := newLState(ls.Options)
 	thread.G = ls.G
 	thread.Env = ls.Env
-	return thread
+	var f context.CancelFunc = nil
+	if ls.ctx != nil {
+		thread.mainLoop = mainLoopWithContext
+		thread.ctx, f = context.WithCancel(ls.ctx)
+	}
+	return thread, f
 }
 
 func (ls *LState) NewUserData() *LUserData {
@@ -1318,8 +1311,7 @@ func (ls *LState) ToThread(n int) *LState {
 
 // This function is equivalent to luaL_error( http://www.lua.org/manual/5.1/manual.html#luaL_error ).
 func (ls *LState) RaiseError(format string, args ...interface{}) {
-	log.Println("Pop 出错,脚本名称", ls.ScriptFileName, args)
-	//ls.raiseError(1, format, args...)
+	ls.raiseError(1, format, args...)
 }
 
 // This function is equivalent to lua_error( http://www.lua.org/manual/5.1/manual.html#lua_error ).
@@ -1668,6 +1660,8 @@ func (ls *LState) PCall(nargs, nret int, errfunc *LFunction) (err error) {
 			} else if len(err.(*ApiError).StackTrace) == 0 {
 				err.(*ApiError).StackTrace = ls.stackTrace(0)
 			}
+			ls.stack.SetSp(sp)
+			ls.currentFrame = ls.stack.Last()
 			ls.reg.SetTop(base)
 		}
 		ls.stack.SetSp(sp)
@@ -1844,6 +1838,25 @@ func (ls *LState) SetMx(mx int) {
 	}()
 }
 
+// SetContext set a context ctx to this LState. The provided ctx must be non-nil.
+func (ls *LState) SetContext(ctx context.Context) {
+	ls.mainLoop = mainLoopWithContext
+	ls.ctx = ctx
+}
+
+// Context returns the LState's context. To change the context, use WithContext.
+func (ls *LState) Context() context.Context {
+	return ls.ctx
+}
+
+// RemoveContext removes the context associated with this LState and returns this context.
+func (ls *LState) RemoveContext() context.Context {
+	oldctx := ls.ctx
+	ls.mainLoop = mainLoop
+	ls.ctx = nil
+	return oldctx
+}
+
 // Converts the Lua value at the given acceptable index to the chan LValue.
 func (ls *LState) ToChannel(n int) chan LValue {
 	if lv, ok := ls.Get(n).(LChannel); ok {

+ 100 - 2
common/src/github.com/yuin/gopher-lua/state_test.go

@@ -1,8 +1,10 @@
 package lua
 
 import (
+	"golang.org/x/net/context"
 	"strings"
 	"testing"
+	"time"
 )
 
 func TestCallStackOverflow(t *testing.T) {
@@ -265,7 +267,7 @@ func TestPCall(t *testing.T) {
 func TestCoroutineApi1(t *testing.T) {
 	L := NewState()
 	defer L.Close()
-	co := L.NewThread()
+	co, _ := L.NewThread()
 	errorIfScriptFail(t, L, `
       function coro(v)
         assert(v == 10)
@@ -308,7 +310,7 @@ func TestCoroutineApi1(t *testing.T) {
       end
     `)
 	fn = L.GetGlobal("coro_error").(*LFunction)
-	co = L.NewThread()
+	co, _ = L.NewThread()
 	st, err, values = L.Resume(co, fn)
 	errorIfNotEqual(t, ResumeYield, st)
 	errorIfNotNil(t, err)
@@ -333,3 +335,99 @@ func TestCoroutineApi1(t *testing.T) {
 	errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread")
 
 }
+
+func TestContextTimeout(t *testing.T) {
+	L := NewState()
+	defer L.Close()
+	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+	defer cancel()
+	L.SetContext(ctx)
+	errorIfNotEqual(t, ctx, L.Context())
+	err := L.DoString(`
+	  local clock = os.clock
+      function sleep(n)  -- seconds
+        local t0 = clock()
+        while clock() - t0 <= n do end
+      end
+	  sleep(3)
+	`)
+	errorIfNil(t, err)
+	errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled")
+
+	oldctx := L.RemoveContext()
+	errorIfNotEqual(t, ctx, oldctx)
+	errorIfNotNil(t, L.ctx)
+}
+
+func TestContextCancel(t *testing.T) {
+	L := NewState()
+	defer L.Close()
+	ctx, cancel := context.WithCancel(context.Background())
+	errch := make(chan error, 1)
+	L.SetContext(ctx)
+	go func() {
+		errch <- L.DoString(`
+	    local clock = os.clock
+        function sleep(n)  -- seconds
+          local t0 = clock()
+          while clock() - t0 <= n do end
+        end
+	    sleep(3)
+	  `)
+	}()
+	time.Sleep(1 * time.Second)
+	cancel()
+	err := <-errch
+	errorIfNil(t, err)
+	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled")
+}
+
+func TestContextWithCroutine(t *testing.T) {
+	L := NewState()
+	defer L.Close()
+	ctx, cancel := context.WithCancel(context.Background())
+	L.SetContext(ctx)
+	defer cancel()
+	L.DoString(`
+	    function coro()
+		  local i = 0
+		  while true do
+		    coroutine.yield(i)
+			i = i+1
+		  end
+		  return i
+	    end
+	`)
+	co, cocancel := L.NewThread()
+	defer cocancel()
+	fn := L.GetGlobal("coro").(*LFunction)
+	_, err, values := L.Resume(co, fn)
+	errorIfNotNil(t, err)
+	errorIfNotEqual(t, LNumber(0), values[0])
+	// cancel the parent context
+	cancel()
+	_, err, values = L.Resume(co, fn)
+	errorIfNil(t, err)
+	errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled")
+
+}
+
+func TestPCallAfterFail(t *testing.T) {
+	L := NewState()
+	defer L.Close()
+	errFn := L.NewFunction(func(L *LState) int {
+		L.RaiseError("error!")
+		return 0
+	})
+	changeError := L.NewFunction(func(L *LState) int {
+		L.Push(errFn)
+		err := L.PCall(0, 0, nil)
+		if err != nil {
+			L.RaiseError("A New Error")
+		}
+		return 0
+	})
+	L.Push(changeError)
+	err := L.PCall(0, 0, nil)
+	errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly")
+}

+ 3 - 4
common/src/github.com/yuin/gopher-lua/stringlib.go

@@ -3,7 +3,6 @@ package lua
 import (
 	"fmt"
 	"strings"
-	"unsafe"
 
 	"github.com/yuin/gopher-lua/pm"
 )
@@ -111,7 +110,7 @@ func strFind(L *LState) int {
 		return 2
 	}
 
-	mds, err := pm.Find(pattern, *(*[]byte)(unsafe.Pointer(&str)), init, 1)
+	mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), init, 1)
 	if err != nil {
 		L.RaiseError(err.Error())
 	}
@@ -151,7 +150,7 @@ func strGsub(L *LState) int {
 	repl := L.CheckAny(3)
 	limit := L.OptInt(4, -1)
 
-	mds, err := pm.Find(pat, *(*[]byte)(unsafe.Pointer(&str)), 0, limit)
+	mds, err := pm.Find(pat, unsafeFastStringToReadOnlyBytes(str), 0, limit)
 	if err != nil {
 		L.RaiseError(err.Error())
 	}
@@ -362,7 +361,7 @@ func strMatch(L *LState) int {
 		offset = 0
 	}
 
-	mds, err := pm.Find(pattern, *(*[]byte)(unsafe.Pointer(&str)), offset, 1)
+	mds, err := pm.Find(pattern, unsafeFastStringToReadOnlyBytes(str), offset, 1)
 	if err != nil {
 		L.RaiseError(err.Error())
 	}

+ 21 - 28
common/src/github.com/yuin/gopher-lua/table.go

@@ -316,10 +316,12 @@ func (tb *LTable) ForEach(cb func(LValue, LValue)) {
 // This function is equivalent to lua_next ( http://www.lua.org/manual/5.1/manual.html#lua_next ).
 func (tb *LTable) Next(key LValue) (LValue, LValue) {
 	// TODO: inefficient way
+	init := false
 	if key == LNil {
 		tb.keys = nil
 		tb.k2i = nil
 		key = LNumber(0)
+		init = true
 	}
 
 	length := 0
@@ -350,43 +352,34 @@ func (tb *LTable) Next(key LValue) (LValue, LValue) {
 		}
 	}
 
-	if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 {
-		index := int(kv)
-		if tb.array != nil {
-			for ; index < len(tb.array); index++ {
-				if v := tb.array[index]; v != LNil {
-					return LNumber(index + 1), v
+	if init || key != LNumber(0) {
+		if kv, ok := key.(LNumber); ok && isInteger(kv) && int(kv) >= 0 {
+			index := int(kv)
+			if tb.array != nil {
+				for ; index < len(tb.array); index++ {
+					if v := tb.array[index]; v != LNil {
+						return LNumber(index + 1), v
+					}
 				}
 			}
-		}
-		if tb.array == nil || index == len(tb.array) {
-			if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) {
-				tb.keys = nil
-				tb.k2i = nil
-				return LNil, LNil
-			}
-			key = tb.keys[0]
-			if skey, sok := key.(LString); sok && tb.strdict != nil {
-				if sv, svok := tb.strdict[string(skey)]; svok && sv != LNil {
-					return key, sv
+			if tb.array == nil || index == len(tb.array) {
+				if (tb.dict == nil || len(tb.dict) == 0) && (tb.strdict == nil || len(tb.strdict) == 0) {
+					tb.keys = nil
+					tb.k2i = nil
+					return LNil, LNil
 				}
-			} else if tb.dict != nil {
-				if v, vok := tb.dict[key]; vok && v != LNil {
+				key = tb.keys[0]
+				if v := tb.RawGetH(key); v != LNil {
 					return key, v
 				}
 			}
 		}
 	}
+
 	for i := tb.k2i[key] + 1; i < length; i++ {
-		key = tb.keys[i]
-		if skey, sok := key.(LString); sok && tb.strdict != nil {
-			if sv, svok := tb.strdict[string(skey)]; svok && sv != LNil {
-				return key, sv
-			}
-		} else if tb.dict != nil {
-			if v, vok := tb.dict[key]; vok && v != LNil {
-				return key, v
-			}
+		key = tb.keys[tb.k2i[key]+1]
+		if v := tb.RawGetH(key); v != LNil {
+			return key, v
 		}
 	}
 	tb.keys = nil

+ 1 - 6
common/src/github.com/yuin/gopher-lua/tablelib.go

@@ -30,12 +30,7 @@ func tableSort(L *LState) int {
 }
 
 func tableGetN(L *LState) int {
-	if v := L.CheckTable(1); v != nil {
-		L.Push(LNumber(v.Len()))
-	} else {
-		L.Push(LNumber(0))
-	}
-	//L.Push(LNumber(L.CheckTable(1).Len()))
+	L.Push(LNumber(L.CheckTable(1).Len()))
 	return 1
 }
 

+ 9 - 1
common/src/github.com/yuin/gopher-lua/utils.go

@@ -4,9 +4,11 @@ import (
 	"bufio"
 	"fmt"
 	"io"
+	"reflect"
 	"strconv"
 	"strings"
 	"time"
+	"unsafe"
 )
 
 func intMin(a, b int) int {
@@ -100,7 +102,7 @@ func (fs *flagScanner) Next() (byte, bool) {
 var cDateFlagToGo = map[byte]string{
 	'a': "mon", 'A': "Monday", 'b': "Jan", 'B': "January", 'c': "02 Jan 06 15:04 MST", 'd': "02",
 	'F': "2006-01-02", 'H': "15", 'I': "03", 'm': "01", 'M': "04", 'p': "PM", 'P': "pm", 'S': "05",
-	'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"}
+	'x': "15/04/05", 'X': "15:04:05", 'y': "06", 'Y': "2006", 'z': "-0700", 'Z': "MST"}
 
 func strftime(t time.Time, cfmt string) string {
 	sc := newFlagScanner('%', "", "", cfmt)
@@ -252,3 +254,9 @@ func strCmp(s1, s2 string) int {
 		}
 	}
 }
+
+func unsafeFastStringToReadOnlyBytes(s string) []byte {
+	sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
+	bh := reflect.SliceHeader{sh.Data, sh.Len, sh.Len}
+	return *(*[]byte)(unsafe.Pointer(&bh))
+}

+ 11 - 9
common/src/github.com/yuin/gopher-lua/value.go

@@ -2,6 +2,7 @@ package lua
 
 import (
 	"fmt"
+	"golang.org/x/net/context"
 	"os"
 )
 
@@ -207,15 +208,16 @@ type LState struct {
 	Dead    bool
 	Options Options
 
-	stop           int32
-	reg            *registry
-	stack          *callFrameStack
-	alloc          *allocator
-	currentFrame   *callFrame
-	wrapped        bool
-	uvcache        *Upvalue
-	hasErrorFunc   bool
-	ScriptFileName string
+	stop         int32
+	reg          *registry
+	stack        *callFrameStack
+	alloc        *allocator
+	currentFrame *callFrame
+	wrapped      bool
+	uvcache      *Upvalue
+	hasErrorFunc bool
+	mainLoop     func(*LState, *callFrame)
+	ctx          context.Context
 }
 
 func (ls *LState) String() string                     { return fmt.Sprintf("thread: %p", ls) }

+ 41 - 5
common/src/github.com/yuin/gopher-lua/vm.go

@@ -34,6 +34,36 @@ func mainLoop(L *LState, baseframe *callFrame) {
 	}
 }
 
+func mainLoopWithContext(L *LState, baseframe *callFrame) {
+	var inst uint32
+	var cf *callFrame
+
+	if L.stack.IsEmpty() {
+		return
+	}
+
+	L.currentFrame = L.stack.Last()
+	if L.currentFrame.Fn.IsG {
+		callGFunction(L, false)
+		return
+	}
+
+	for {
+		cf = L.currentFrame
+		inst = cf.Fn.Proto.Code[cf.Pc]
+		cf.Pc++
+		select {
+		case <-L.ctx.Done():
+			L.RaiseError(L.ctx.Err().Error())
+			return
+		default:
+			if jumpTable[int(inst>>26)](L, inst, baseframe) == 1 {
+				return
+			}
+		}
+	}
+}
+
 func copyReturnValues(L *LState, regv, start, n, b int) { // +inline-start
 	if b == 1 {
 		// this section is inlined by go-inline
@@ -160,7 +190,7 @@ func threadRun(L *LState) {
 			}
 		}
 	}()
-	mainLoop(L, nil)
+	L.mainLoop(L, nil)
 }
 
 type instFunc func(*LState, uint32, *callFrame) int
@@ -691,6 +721,9 @@ func init() {
 			} else {
 				callable, meta = L.metaCall(lv)
 			}
+			if callable == nil {
+				L.RaiseError("attempt to call a non-function object")
+			}
 			// this section is inlined by go-inline
 			// source function is 'func (ls *LState) closeUpvalues(idx int) ' in '_state.go'
 			{
@@ -1012,14 +1045,17 @@ func init() {
 			RA := lbase + A
 			C := int(inst>>9) & 0x1ff //GETC
 			nret := C
-			reg.SetTop(RA + 3)
+			reg.SetTop(RA + 3 + 2)
+			reg.Set(RA+3+2, reg.Get(RA+2))
+			reg.Set(RA+3+1, reg.Get(RA+1))
+			reg.Set(RA+3, reg.Get(RA))
 			L.callR(2, nret, RA+3)
-			reg.SetTop(RA + 2 + C + 1)
 			if value := reg.Get(RA + 3); value != LNil {
 				reg.Set(RA+2, value)
-			} else {
-				cf.Pc++
+				pc := cf.Fn.Proto.Code[cf.Pc]
+				cf.Pc += int(pc&0x3ffff) - opMaxArgSbx
 			}
+			cf.Pc++
 			return 0
 		},
 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETLIST

+ 7 - 0
common/src/qfw/util/rpc/push.go

@@ -13,3 +13,10 @@ type FollowPush struct {
 	OpenId      string
 	Flag        int
 }
+type FollowPushEnt struct {
+	Entname  string
+	InfoId   string
+	FollowId string
+	OpenId   string
+	Flag     int
+}