// Peval evaluates a piece of lua code. no panic when error occur. func (l *Lua) Peval(code string, envs ...interface{}) (returns []interface{}, err error) { defer C.lua_settop(l.State, 0) C.push_errfunc(l.State) curTop := C.lua_gettop(l.State) // parse cCode := C.CString(code) defer C.free(unsafe.Pointer(cCode)) if ret := C.luaL_loadstring(l.State, cCode); ret != 0 { // load error return nil, fmt.Errorf("LOAD ERROR: %s", C.GoString(C.lua_tolstring(l.State, -1, nil))) } // env if len(envs) > 0 { if len(envs)%2 != 0 { return nil, fmt.Errorf("number of arguments not match") } C.lua_createtable(l.State, 0, 0) for i := 0; i < len(envs); i += 2 { name, ok := envs[i].(string) if !ok { return nil, fmt.Errorf("name must be string, not %v", envs[i]) } C.lua_pushstring(l.State, cstr(name)) err := l.pushGoValue(envs[i+1], name) if err != nil { return nil, err } C.lua_rawset(l.State, -3) } // set env C.set_eval_env(l.State) } // call l.err = nil if ret := C.lua_pcall(l.State, 0, C.LUA_MULTRET, -2); ret != 0 { // error occured return nil, fmt.Errorf("CALL ERROR: %s", C.GoString(C.lua_tolstring(l.State, -1, nil))) } else if l.err != nil { // error raise by invokeGoFunc return nil, l.err } else { // return values nReturn := C.lua_gettop(l.State) - curTop returns = make([]interface{}, int(nReturn)) for i := C.int(0); i < nReturn; i++ { value, err := l.toGoValue(-1-i, interfaceType) if err != nil { return nil, err } if value != nil { returns[int(nReturn-1-i)] = value.Interface() } else { returns[int(nReturn-1-i)] = nil } } } return }
func Tostring(s *State, index int) string { var l C.size_t cs := C.lua_tolstring((*C.lua_State)(s), C.int(index), &l) // Remake this into a string! return C.GoStringN(cs, C.int(l)) }
// Converts the Lua value at the given valid index to a Go // string. The Lua value must be a string or a number; otherwise, // the function returns an empty string. If the value is a number, then // Tostring also changes the actual value in the stack to a string. (This // change confuses Next when Tostring is applied to keys during a table // traversal). The string always has a zero ('\0') after its last // character (as in C), but can contain other zeros in its body. func (this *State) Tostring(index int) string { str := C.lua_tolstring(this.luastate, C.int(index), nil) if str == nil { return "" } return C.GoString(str) }
func (state State) getStructField(structPtr reflect.Value, lkey C.int) (ret int, err error) { L := state.L vm := state.VM structValue := structPtr.Elem() t := structValue.Type() info := vm.findStruct(t) if info == nil { return -1, fmt.Errorf("can not index a solid struct") } ltype := int(C.lua_type(L, lkey)) if ltype != C.LUA_TSTRING { return -1, fmt.Errorf("field key of struct must be a string") } // //key := stringFromLua(L, lkey) //fld, ok := info.fields[key] // // <hack> using string pstr cache pstr := C.lua_tolstring(L, lkey, nil) // </hack> fld, ok := info.cache[pstr] if !ok { key := stringFromLua(L, lkey) return -1, fmt.Errorf("not such field `%v'", key) } value := getStructFieldValue(structValue, fld) state.goToLuaValue(value) return 1, nil }
// Initializes the Lua context and compiles the source code. func (e *ExecutionEngine) init() error { if e.state != nil { return nil } // Initialize the state and open the libraries. e.state = C.luaL_newstate() if e.state == nil { return errors.New("Unable to initialize Lua context.") } C.luaL_openlibs(e.state) // Generate the header file. err := e.generateHeader() if err != nil { e.Destroy() return err } // Compile the script. e.fullSource = fmt.Sprintf("%v\n%v", e.header, e.source) source := C.CString(e.fullSource) defer C.free(unsafe.Pointer(source)) ret := C.luaL_loadstring(e.state, source) if ret != 0 { defer e.Destroy() errstring := C.GoString(C.lua_tolstring(e.state, -1, nil)) return fmt.Errorf("skyd.ExecutionEngine: Syntax Error: %v", errstring) } // Run script once to initialize. ret = C.lua_pcall(e.state, 0, 0, 0) if ret != 0 { defer e.Destroy() errstring := C.GoString(C.lua_tolstring(e.state, -1, nil)) return fmt.Errorf("skyd.ExecutionEngine: Init Error: %v", errstring) } // Setup cursor. err = e.initCursor() if err != nil { e.Destroy() return err } return nil }
func (sinfo *structInfo) makeFieldsIndexCache(vm *VM) { L := vm.globalL sinfo.cache = make(map[*C.char]*structField, len(sinfo.fields)) sinfo.lref = make(map[string]int, len(sinfo.fields)) for key, field := range sinfo.fields { pushStringToLua(L, key) pstr := C.lua_tolstring(L, -1, nil) sinfo.cache[pstr] = field sinfo.lref[key] = int(C.luaL_ref(L, C.LUA_REGISTRYINDEX)) } }
func (self *Lua) RunString(code string) { defer func() { if r := recover(); r != nil { if self.PrintTraceback { //NOCOVER print("============ start lua traceback ============\n") self.RunString(`print(debug.traceback())`) print("============ end lua traceback ==============\n") } panic(r) } }() cCode := cstr(code) C.setup_message_handler(self.State) if ret := C.luaL_loadstring(self.State, cCode); ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, nil))) } ret := C.lua_pcallk(self.State, 0, 0, C.lua_gettop(self.State)-C.int(1), 0, nil) if ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, nil))) } C.lua_settop(self.State, 0) }
func packLuaString(out io.Writer, state State, object int) (n int, err error) { var cslen C.size_t cs := C.lua_tolstring(state.L, C.int(object), &cslen) // <HACK> pretend a []byte slice to avoid intermediate buffer slhead := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(cs)), Len: int(cslen), Cap: int(cslen), } pslice := (*[]byte)(unsafe.Pointer(&slhead)) // </HACK> return P.PackRaw(out, *pslice) }
// Executes an aggregation over the iterator. func (e *ExecutionEngine) Aggregate() (interface{}, error) { functionName := C.CString("sky_aggregate") defer C.free(unsafe.Pointer(functionName)) C.lua_getfield(e.state, -10002, functionName) C.lua_pushlightuserdata(e.state, unsafe.Pointer(e.cursor)) rc := C.lua_pcall(e.state, 1, 1, 0) if rc != 0 { luaErrString := C.GoString(C.lua_tolstring(e.state, -1, nil)) fmt.Println(e.FullAnnotatedSource()) return nil, fmt.Errorf("skyd.ExecutionEngine: Unable to aggregate: %s", luaErrString) } return e.decodeResult() }
func luaUnpackFromString(state State) int { // arg 1 is func udata itself var cslen C.size_t cs := C.lua_tolstring(state.L, C.int(2), &cslen) // <HACK> pretend a []byte slice to avoid intermediate buffer slhead := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(cs)), Len: int(cslen), Cap: int(cslen), } pslice := (*[]byte)(unsafe.Pointer(&slhead)) // </HACK> reader := bytes.NewReader(*pslice) return UnpackToLua(reader, state) }
// Pcall calls a lua function. no panic func (l *Lua) Pcall(fullname string, args ...interface{}) (returns []interface{}, err error) { C.push_errfunc(l.State) curTop := C.lua_gettop(l.State) // get function path := strings.Split(fullname, ".") for i, name := range path { if i == 0 { C.lua_getfield(l.State, C.LUA_GLOBALSINDEX, cstr(name)) } else { if C.lua_type(l.State, -1) != C.LUA_TTABLE { return nil, fmt.Errorf("%s is not a function", fullname) } C.lua_pushstring(l.State, cstr(name)) C.lua_gettable(l.State, -2) C.lua_remove(l.State, -2) // remove table } } if C.lua_type(l.State, -1) != C.LUA_TFUNCTION { return nil, fmt.Errorf("%s is not a function", fullname) } // args for _, arg := range args { l.pushGoValue(arg, "") } // call l.err = nil if ret := C.lua_pcall(l.State, C.int(len(args)), C.LUA_MULTRET, C.int(-(len(args))-2)); ret != 0 { // error occured return nil, fmt.Errorf("CALL ERROR: %s", C.GoString(C.lua_tolstring(l.State, -1, nil))) } else if l.err != nil { // error raise by invokeGoFunc return nil, l.err } else { // return values nReturn := C.lua_gettop(l.State) - curTop returns = make([]interface{}, int(nReturn)) for i := C.int(0); i < nReturn; i++ { value, err := l.toGoValue(-1-i, interfaceType) if err != nil { return nil, err } returns[int(nReturn-1-i)] = value.Interface() } } return }
// Decodes the result from a function into a Go object. func (e *ExecutionEngine) decodeResult() (interface{}, error) { // Encode Lua object into msgpack. rc := C.mp_pack(e.state) if rc != 1 { return nil, errors.New("skyd.ExecutionEngine: Unable to msgpack decode Lua result") } sz := C.size_t(0) ptr := C.lua_tolstring(e.state, -1, (*C.size_t)(&sz)) str := C.GoStringN(ptr, (C.int)(sz)) C.lua_settop(e.state, -(1)-1) // lua_pop() // Decode msgpack into a Go object. var ret interface{} decoder := msgpack.NewDecoder(bytes.NewBufferString(str), nil) err := decoder.Decode(&ret) if err != nil { return nil, err } return ret, nil }
// Executes an merge over the iterator. func (e *ExecutionEngine) Merge(results interface{}, data interface{}) (interface{}, error) { functionName := C.CString("sky_merge") defer C.free(unsafe.Pointer(functionName)) C.lua_getfield(e.state, -10002, functionName) err := e.encodeArgument(results) if err != nil { return results, err } err = e.encodeArgument(data) if err != nil { return results, err } rc := C.lua_pcall(e.state, 2, 1, 0) if rc != 0 { luaErrString := C.GoString(C.lua_tolstring(e.state, -1, nil)) fmt.Println(e.FullAnnotatedSource()) return results, fmt.Errorf("skyd.ExecutionEngine: Unable to merge: %s", luaErrString) } return e.decodeResult() }
// Initializes the cursor used by the script. func (e *ExecutionEngine) initCursor() error { // Create the cursor. minPropertyId, maxPropertyId := e.propertyFile.NextIdentifiers() e.cursor = C.sky_cursor_new((C.int32_t)(minPropertyId), (C.int32_t)(maxPropertyId)) e.cursor.context = unsafe.Pointer(e) C.executionEngine_setNextObjectFunc(unsafe.Pointer(e.cursor)) // Initialize the cursor from within Lua. functionName := C.CString("sky_init_cursor") defer C.free(unsafe.Pointer(functionName)) C.lua_getfield(e.state, -10002, functionName) C.lua_pushlightuserdata(e.state, unsafe.Pointer(e.cursor)) //fmt.Printf("%s\n\n", e.FullAnnotatedSource()) rc := C.lua_pcall(e.state, 1, 0, 0) if rc != 0 { luaErrString := C.GoString(C.lua_tolstring(e.state, -1, nil)) return fmt.Errorf("Unable to init cursor: %s", luaErrString) } return nil }
func (self *Lua) CallFunction(name string, args ...interface{}) { defer func() { if r := recover(); r != nil { if self.PrintTraceback { //NOCOVER print("============ start lua traceback ============\n") self.RunString(`print(debug.traceback())`) print("============ end lua traceback ==============\n") } panic(r) } }() cName := cstr(name) C.setup_message_handler(self.State) C.lua_getglobal(self.State, cName) for _, arg := range args { self.PushGoValue(reflect.ValueOf(arg)) } ret := C.lua_pcallk(self.State, C.int(len(args)), 0, C.lua_gettop(self.State)-C.int(len(args)+2), 0, nil) if ret != C.int(0) { self.Panic("%s", C.GoString(C.lua_tolstring(self.State, -1, nil))) } }
//export golua_interface_newindex_callback func golua_interface_newindex_callback(Li interface{}, iid uint, field_name_cstr *C.char) int { L := Li.(*State) iface := L.registry[iid] ifacevalue := reflect.ValueOf(iface).Elem() field_name := C.GoString(field_name_cstr) fval := ifacevalue.FieldByName(field_name) if fval.Kind() == reflect.Ptr { fval = fval.Elem() } luatype := LuaValType(C.lua_type(L.s, 3)) switch fval.Kind() { case reflect.Bool: if luatype == LUA_TBOOLEAN { fval.SetBool(int(C.lua_toboolean(L.s, 3)) != 0) return 1 } else { L.PushString("Wrong assignment to field " + field_name) return -1 } case reflect.Int: fallthrough case reflect.Int8: fallthrough case reflect.Int16: fallthrough case reflect.Int32: fallthrough case reflect.Int64: if luatype == LUA_TNUMBER { fval.SetInt(int64(C.lua_tointeger(L.s, 3))) return 1 } else { L.PushString("Wrong assignment to field " + field_name) return -1 } case reflect.Uint: fallthrough case reflect.Uint8: fallthrough case reflect.Uint16: fallthrough case reflect.Uint32: fallthrough case reflect.Uint64: if luatype == LUA_TNUMBER { fval.SetUint(uint64(C.lua_tointeger(L.s, 3))) return 1 } else { L.PushString("Wrong assignment to field " + field_name) return -1 } case reflect.String: if luatype == LUA_TSTRING { fval.SetString(C.GoString(C.lua_tolstring(L.s, 3, nil))) return 1 } else { L.PushString("Wrong assignment to field " + field_name) return -1 } case reflect.Float32: fallthrough case reflect.Float64: if luatype == LUA_TNUMBER { fval.SetFloat(float64(C.lua_tonumber(L.s, 3))) return 1 } else { L.PushString("Wrong assignment to field " + field_name) return -1 } } L.PushString("Unsupported type of field " + field_name + ": " + fval.Type().String()) return -1 }
func (L *State) ToString(index int) string { var size C.size_t //C.GoString(C.lua_tolstring(L.s, C.int(index), &size)); return C.GoString(C.lua_tolstring(L.s, C.int(index), &size)) }
func (L *State) ToBytes(index int) []byte { var size C.size_t b := C.lua_tolstring(L.s, C.int(index), &size) return C.GoBytes(unsafe.Pointer(b), C.int(size)) }
func (L *State) getString(i int) (ret string) { return C.GoString(C.lua_tolstring(L.s, C.int(i+1), nil)) }
func (lua *Lua) toGoValue(i C.int, paramType reflect.Type) (ret reflect.Value) { luaType := C.lua_type(lua.State, i) paramKind := paramType.Kind() switch paramKind { case reflect.Bool: if luaType != C.LUA_TBOOLEAN { lua.Panic("not a boolean") } ret = reflect.ValueOf(C.lua_toboolean(lua.State, i) == C.int(1)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if luaType != C.LUA_TNUMBER { lua.Panic("not an integer") } ret = reflect.New(paramType).Elem() ret.SetInt(int64(C.lua_tointegerx(lua.State, i, nil))) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if luaType != C.LUA_TNUMBER { lua.Panic("not an unsigned") } ret = reflect.New(paramType).Elem() ret.SetUint(uint64(C.lua_tointegerx(lua.State, i, nil))) case reflect.Float32, reflect.Float64: if luaType != C.LUA_TNUMBER { lua.Panic("not a float") } ret = reflect.New(paramType).Elem() ret.SetFloat(float64(C.lua_tonumberx(lua.State, i, nil))) case reflect.Interface: switch luaType { case C.LUA_TNUMBER: ret = reflect.New(floatType).Elem() ret.SetFloat(float64(C.lua_tonumberx(lua.State, i, nil))) case C.LUA_TSTRING: ret = reflect.New(stringType).Elem() ret.SetString(C.GoString(C.lua_tolstring(lua.State, i, nil))) case C.LUA_TLIGHTUSERDATA: ret = reflect.ValueOf(C.lua_topointer(lua.State, i)) case C.LUA_TBOOLEAN: ret = reflect.New(boolType).Elem() ret.SetBool(C.lua_toboolean(lua.State, i) == C.int(1)) //TODO nil //TODO table default: lua.Panic("wrong interface argument: %v", paramKind) } case reflect.String: if luaType != C.LUA_TSTRING { lua.Panic("not a string") } ret = reflect.New(paramType).Elem() ret.SetString(C.GoString(C.lua_tolstring(lua.State, i, nil))) case reflect.Slice: switch luaType { case C.LUA_TSTRING: ret = reflect.New(paramType).Elem() cstr := C.lua_tolstring(lua.State, i, nil) ret.SetBytes(C.GoBytes(unsafe.Pointer(cstr), C.int(C.strlen(cstr)))) case C.LUA_TTABLE: ret = reflect.MakeSlice(paramType, 0, 0) C.lua_pushnil(lua.State) elemType := paramType.Elem() for C.lua_next(lua.State, i) != 0 { ret = reflect.Append(ret, lua.toGoValue(-1, elemType)) C.lua_settop(lua.State, -2) } default: lua.Panic("wrong slice argument") } case reflect.Ptr: if luaType != C.LUA_TLIGHTUSERDATA { lua.Panic("not a pointer") } pointer := C.lua_topointer(lua.State, i) ret = reflect.NewAt(paramType, unsafe.Pointer(&pointer)).Elem() case reflect.Map: if luaType != C.LUA_TTABLE { lua.Panic("not a map") } ret = reflect.MakeMap(paramType) C.lua_pushnil(lua.State) keyType := paramType.Key() elemType := paramType.Elem() for C.lua_next(lua.State, i) != 0 { ret.SetMapIndex( lua.toGoValue(-2, keyType), lua.toGoValue(-1, elemType)) C.lua_settop(lua.State, -2) } case reflect.UnsafePointer: ret = reflect.ValueOf(C.lua_topointer(lua.State, i)) //TODO complex64/128 //TODO array //TODO chan //TODO func //TODO struct default: lua.Panic("unknown argument type %v", paramType) } return }
func stringFromLua(L *C.lua_State, lvalue C.int) string { var cslen C.size_t cs := C.lua_tolstring(L, lvalue, &cslen) return C.GoStringN(cs, C.int(cslen)) }
func (l *Lua) getStackTraceback() string { C.lua_getfield(l.State, C.LUA_GLOBALSINDEX, cstr("debug")) C.lua_getfield(l.State, -1, cstr("traceback")) C.lua_call(l.State, 0, 1) return C.GoString(C.lua_tolstring(l.State, -1, nil)) }
// lua_tostring func (L *State) ToString(index int) string { var size C.size_t r := C.lua_tolstring(L.s, C.int(index), &size) return C.GoStringN(r, C.int(size)) }
func (l *Lua) toGoValue(i C.int, paramType reflect.Type) (ret *reflect.Value, err error) { luaType := C.lua_type(l.State, i) paramKind := paramType.Kind() switch paramKind { case reflect.Bool: if luaType != C.LUA_TBOOLEAN { err = fmt.Errorf("not a boolean") return } v := reflect.ValueOf(C.lua_toboolean(l.State, i) == C.int(1)) ret = &v case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not an integer") return } v := reflect.New(paramType).Elem() v.SetInt(int64(C.lua_tointeger(l.State, i))) ret = &v case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not a unsigned") return } v := reflect.New(paramType).Elem() v.SetUint(uint64(C.lua_tointeger(l.State, i))) ret = &v case reflect.Float32, reflect.Float64: if luaType != C.LUA_TNUMBER { err = fmt.Errorf("not a float") return } v := reflect.New(paramType).Elem() v.SetFloat(float64(C.lua_tonumber(l.State, i))) ret = &v case reflect.Interface: switch paramType { case interfaceType: switch luaType { case C.LUA_TNUMBER: v := reflect.New(floatType).Elem() // always return float64 for interface{} v.SetFloat(float64(C.lua_tonumber(l.State, i))) ret = &v case C.LUA_TSTRING: v := reflect.New(stringType).Elem() v.SetString(C.GoString(C.lua_tolstring(l.State, i, nil))) ret = &v case C.LUA_TLIGHTUSERDATA, C.LUA_TUSERDATA: v := reflect.ValueOf(C.lua_touserdata(l.State, i)) ret = &v case C.LUA_TBOOLEAN: v := reflect.New(boolType).Elem() v.SetBool(C.lua_toboolean(l.State, i) == C.int(1)) ret = &v case C.LUA_TNIL: ret = nil default: err = fmt.Errorf("unsupported type %s for interface{}", luaTypeName(luaType)) return } default: err = fmt.Errorf("only interface{} is supported, no %v", paramType) return } case reflect.String: if luaType != C.LUA_TSTRING { err = fmt.Errorf("not a string") return } v := reflect.New(paramType).Elem() v.SetString(C.GoString(C.lua_tolstring(l.State, i, nil))) ret = &v case reflect.Slice: switch luaType { case C.LUA_TSTRING: v := reflect.New(paramType).Elem() cstr := C.lua_tolstring(l.State, i, nil) v.SetBytes(C.GoBytes(unsafe.Pointer(cstr), C.int(C.strlen(cstr)))) ret = &v case C.LUA_TTABLE: v := reflect.MakeSlice(paramType, 0, 0) C.lua_pushnil(l.State) elemType := paramType.Elem() for C.lua_next(l.State, i) != 0 { elemValue, e := l.toGoValue(-1, elemType) if e != nil { err = e return } // there is no nil value in lua table so elemValue will never be nil v = reflect.Append(v, *elemValue) C.lua_settop(l.State, -2) ret = &v } default: err = fmt.Errorf("wrong slice argument") return } case reflect.Ptr: if luaType != C.LUA_TLIGHTUSERDATA { err = fmt.Errorf("not a pointer") return } p := C.lua_topointer(l.State, i) v := reflect.NewAt(paramType, unsafe.Pointer(&p)).Elem() ret = &v case reflect.Map: if luaType != C.LUA_TTABLE { err = fmt.Errorf("not a map") return } v := reflect.MakeMap(paramType) C.lua_pushnil(l.State) keyType := paramType.Key() elemType := paramType.Elem() for C.lua_next(l.State, i) != 0 { keyValue, e := l.toGoValue(-2, keyType) if e != nil { err = e return } // table has no nil key so keyValue will not be nil elemValue, e := l.toGoValue(-1, elemType) if e != nil { err = e return } // table has no nil value so elemValue will not be nil v.SetMapIndex(*keyValue, *elemValue) C.lua_settop(l.State, -2) } ret = &v case reflect.UnsafePointer: v := reflect.ValueOf(C.lua_topointer(l.State, i)) ret = &v default: err = fmt.Errorf("unsupported toGoValue type %v", paramType) return } return }