1727 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1727 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			Go
		
	
	
	
| package lua
 | |
| 
 | |
| ////////////////////////////////////////////////////////
 | |
| // This file was generated by go-inline. DO NOT EDIT. //
 | |
| ////////////////////////////////////////////////////////
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| func mainLoop(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++
 | |
| 		if jumpTable[int(inst>>26)](L, inst, baseframe) == 1 {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // regv is the first target register to copy the return values to.
 | |
| // It can be reg.top, indicating that the copied values are going into new registers, or it can be below reg.top
 | |
| // Indicating that the values should be within the existing registers.
 | |
| // b is the available number of return values + 1.
 | |
| // n is the desired number of return values.
 | |
| // If n more than the available return values then the extra values are set to nil.
 | |
| // When this function returns the top of the registry will be set to regv+n.
 | |
| func copyReturnValues(L *LState, regv, start, n, b int) { // +inline-start
 | |
| 	if b == 1 {
 | |
| 		// this section is inlined by go-inline
 | |
| 		// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 		{
 | |
| 			rg := L.reg
 | |
| 			regm := regv
 | |
| 			newSize := regm + n
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 			{
 | |
| 				requiredSize := newSize
 | |
| 				if requiredSize > cap(rg.array) {
 | |
| 					rg.resize(requiredSize)
 | |
| 				}
 | |
| 			}
 | |
| 			for i := 0; i < n; i++ {
 | |
| 				rg.array[regm+i] = LNil
 | |
| 			}
 | |
| 			// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 			// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 			oldtop := rg.top
 | |
| 			rg.top = regm + n
 | |
| 			if rg.top < oldtop {
 | |
| 				nilRange := rg.array[rg.top:oldtop]
 | |
| 				for i := range nilRange {
 | |
| 					nilRange[i] = nil
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		// this section is inlined by go-inline
 | |
| 		// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 		{
 | |
| 			rg := L.reg
 | |
| 			limit := -1
 | |
| 			newSize := regv + n
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 			{
 | |
| 				requiredSize := newSize
 | |
| 				if requiredSize > cap(rg.array) {
 | |
| 					rg.resize(requiredSize)
 | |
| 				}
 | |
| 			}
 | |
| 			if limit == -1 || limit > rg.top {
 | |
| 				limit = rg.top
 | |
| 			}
 | |
| 			for i := 0; i < n; i++ {
 | |
| 				srcIdx := start + i
 | |
| 				if srcIdx >= limit || srcIdx < 0 {
 | |
| 					rg.array[regv+i] = LNil
 | |
| 				} else {
 | |
| 					rg.array[regv+i] = rg.array[srcIdx]
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 			// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 			oldtop := rg.top
 | |
| 			rg.top = regv + n
 | |
| 			if rg.top < oldtop {
 | |
| 				nilRange := rg.array[rg.top:oldtop]
 | |
| 				for i := range nilRange {
 | |
| 					nilRange[i] = nil
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if b > 1 && n > (b-1) {
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 			{
 | |
| 				rg := L.reg
 | |
| 				regm := regv + b - 1
 | |
| 				n := n - (b - 1)
 | |
| 				newSize := regm + n
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 				{
 | |
| 					requiredSize := newSize
 | |
| 					if requiredSize > cap(rg.array) {
 | |
| 						rg.resize(requiredSize)
 | |
| 					}
 | |
| 				}
 | |
| 				for i := 0; i < n; i++ {
 | |
| 					rg.array[regm+i] = LNil
 | |
| 				}
 | |
| 				// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 				// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 				oldtop := rg.top
 | |
| 				rg.top = regm + n
 | |
| 				if rg.top < oldtop {
 | |
| 					nilRange := rg.array[rg.top:oldtop]
 | |
| 					for i := range nilRange {
 | |
| 						nilRange[i] = nil
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| } // +inline-end
 | |
| 
 | |
| func switchToParentThread(L *LState, nargs int, haserror bool, kill bool) {
 | |
| 	parent := L.Parent
 | |
| 	if parent == nil {
 | |
| 		L.RaiseError("can not yield from outside of a coroutine")
 | |
| 	}
 | |
| 	L.G.CurrentThread = parent
 | |
| 	L.Parent = nil
 | |
| 	if !L.wrapped {
 | |
| 		if haserror {
 | |
| 			parent.Push(LFalse)
 | |
| 		} else {
 | |
| 			parent.Push(LTrue)
 | |
| 		}
 | |
| 	}
 | |
| 	L.XMoveTo(parent, nargs)
 | |
| 	L.stack.Pop()
 | |
| 	offset := L.currentFrame.LocalBase - L.currentFrame.ReturnBase
 | |
| 	L.currentFrame = L.stack.Last()
 | |
| 	L.reg.SetTop(L.reg.Top() - offset) // remove 'yield' function(including tailcalled functions)
 | |
| 	if kill {
 | |
| 		L.kill()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func callGFunction(L *LState, tailcall bool) bool {
 | |
| 	frame := L.currentFrame
 | |
| 	gfnret := frame.Fn.GFunction(L)
 | |
| 	if tailcall {
 | |
| 		L.currentFrame = L.RemoveCallerFrame()
 | |
| 	}
 | |
| 
 | |
| 	if gfnret < 0 {
 | |
| 		switchToParentThread(L, L.GetTop(), false, false)
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	wantret := frame.NRet
 | |
| 	if wantret == MultRet {
 | |
| 		wantret = gfnret
 | |
| 	}
 | |
| 
 | |
| 	if tailcall && L.Parent != nil && L.stack.Sp() == 1 {
 | |
| 		switchToParentThread(L, wantret, false, true)
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	// this section is inlined by go-inline
 | |
| 	// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 	{
 | |
| 		rg := L.reg
 | |
| 		regv := frame.ReturnBase
 | |
| 		start := L.reg.Top() - gfnret
 | |
| 		limit := -1
 | |
| 		n := wantret
 | |
| 		newSize := regv + n
 | |
| 		// this section is inlined by go-inline
 | |
| 		// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 		{
 | |
| 			requiredSize := newSize
 | |
| 			if requiredSize > cap(rg.array) {
 | |
| 				rg.resize(requiredSize)
 | |
| 			}
 | |
| 		}
 | |
| 		if limit == -1 || limit > rg.top {
 | |
| 			limit = rg.top
 | |
| 		}
 | |
| 		for i := 0; i < n; i++ {
 | |
| 			srcIdx := start + i
 | |
| 			if srcIdx >= limit || srcIdx < 0 {
 | |
| 				rg.array[regv+i] = LNil
 | |
| 			} else {
 | |
| 				rg.array[regv+i] = rg.array[srcIdx]
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 		// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 		oldtop := rg.top
 | |
| 		rg.top = regv + n
 | |
| 		if rg.top < oldtop {
 | |
| 			nilRange := rg.array[rg.top:oldtop]
 | |
| 			for i := range nilRange {
 | |
| 				nilRange[i] = nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	L.stack.Pop()
 | |
| 	L.currentFrame = L.stack.Last()
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func threadRun(L *LState) {
 | |
| 	if L.stack.IsEmpty() {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	defer func() {
 | |
| 		if rcv := recover(); rcv != nil {
 | |
| 			var lv LValue
 | |
| 			if v, ok := rcv.(*ApiError); ok {
 | |
| 				lv = v.Object
 | |
| 			} else {
 | |
| 				lv = LString(fmt.Sprint(rcv))
 | |
| 			}
 | |
| 			if parent := L.Parent; parent != nil {
 | |
| 				if L.wrapped {
 | |
| 					L.Push(lv)
 | |
| 					parent.Panic(L)
 | |
| 				} else {
 | |
| 					L.SetTop(0)
 | |
| 					L.Push(lv)
 | |
| 					switchToParentThread(L, 1, true, true)
 | |
| 				}
 | |
| 			} else {
 | |
| 				panic(rcv)
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| 	L.mainLoop(L, nil)
 | |
| }
 | |
| 
 | |
| type instFunc func(*LState, uint32, *callFrame) int
 | |
| 
 | |
| var jumpTable [opCodeMax + 1]instFunc
 | |
| 
 | |
| func init() {
 | |
| 	jumpTable = [opCodeMax + 1]instFunc{
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVE
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			reg.Set(RA, reg.Get(lbase+B))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_MOVEN
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			reg.Set(lbase+A, reg.Get(lbase+B))
 | |
| 			code := cf.Fn.Proto.Code
 | |
| 			pc := cf.Pc
 | |
| 			for i := 0; i < C; i++ {
 | |
| 				inst = code[pc]
 | |
| 				pc++
 | |
| 				A = int(inst>>18) & 0xff //GETA
 | |
| 				B = int(inst & 0x1ff)    //GETB
 | |
| 				reg.Set(lbase+A, reg.Get(lbase+B))
 | |
| 			}
 | |
| 			cf.Pc = pc
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADK
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			Bx := int(inst & 0x3ffff) //GETBX
 | |
| 			reg.Set(RA, cf.Fn.Proto.Constants[Bx])
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADBOOL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			if B != 0 {
 | |
| 				reg.Set(RA, LTrue)
 | |
| 			} else {
 | |
| 				reg.Set(RA, LFalse)
 | |
| 			}
 | |
| 			if C != 0 {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LOADNIL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			for i := RA; i <= lbase+B; i++ {
 | |
| 				reg.Set(i, LNil)
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETUPVAL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			reg.Set(RA, cf.Fn.Upvalues[B].Value())
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETGLOBAL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			Bx := int(inst & 0x3ffff) //GETBX
 | |
| 			//reg.Set(RA, L.getField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx]))
 | |
| 			reg.Set(RA, L.getFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx]))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLE
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			reg.Set(RA, L.getField(reg.Get(lbase+B), L.rkValue(C)))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_GETTABLEKS
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			reg.Set(RA, L.getFieldString(reg.Get(lbase+B), L.rkString(C)))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETGLOBAL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			Bx := int(inst & 0x3ffff) //GETBX
 | |
| 			//L.setField(cf.Fn.Env, cf.Fn.Proto.Constants[Bx], reg.Get(RA))
 | |
| 			L.setFieldString(cf.Fn.Env, cf.Fn.Proto.stringConstants[Bx], reg.Get(RA))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETUPVAL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			cf.Fn.Upvalues[B].SetValue(reg.Get(RA))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLE
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			L.setField(reg.Get(RA), L.rkValue(B), L.rkValue(C))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SETTABLEKS
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			L.setFieldString(reg.Get(RA), L.rkString(B), L.rkValue(C))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NEWTABLE
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			reg.Set(RA, newLTable(B, C))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_SELF
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			selfobj := reg.Get(lbase + B)
 | |
| 			reg.Set(RA, L.getFieldString(selfobj, L.rkString(C)))
 | |
| 			reg.Set(RA+1, selfobj)
 | |
| 			return 0
 | |
| 		},
 | |
| 		opArith, // OP_ADD
 | |
| 		opArith, // OP_SUB
 | |
| 		opArith, // OP_MUL
 | |
| 		opArith, // OP_DIV
 | |
| 		opArith, // OP_MOD
 | |
| 		opArith, // OP_POW
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_UNM
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			unaryv := L.rkValue(B)
 | |
| 			if nm, ok := unaryv.(LNumber); ok {
 | |
| 				reg.SetNumber(RA, -nm)
 | |
| 			} else {
 | |
| 				op := L.metaOp1(unaryv, "__unm")
 | |
| 				if op.Type() == LTFunction {
 | |
| 					reg.Push(op)
 | |
| 					reg.Push(unaryv)
 | |
| 					L.Call(1, 1)
 | |
| 					reg.Set(RA, reg.Pop())
 | |
| 				} else if str, ok1 := unaryv.(LString); ok1 {
 | |
| 					if num, err := parseNumber(string(str)); err == nil {
 | |
| 						reg.Set(RA, -num)
 | |
| 					} else {
 | |
| 						L.RaiseError("__unm undefined")
 | |
| 					}
 | |
| 				} else {
 | |
| 					L.RaiseError("__unm undefined")
 | |
| 				}
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOT
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			if LVIsFalse(reg.Get(lbase + B)) {
 | |
| 				reg.Set(RA, LTrue)
 | |
| 			} else {
 | |
| 				reg.Set(RA, LFalse)
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LEN
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			switch lv := L.rkValue(B).(type) {
 | |
| 			case LString:
 | |
| 				reg.SetNumber(RA, LNumber(len(lv)))
 | |
| 			default:
 | |
| 				op := L.metaOp1(lv, "__len")
 | |
| 				if op.Type() == LTFunction {
 | |
| 					reg.Push(op)
 | |
| 					reg.Push(lv)
 | |
| 					L.Call(1, 1)
 | |
| 					ret := reg.Pop()
 | |
| 					if ret.Type() == LTNumber {
 | |
| 						reg.SetNumber(RA, ret.(LNumber))
 | |
| 					} else {
 | |
| 						reg.Set(RA, ret)
 | |
| 					}
 | |
| 				} else if lv.Type() == LTTable {
 | |
| 					reg.SetNumber(RA, LNumber(lv.(*LTable).Len()))
 | |
| 				} else {
 | |
| 					L.RaiseError("__len undefined")
 | |
| 				}
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CONCAT
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			RC := lbase + C
 | |
| 			RB := lbase + B
 | |
| 			reg.Set(RA, stringConcat(L, RC-RB+1, RC))
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_JMP
 | |
| 			cf := L.currentFrame
 | |
| 			Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX
 | |
| 			cf.Pc += Sbx
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_EQ
 | |
| 			cf := L.currentFrame
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			ret := equals(L, L.rkValue(B), L.rkValue(C), false)
 | |
| 			v := 1
 | |
| 			if ret {
 | |
| 				v = 0
 | |
| 			}
 | |
| 			if v == A {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LT
 | |
| 			cf := L.currentFrame
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			ret := lessThan(L, L.rkValue(B), L.rkValue(C))
 | |
| 			v := 1
 | |
| 			if ret {
 | |
| 				v = 0
 | |
| 			}
 | |
| 			if v == A {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_LE
 | |
| 			cf := L.currentFrame
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			lhs := L.rkValue(B)
 | |
| 			rhs := L.rkValue(C)
 | |
| 			ret := false
 | |
| 
 | |
| 			if v1, ok1 := lhs.assertFloat64(); ok1 {
 | |
| 				if v2, ok2 := rhs.assertFloat64(); ok2 {
 | |
| 					ret = v1 <= v2
 | |
| 				} else {
 | |
| 					L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String())
 | |
| 				}
 | |
| 			} else {
 | |
| 				if lhs.Type() != rhs.Type() {
 | |
| 					L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String())
 | |
| 				}
 | |
| 				switch lhs.Type() {
 | |
| 				case LTString:
 | |
| 					ret = strCmp(string(lhs.(LString)), string(rhs.(LString))) <= 0
 | |
| 				default:
 | |
| 					switch objectRational(L, lhs, rhs, "__le") {
 | |
| 					case 1:
 | |
| 						ret = true
 | |
| 					case 0:
 | |
| 						ret = false
 | |
| 					default:
 | |
| 						ret = !objectRationalWithError(L, rhs, lhs, "__lt")
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			v := 1
 | |
| 			if ret {
 | |
| 				v = 0
 | |
| 			}
 | |
| 			if v == A {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TEST
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			if LVAsBool(reg.Get(RA)) == (C == 0) {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TESTSET
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			if value := reg.Get(lbase + B); LVAsBool(value) != (C == 0) {
 | |
| 				reg.Set(RA, value)
 | |
| 			} else {
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CALL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			nargs := B - 1
 | |
| 			if B == 0 {
 | |
| 				nargs = reg.Top() - (RA + 1)
 | |
| 			}
 | |
| 			lv := reg.Get(RA)
 | |
| 			nret := C - 1
 | |
| 			var callable *LFunction
 | |
| 			var meta bool
 | |
| 			if fn, ok := lv.assertFunction(); ok {
 | |
| 				callable = fn
 | |
| 				meta = false
 | |
| 			} else {
 | |
| 				callable, meta = L.metaCall(lv)
 | |
| 			}
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (ls *LState) pushCallFrame(cf callFrame, fn LValue, meta bool) ' in '_state.go'
 | |
| 			{
 | |
| 				ls := L
 | |
| 				cf := callFrame{Fn: callable, Pc: 0, Base: RA, LocalBase: RA + 1, ReturnBase: RA, NArgs: nargs, NRet: nret, Parent: cf, TailCall: 0}
 | |
| 				fn := lv
 | |
| 				if meta {
 | |
| 					cf.NArgs++
 | |
| 					ls.reg.Insert(fn, cf.LocalBase)
 | |
| 				}
 | |
| 				if cf.Fn == nil {
 | |
| 					ls.RaiseError("attempt to call a non-function object")
 | |
| 				}
 | |
| 				if ls.stack.IsFull() {
 | |
| 					ls.RaiseError("stack overflow")
 | |
| 				}
 | |
| 				ls.stack.Push(cf)
 | |
| 				newcf := ls.stack.Last()
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func (ls *LState) initCallFrame(cf *callFrame) ' in '_state.go'
 | |
| 				{
 | |
| 					cf := newcf
 | |
| 					if cf.Fn.IsG {
 | |
| 						ls.reg.SetTop(cf.LocalBase + cf.NArgs)
 | |
| 					} else {
 | |
| 						proto := cf.Fn.Proto
 | |
| 						nargs := cf.NArgs
 | |
| 						np := int(proto.NumParameters)
 | |
| 						if nargs < np {
 | |
| 							// default any missing arguments to nil
 | |
| 							newSize := cf.LocalBase + np
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								rg := ls.reg
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := nargs; i < np; i++ {
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 							nargs = np
 | |
| 							ls.reg.top = newSize
 | |
| 						}
 | |
| 
 | |
| 						if (proto.IsVarArg & VarArgIsVarArg) == 0 {
 | |
| 							if nargs < int(proto.NumUsedRegisters) {
 | |
| 								nargs = int(proto.NumUsedRegisters)
 | |
| 							}
 | |
| 							newSize := cf.LocalBase + nargs
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								rg := ls.reg
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := np; i < nargs; i++ {
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 							ls.reg.top = cf.LocalBase + int(proto.NumUsedRegisters)
 | |
| 						} else {
 | |
| 							/* swap vararg positions:
 | |
| 									   closure
 | |
| 									   namedparam1 <- lbase
 | |
| 									   namedparam2
 | |
| 									   vararg1
 | |
| 									   vararg2
 | |
| 
 | |
| 							           TO
 | |
| 
 | |
| 									   closure
 | |
| 									   nil
 | |
| 									   nil
 | |
| 									   vararg1
 | |
| 									   vararg2
 | |
| 									   namedparam1 <- lbase
 | |
| 									   namedparam2
 | |
| 							*/
 | |
| 							nvarargs := nargs - np
 | |
| 							if nvarargs < 0 {
 | |
| 								nvarargs = 0
 | |
| 							}
 | |
| 
 | |
| 							ls.reg.SetTop(cf.LocalBase + nargs + np)
 | |
| 							for i := 0; i < np; i++ {
 | |
| 								//ls.reg.Set(cf.LocalBase+nargs+i, ls.reg.Get(cf.LocalBase+i))
 | |
| 								ls.reg.array[cf.LocalBase+nargs+i] = ls.reg.array[cf.LocalBase+i]
 | |
| 								//ls.reg.Set(cf.LocalBase+i, LNil)
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 
 | |
| 							if CompatVarArg {
 | |
| 								ls.reg.SetTop(cf.LocalBase + nargs + np + 1)
 | |
| 								if (proto.IsVarArg & VarArgNeedsArg) != 0 {
 | |
| 									argtb := newLTable(nvarargs, 0)
 | |
| 									for i := 0; i < nvarargs; i++ {
 | |
| 										argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i))
 | |
| 									}
 | |
| 									argtb.RawSetString("n", LNumber(nvarargs))
 | |
| 									//ls.reg.Set(cf.LocalBase+nargs+np, argtb)
 | |
| 									ls.reg.array[cf.LocalBase+nargs+np] = argtb
 | |
| 								} else {
 | |
| 									ls.reg.array[cf.LocalBase+nargs+np] = LNil
 | |
| 								}
 | |
| 							}
 | |
| 							cf.LocalBase += nargs
 | |
| 							maxreg := cf.LocalBase + int(proto.NumUsedRegisters)
 | |
| 							ls.reg.SetTop(maxreg)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				ls.currentFrame = newcf
 | |
| 			}
 | |
| 			if callable.IsG && callGFunction(L, false) {
 | |
| 				return 1
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TAILCALL
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			nargs := B - 1
 | |
| 			if B == 0 {
 | |
| 				nargs = reg.Top() - (RA + 1)
 | |
| 			}
 | |
| 			lv := reg.Get(RA)
 | |
| 			var callable *LFunction
 | |
| 			var meta bool
 | |
| 			if fn, ok := lv.assertFunction(); ok {
 | |
| 				callable = fn
 | |
| 				meta = false
 | |
| 			} 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'
 | |
| 			{
 | |
| 				ls := L
 | |
| 				idx := lbase
 | |
| 				if ls.uvcache != nil {
 | |
| 					var prev *Upvalue
 | |
| 					for uv := ls.uvcache; uv != nil; uv = uv.next {
 | |
| 						if uv.index >= idx {
 | |
| 							if prev != nil {
 | |
| 								prev.next = nil
 | |
| 							} else {
 | |
| 								ls.uvcache = nil
 | |
| 							}
 | |
| 							uv.Close()
 | |
| 						}
 | |
| 						prev = uv
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			if callable.IsG {
 | |
| 				luaframe := cf
 | |
| 				L.pushCallFrame(callFrame{
 | |
| 					Fn:         callable,
 | |
| 					Pc:         0,
 | |
| 					Base:       RA,
 | |
| 					LocalBase:  RA + 1,
 | |
| 					ReturnBase: cf.ReturnBase,
 | |
| 					NArgs:      nargs,
 | |
| 					NRet:       cf.NRet,
 | |
| 					Parent:     cf,
 | |
| 					TailCall:   0,
 | |
| 				}, lv, meta)
 | |
| 				if callGFunction(L, true) {
 | |
| 					return 1
 | |
| 				}
 | |
| 				if L.currentFrame == nil || L.currentFrame.Fn.IsG || luaframe == baseframe {
 | |
| 					return 1
 | |
| 				}
 | |
| 			} else {
 | |
| 				base := cf.Base
 | |
| 				cf.Fn = callable
 | |
| 				cf.Pc = 0
 | |
| 				cf.Base = RA
 | |
| 				cf.LocalBase = RA + 1
 | |
| 				cf.ReturnBase = cf.ReturnBase
 | |
| 				cf.NArgs = nargs
 | |
| 				cf.NRet = cf.NRet
 | |
| 				cf.TailCall++
 | |
| 				lbase := cf.LocalBase
 | |
| 				if meta {
 | |
| 					cf.NArgs++
 | |
| 					L.reg.Insert(lv, cf.LocalBase)
 | |
| 				}
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func (ls *LState) initCallFrame(cf *callFrame) ' in '_state.go'
 | |
| 				{
 | |
| 					ls := L
 | |
| 					if cf.Fn.IsG {
 | |
| 						ls.reg.SetTop(cf.LocalBase + cf.NArgs)
 | |
| 					} else {
 | |
| 						proto := cf.Fn.Proto
 | |
| 						nargs := cf.NArgs
 | |
| 						np := int(proto.NumParameters)
 | |
| 						if nargs < np {
 | |
| 							// default any missing arguments to nil
 | |
| 							newSize := cf.LocalBase + np
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								rg := ls.reg
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := nargs; i < np; i++ {
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 							nargs = np
 | |
| 							ls.reg.top = newSize
 | |
| 						}
 | |
| 
 | |
| 						if (proto.IsVarArg & VarArgIsVarArg) == 0 {
 | |
| 							if nargs < int(proto.NumUsedRegisters) {
 | |
| 								nargs = int(proto.NumUsedRegisters)
 | |
| 							}
 | |
| 							newSize := cf.LocalBase + nargs
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								rg := ls.reg
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := np; i < nargs; i++ {
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 							ls.reg.top = cf.LocalBase + int(proto.NumUsedRegisters)
 | |
| 						} else {
 | |
| 							/* swap vararg positions:
 | |
| 									   closure
 | |
| 									   namedparam1 <- lbase
 | |
| 									   namedparam2
 | |
| 									   vararg1
 | |
| 									   vararg2
 | |
| 
 | |
| 							           TO
 | |
| 
 | |
| 									   closure
 | |
| 									   nil
 | |
| 									   nil
 | |
| 									   vararg1
 | |
| 									   vararg2
 | |
| 									   namedparam1 <- lbase
 | |
| 									   namedparam2
 | |
| 							*/
 | |
| 							nvarargs := nargs - np
 | |
| 							if nvarargs < 0 {
 | |
| 								nvarargs = 0
 | |
| 							}
 | |
| 
 | |
| 							ls.reg.SetTop(cf.LocalBase + nargs + np)
 | |
| 							for i := 0; i < np; i++ {
 | |
| 								//ls.reg.Set(cf.LocalBase+nargs+i, ls.reg.Get(cf.LocalBase+i))
 | |
| 								ls.reg.array[cf.LocalBase+nargs+i] = ls.reg.array[cf.LocalBase+i]
 | |
| 								//ls.reg.Set(cf.LocalBase+i, LNil)
 | |
| 								ls.reg.array[cf.LocalBase+i] = LNil
 | |
| 							}
 | |
| 
 | |
| 							if CompatVarArg {
 | |
| 								ls.reg.SetTop(cf.LocalBase + nargs + np + 1)
 | |
| 								if (proto.IsVarArg & VarArgNeedsArg) != 0 {
 | |
| 									argtb := newLTable(nvarargs, 0)
 | |
| 									for i := 0; i < nvarargs; i++ {
 | |
| 										argtb.RawSetInt(i+1, ls.reg.Get(cf.LocalBase+np+i))
 | |
| 									}
 | |
| 									argtb.RawSetString("n", LNumber(nvarargs))
 | |
| 									//ls.reg.Set(cf.LocalBase+nargs+np, argtb)
 | |
| 									ls.reg.array[cf.LocalBase+nargs+np] = argtb
 | |
| 								} else {
 | |
| 									ls.reg.array[cf.LocalBase+nargs+np] = LNil
 | |
| 								}
 | |
| 							}
 | |
| 							cf.LocalBase += nargs
 | |
| 							maxreg := cf.LocalBase + int(proto.NumUsedRegisters)
 | |
| 							ls.reg.SetTop(maxreg)
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 				{
 | |
| 					rg := L.reg
 | |
| 					regv := base
 | |
| 					start := RA
 | |
| 					limit := -1
 | |
| 					n := reg.Top() - RA - 1
 | |
| 					newSize := regv + n
 | |
| 					// this section is inlined by go-inline
 | |
| 					// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 					{
 | |
| 						requiredSize := newSize
 | |
| 						if requiredSize > cap(rg.array) {
 | |
| 							rg.resize(requiredSize)
 | |
| 						}
 | |
| 					}
 | |
| 					if limit == -1 || limit > rg.top {
 | |
| 						limit = rg.top
 | |
| 					}
 | |
| 					for i := 0; i < n; i++ {
 | |
| 						srcIdx := start + i
 | |
| 						if srcIdx >= limit || srcIdx < 0 {
 | |
| 							rg.array[regv+i] = LNil
 | |
| 						} else {
 | |
| 							rg.array[regv+i] = rg.array[srcIdx]
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 					// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 					oldtop := rg.top
 | |
| 					rg.top = regv + n
 | |
| 					if rg.top < oldtop {
 | |
| 						nilRange := rg.array[rg.top:oldtop]
 | |
| 						for i := range nilRange {
 | |
| 							nilRange[i] = nil
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				cf.Base = base
 | |
| 				cf.LocalBase = base + (cf.LocalBase - lbase + 1)
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_RETURN
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (ls *LState) closeUpvalues(idx int) ' in '_state.go'
 | |
| 			{
 | |
| 				ls := L
 | |
| 				idx := lbase
 | |
| 				if ls.uvcache != nil {
 | |
| 					var prev *Upvalue
 | |
| 					for uv := ls.uvcache; uv != nil; uv = uv.next {
 | |
| 						if uv.index >= idx {
 | |
| 							if prev != nil {
 | |
| 								prev.next = nil
 | |
| 							} else {
 | |
| 								ls.uvcache = nil
 | |
| 							}
 | |
| 							uv.Close()
 | |
| 						}
 | |
| 						prev = uv
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			nret := B - 1
 | |
| 			if B == 0 {
 | |
| 				nret = reg.Top() - RA
 | |
| 			}
 | |
| 			n := cf.NRet
 | |
| 			if cf.NRet == MultRet {
 | |
| 				n = nret
 | |
| 			}
 | |
| 
 | |
| 			if L.Parent != nil && L.stack.Sp() == 1 {
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func copyReturnValues(L *LState, regv, start, n, b int) ' in '_vm.go'
 | |
| 				{
 | |
| 					regv := reg.Top()
 | |
| 					start := RA
 | |
| 					b := B
 | |
| 					if b == 1 {
 | |
| 						// this section is inlined by go-inline
 | |
| 						// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 						{
 | |
| 							rg := L.reg
 | |
| 							regm := regv
 | |
| 							newSize := regm + n
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := 0; i < n; i++ {
 | |
| 								rg.array[regm+i] = LNil
 | |
| 							}
 | |
| 							// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 							// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 							oldtop := rg.top
 | |
| 							rg.top = regm + n
 | |
| 							if rg.top < oldtop {
 | |
| 								nilRange := rg.array[rg.top:oldtop]
 | |
| 								for i := range nilRange {
 | |
| 									nilRange[i] = nil
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 					} else {
 | |
| 						// this section is inlined by go-inline
 | |
| 						// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 						{
 | |
| 							rg := L.reg
 | |
| 							limit := -1
 | |
| 							newSize := regv + n
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							if limit == -1 || limit > rg.top {
 | |
| 								limit = rg.top
 | |
| 							}
 | |
| 							for i := 0; i < n; i++ {
 | |
| 								srcIdx := start + i
 | |
| 								if srcIdx >= limit || srcIdx < 0 {
 | |
| 									rg.array[regv+i] = LNil
 | |
| 								} else {
 | |
| 									rg.array[regv+i] = rg.array[srcIdx]
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 							// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 							oldtop := rg.top
 | |
| 							rg.top = regv + n
 | |
| 							if rg.top < oldtop {
 | |
| 								nilRange := rg.array[rg.top:oldtop]
 | |
| 								for i := range nilRange {
 | |
| 									nilRange[i] = nil
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 						if b > 1 && n > (b-1) {
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 							{
 | |
| 								rg := L.reg
 | |
| 								regm := regv + b - 1
 | |
| 								n := n - (b - 1)
 | |
| 								newSize := regm + n
 | |
| 								// this section is inlined by go-inline
 | |
| 								// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 								{
 | |
| 									requiredSize := newSize
 | |
| 									if requiredSize > cap(rg.array) {
 | |
| 										rg.resize(requiredSize)
 | |
| 									}
 | |
| 								}
 | |
| 								for i := 0; i < n; i++ {
 | |
| 									rg.array[regm+i] = LNil
 | |
| 								}
 | |
| 								// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 								// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 								oldtop := rg.top
 | |
| 								rg.top = regm + n
 | |
| 								if rg.top < oldtop {
 | |
| 									nilRange := rg.array[rg.top:oldtop]
 | |
| 									for i := range nilRange {
 | |
| 										nilRange[i] = nil
 | |
| 									}
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				switchToParentThread(L, n, false, true)
 | |
| 				return 1
 | |
| 			}
 | |
| 			islast := baseframe == L.stack.Pop() || L.stack.IsEmpty()
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func copyReturnValues(L *LState, regv, start, n, b int) ' in '_vm.go'
 | |
| 			{
 | |
| 				regv := cf.ReturnBase
 | |
| 				start := RA
 | |
| 				b := B
 | |
| 				if b == 1 {
 | |
| 					// this section is inlined by go-inline
 | |
| 					// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 					{
 | |
| 						rg := L.reg
 | |
| 						regm := regv
 | |
| 						newSize := regm + n
 | |
| 						// this section is inlined by go-inline
 | |
| 						// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 						{
 | |
| 							requiredSize := newSize
 | |
| 							if requiredSize > cap(rg.array) {
 | |
| 								rg.resize(requiredSize)
 | |
| 							}
 | |
| 						}
 | |
| 						for i := 0; i < n; i++ {
 | |
| 							rg.array[regm+i] = LNil
 | |
| 						}
 | |
| 						// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 						// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 						oldtop := rg.top
 | |
| 						rg.top = regm + n
 | |
| 						if rg.top < oldtop {
 | |
| 							nilRange := rg.array[rg.top:oldtop]
 | |
| 							for i := range nilRange {
 | |
| 								nilRange[i] = nil
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				} else {
 | |
| 					// this section is inlined by go-inline
 | |
| 					// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 					{
 | |
| 						rg := L.reg
 | |
| 						limit := -1
 | |
| 						newSize := regv + n
 | |
| 						// this section is inlined by go-inline
 | |
| 						// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 						{
 | |
| 							requiredSize := newSize
 | |
| 							if requiredSize > cap(rg.array) {
 | |
| 								rg.resize(requiredSize)
 | |
| 							}
 | |
| 						}
 | |
| 						if limit == -1 || limit > rg.top {
 | |
| 							limit = rg.top
 | |
| 						}
 | |
| 						for i := 0; i < n; i++ {
 | |
| 							srcIdx := start + i
 | |
| 							if srcIdx >= limit || srcIdx < 0 {
 | |
| 								rg.array[regv+i] = LNil
 | |
| 							} else {
 | |
| 								rg.array[regv+i] = rg.array[srcIdx]
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 						// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 						oldtop := rg.top
 | |
| 						rg.top = regv + n
 | |
| 						if rg.top < oldtop {
 | |
| 							nilRange := rg.array[rg.top:oldtop]
 | |
| 							for i := range nilRange {
 | |
| 								nilRange[i] = nil
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 					if b > 1 && n > (b-1) {
 | |
| 						// this section is inlined by go-inline
 | |
| 						// source function is 'func (rg *registry) FillNil(regm, n int) ' in '_state.go'
 | |
| 						{
 | |
| 							rg := L.reg
 | |
| 							regm := regv + b - 1
 | |
| 							n := n - (b - 1)
 | |
| 							newSize := regm + n
 | |
| 							// this section is inlined by go-inline
 | |
| 							// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 							{
 | |
| 								requiredSize := newSize
 | |
| 								if requiredSize > cap(rg.array) {
 | |
| 									rg.resize(requiredSize)
 | |
| 								}
 | |
| 							}
 | |
| 							for i := 0; i < n; i++ {
 | |
| 								rg.array[regm+i] = LNil
 | |
| 							}
 | |
| 							// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 							// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 							oldtop := rg.top
 | |
| 							rg.top = regm + n
 | |
| 							if rg.top < oldtop {
 | |
| 								nilRange := rg.array[rg.top:oldtop]
 | |
| 								for i := range nilRange {
 | |
| 									nilRange[i] = nil
 | |
| 								}
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			L.currentFrame = L.stack.Last()
 | |
| 			if islast || L.currentFrame == nil || L.currentFrame.Fn.IsG {
 | |
| 				return 1
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORLOOP
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			if init, ok1 := reg.Get(RA).assertFloat64(); ok1 {
 | |
| 				if limit, ok2 := reg.Get(RA + 1).assertFloat64(); ok2 {
 | |
| 					if step, ok3 := reg.Get(RA + 2).assertFloat64(); ok3 {
 | |
| 						init += step
 | |
| 						reg.SetNumber(RA, LNumber(init))
 | |
| 						if (step > 0 && init <= limit) || (step <= 0 && init >= limit) {
 | |
| 							Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX
 | |
| 							cf.Pc += Sbx
 | |
| 							reg.SetNumber(RA+3, LNumber(init))
 | |
| 						} else {
 | |
| 							reg.SetTop(RA + 1)
 | |
| 						}
 | |
| 					} else {
 | |
| 						L.RaiseError("for statement step must be a number")
 | |
| 					}
 | |
| 				} else {
 | |
| 					L.RaiseError("for statement limit must be a number")
 | |
| 				}
 | |
| 			} else {
 | |
| 				L.RaiseError("for statement init must be a number")
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_FORPREP
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			Sbx := int(inst&0x3ffff) - opMaxArgSbx //GETSBX
 | |
| 			if init, ok1 := reg.Get(RA).assertFloat64(); ok1 {
 | |
| 				if step, ok2 := reg.Get(RA + 2).assertFloat64(); ok2 {
 | |
| 					reg.SetNumber(RA, LNumber(init-step))
 | |
| 				} else {
 | |
| 					L.RaiseError("for statement step must be a number")
 | |
| 				}
 | |
| 			} else {
 | |
| 				L.RaiseError("for statement init must be a number")
 | |
| 			}
 | |
| 			cf.Pc += Sbx
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_TFORLOOP
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			nret := C
 | |
| 			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)
 | |
| 			if value := reg.Get(RA + 3); value != LNil {
 | |
| 				reg.Set(RA+2, value)
 | |
| 				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
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff)    //GETB
 | |
| 			C := int(inst>>9) & 0x1ff //GETC
 | |
| 			if C == 0 {
 | |
| 				C = int(cf.Fn.Proto.Code[cf.Pc])
 | |
| 				cf.Pc++
 | |
| 			}
 | |
| 			offset := (C - 1) * FieldsPerFlush
 | |
| 			table := reg.Get(RA).(*LTable)
 | |
| 			nelem := B
 | |
| 			if B == 0 {
 | |
| 				nelem = reg.Top() - RA - 1
 | |
| 			}
 | |
| 			for i := 1; i <= nelem; i++ {
 | |
| 				table.RawSetInt(offset+i, reg.Get(RA+i))
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSE
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (ls *LState) closeUpvalues(idx int) ' in '_state.go'
 | |
| 			{
 | |
| 				ls := L
 | |
| 				idx := RA
 | |
| 				if ls.uvcache != nil {
 | |
| 					var prev *Upvalue
 | |
| 					for uv := ls.uvcache; uv != nil; uv = uv.next {
 | |
| 						if uv.index >= idx {
 | |
| 							if prev != nil {
 | |
| 								prev.next = nil
 | |
| 							} else {
 | |
| 								ls.uvcache = nil
 | |
| 							}
 | |
| 							uv.Close()
 | |
| 						}
 | |
| 						prev = uv
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_CLOSURE
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			Bx := int(inst & 0x3ffff) //GETBX
 | |
| 			proto := cf.Fn.Proto.FunctionPrototypes[Bx]
 | |
| 			closure := newLFunctionL(proto, cf.Fn.Env, int(proto.NumUpvalues))
 | |
| 			reg.Set(RA, closure)
 | |
| 			for i := 0; i < int(proto.NumUpvalues); i++ {
 | |
| 				inst = cf.Fn.Proto.Code[cf.Pc]
 | |
| 				cf.Pc++
 | |
| 				B := opGetArgB(inst)
 | |
| 				switch opGetOpCode(inst) {
 | |
| 				case OP_MOVE:
 | |
| 					closure.Upvalues[i] = L.findUpvalue(lbase + B)
 | |
| 				case OP_GETUPVAL:
 | |
| 					closure.Upvalues[i] = cf.Fn.Upvalues[B]
 | |
| 				}
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_VARARG
 | |
| 			reg := L.reg
 | |
| 			cf := L.currentFrame
 | |
| 			lbase := cf.LocalBase
 | |
| 			A := int(inst>>18) & 0xff //GETA
 | |
| 			RA := lbase + A
 | |
| 			B := int(inst & 0x1ff) //GETB
 | |
| 			nparams := int(cf.Fn.Proto.NumParameters)
 | |
| 			nvarargs := cf.NArgs - nparams
 | |
| 			if nvarargs < 0 {
 | |
| 				nvarargs = 0
 | |
| 			}
 | |
| 			nwant := B - 1
 | |
| 			if B == 0 {
 | |
| 				nwant = nvarargs
 | |
| 			}
 | |
| 			// this section is inlined by go-inline
 | |
| 			// source function is 'func (rg *registry) CopyRange(regv, start, limit, n int) ' in '_state.go'
 | |
| 			{
 | |
| 				rg := reg
 | |
| 				regv := RA
 | |
| 				start := cf.Base + nparams + 1
 | |
| 				limit := cf.LocalBase
 | |
| 				n := nwant
 | |
| 				newSize := regv + n
 | |
| 				// this section is inlined by go-inline
 | |
| 				// source function is 'func (rg *registry) checkSize(requiredSize int) ' in '_state.go'
 | |
| 				{
 | |
| 					requiredSize := newSize
 | |
| 					if requiredSize > cap(rg.array) {
 | |
| 						rg.resize(requiredSize)
 | |
| 					}
 | |
| 				}
 | |
| 				if limit == -1 || limit > rg.top {
 | |
| 					limit = rg.top
 | |
| 				}
 | |
| 				for i := 0; i < n; i++ {
 | |
| 					srcIdx := start + i
 | |
| 					if srcIdx >= limit || srcIdx < 0 {
 | |
| 						rg.array[regv+i] = LNil
 | |
| 					} else {
 | |
| 						rg.array[regv+i] = rg.array[srcIdx]
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// values beyond top don't need to be valid LValues, so setting them to nil is fine
 | |
| 				// setting them to nil rather than LNil lets us invoke the golang memclr opto
 | |
| 				oldtop := rg.top
 | |
| 				rg.top = regv + n
 | |
| 				if rg.top < oldtop {
 | |
| 					nilRange := rg.array[rg.top:oldtop]
 | |
| 					for i := range nilRange {
 | |
| 						nilRange[i] = nil
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			return 0
 | |
| 		},
 | |
| 		func(L *LState, inst uint32, baseframe *callFrame) int { //OP_NOP
 | |
| 			return 0
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func opArith(L *LState, inst uint32, baseframe *callFrame) int { //OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW
 | |
| 	reg := L.reg
 | |
| 	cf := L.currentFrame
 | |
| 	lbase := cf.LocalBase
 | |
| 	A := int(inst>>18) & 0xff //GETA
 | |
| 	RA := lbase + A
 | |
| 	opcode := int(inst >> 26) //GETOPCODE
 | |
| 	B := int(inst & 0x1ff)    //GETB
 | |
| 	C := int(inst>>9) & 0x1ff //GETC
 | |
| 	lhs := L.rkValue(B)
 | |
| 	rhs := L.rkValue(C)
 | |
| 	v1, ok1 := lhs.assertFloat64()
 | |
| 	v2, ok2 := rhs.assertFloat64()
 | |
| 	if ok1 && ok2 {
 | |
| 		reg.SetNumber(RA, numberArith(L, opcode, LNumber(v1), LNumber(v2)))
 | |
| 	} else {
 | |
| 		reg.Set(RA, objectArith(L, opcode, lhs, rhs))
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| func luaModulo(lhs, rhs LNumber) LNumber {
 | |
| 	flhs := float64(lhs)
 | |
| 	frhs := float64(rhs)
 | |
| 	v := math.Mod(flhs, frhs)
 | |
| 	if flhs < 0 || frhs < 0 && !(flhs < 0 && frhs < 0) {
 | |
| 		v += frhs
 | |
| 	}
 | |
| 	return LNumber(v)
 | |
| }
 | |
| 
 | |
| func numberArith(L *LState, opcode int, lhs, rhs LNumber) LNumber {
 | |
| 	switch opcode {
 | |
| 	case OP_ADD:
 | |
| 		return lhs + rhs
 | |
| 	case OP_SUB:
 | |
| 		return lhs - rhs
 | |
| 	case OP_MUL:
 | |
| 		return lhs * rhs
 | |
| 	case OP_DIV:
 | |
| 		return lhs / rhs
 | |
| 	case OP_MOD:
 | |
| 		return luaModulo(lhs, rhs)
 | |
| 	case OP_POW:
 | |
| 		flhs := float64(lhs)
 | |
| 		frhs := float64(rhs)
 | |
| 		return LNumber(math.Pow(flhs, frhs))
 | |
| 	}
 | |
| 	panic("should not reach here")
 | |
| 	return LNumber(0)
 | |
| }
 | |
| 
 | |
| func objectArith(L *LState, opcode int, lhs, rhs LValue) LValue {
 | |
| 	event := ""
 | |
| 	switch opcode {
 | |
| 	case OP_ADD:
 | |
| 		event = "__add"
 | |
| 	case OP_SUB:
 | |
| 		event = "__sub"
 | |
| 	case OP_MUL:
 | |
| 		event = "__mul"
 | |
| 	case OP_DIV:
 | |
| 		event = "__div"
 | |
| 	case OP_MOD:
 | |
| 		event = "__mod"
 | |
| 	case OP_POW:
 | |
| 		event = "__pow"
 | |
| 	}
 | |
| 	op := L.metaOp2(lhs, rhs, event)
 | |
| 	if op.Type() == LTFunction {
 | |
| 		L.reg.Push(op)
 | |
| 		L.reg.Push(lhs)
 | |
| 		L.reg.Push(rhs)
 | |
| 		L.Call(2, 1)
 | |
| 		return L.reg.Pop()
 | |
| 	}
 | |
| 	if str, ok := lhs.(LString); ok {
 | |
| 		if lnum, err := parseNumber(string(str)); err == nil {
 | |
| 			lhs = lnum
 | |
| 		}
 | |
| 	}
 | |
| 	if str, ok := rhs.(LString); ok {
 | |
| 		if rnum, err := parseNumber(string(str)); err == nil {
 | |
| 			rhs = rnum
 | |
| 		}
 | |
| 	}
 | |
| 	if v1, ok1 := lhs.assertFloat64(); ok1 {
 | |
| 		if v2, ok2 := rhs.assertFloat64(); ok2 {
 | |
| 			return numberArith(L, opcode, LNumber(v1), LNumber(v2))
 | |
| 		}
 | |
| 	}
 | |
| 	L.RaiseError(fmt.Sprintf("cannot perform %v operation between %v and %v",
 | |
| 		strings.TrimLeft(event, "_"), lhs.Type().String(), rhs.Type().String()))
 | |
| 
 | |
| 	return LNil
 | |
| }
 | |
| 
 | |
| func stringConcat(L *LState, total, last int) LValue {
 | |
| 	rhs := L.reg.Get(last)
 | |
| 	total--
 | |
| 	for i := last - 1; total > 0; {
 | |
| 		lhs := L.reg.Get(i)
 | |
| 		if !(LVCanConvToString(lhs) && LVCanConvToString(rhs)) {
 | |
| 			op := L.metaOp2(lhs, rhs, "__concat")
 | |
| 			if op.Type() == LTFunction {
 | |
| 				L.reg.Push(op)
 | |
| 				L.reg.Push(lhs)
 | |
| 				L.reg.Push(rhs)
 | |
| 				L.Call(2, 1)
 | |
| 				rhs = L.reg.Pop()
 | |
| 				total--
 | |
| 				i--
 | |
| 			} else {
 | |
| 				L.RaiseError("cannot perform concat operation between %v and %v", lhs.Type().String(), rhs.Type().String())
 | |
| 				return LNil
 | |
| 			}
 | |
| 		} else {
 | |
| 			buf := make([]string, total+1)
 | |
| 			buf[total] = LVAsString(rhs)
 | |
| 			for total > 0 {
 | |
| 				lhs = L.reg.Get(i)
 | |
| 				if !LVCanConvToString(lhs) {
 | |
| 					break
 | |
| 				}
 | |
| 				buf[total-1] = LVAsString(lhs)
 | |
| 				i--
 | |
| 				total--
 | |
| 			}
 | |
| 			rhs = LString(strings.Join(buf, ""))
 | |
| 		}
 | |
| 	}
 | |
| 	return rhs
 | |
| }
 | |
| 
 | |
| func lessThan(L *LState, lhs, rhs LValue) bool {
 | |
| 	// optimization for numbers
 | |
| 	if v1, ok1 := lhs.assertFloat64(); ok1 {
 | |
| 		if v2, ok2 := rhs.assertFloat64(); ok2 {
 | |
| 			return v1 < v2
 | |
| 		}
 | |
| 		L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String())
 | |
| 	}
 | |
| 	if lhs.Type() != rhs.Type() {
 | |
| 		L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String())
 | |
| 		return false
 | |
| 	}
 | |
| 	ret := false
 | |
| 	switch lhs.Type() {
 | |
| 	case LTString:
 | |
| 		ret = strCmp(string(lhs.(LString)), string(rhs.(LString))) < 0
 | |
| 	default:
 | |
| 		ret = objectRationalWithError(L, lhs, rhs, "__lt")
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func equals(L *LState, lhs, rhs LValue, raw bool) bool {
 | |
| 	if lhs.Type() != rhs.Type() {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	ret := false
 | |
| 	switch lhs.Type() {
 | |
| 	case LTNil:
 | |
| 		ret = true
 | |
| 	case LTNumber:
 | |
| 		v1, _ := lhs.assertFloat64()
 | |
| 		v2, _ := rhs.assertFloat64()
 | |
| 		ret = v1 == v2
 | |
| 	case LTBool:
 | |
| 		ret = bool(lhs.(LBool)) == bool(rhs.(LBool))
 | |
| 	case LTString:
 | |
| 		ret = string(lhs.(LString)) == string(rhs.(LString))
 | |
| 	case LTUserData, LTTable:
 | |
| 		if lhs == rhs {
 | |
| 			ret = true
 | |
| 		} else if !raw {
 | |
| 			switch objectRational(L, lhs, rhs, "__eq") {
 | |
| 			case 1:
 | |
| 				ret = true
 | |
| 			default:
 | |
| 				ret = false
 | |
| 			}
 | |
| 		}
 | |
| 	default:
 | |
| 		ret = lhs == rhs
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func objectRationalWithError(L *LState, lhs, rhs LValue, event string) bool {
 | |
| 	switch objectRational(L, lhs, rhs, event) {
 | |
| 	case 1:
 | |
| 		return true
 | |
| 	case 0:
 | |
| 		return false
 | |
| 	}
 | |
| 	L.RaiseError("attempt to compare %v with %v", lhs.Type().String(), rhs.Type().String())
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func objectRational(L *LState, lhs, rhs LValue, event string) int {
 | |
| 	m1 := L.metaOp1(lhs, event)
 | |
| 	m2 := L.metaOp1(rhs, event)
 | |
| 	if m1.Type() == LTFunction && m1 == m2 {
 | |
| 		L.reg.Push(m1)
 | |
| 		L.reg.Push(lhs)
 | |
| 		L.reg.Push(rhs)
 | |
| 		L.Call(2, 1)
 | |
| 		if LVAsBool(L.reg.Pop()) {
 | |
| 			return 1
 | |
| 		}
 | |
| 		return 0
 | |
| 	}
 | |
| 	return -1
 | |
| }
 |