diff --git a/.gitignore b/.gitignore index 3f4f5bb..ac68bba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/__history/ +__history \ No newline at end of file diff --git a/Lua.API.pas b/Lua.API.pas index 3ff0c9d..a1a4133 100644 --- a/Lua.API.pas +++ b/Lua.API.pas @@ -194,8 +194,8 @@ var lua_pushunsigned: procedure(L: lua_State; n: lua_Unsigned); cdecl; lua_pushlstring: function (L: lua_State; s: PAnsiChar; l_: size_t): PAnsiChar; cdecl; lua_pushstring: function (L: lua_State; s: PAnsiChar): PAnsiChar; cdecl; - lua_pushvfstring: function (L: lua_State; fmt: PAnsiChar; argp: array of const): PAnsiChar; cdecl; - lua_pushfstring: function (L: lua_State; fmt: PAnsiChar): PAnsiChar; cdecl; + lua_pushvfstring: function (L: lua_State; fmt: PAnsiChar; argp: Pointer): PAnsiChar; cdecl; + lua_pushfstring: function (L: lua_State; fmt: PAnsiChar; argp: Pointer): PAnsiChar; cdecl; lua_pushcclosure: procedure(L: lua_State; fn: lua_CFunction; n: Integer); cdecl; lua_pushboolean: procedure(L: lua_State; b: Integer); cdecl; lua_pushlightuserdata: procedure(L: lua_State; p: Pointer); cdecl; @@ -408,6 +408,10 @@ type var luaL_setfuncs: procedure(L: lua_State; luaL_Reg: PluaL_Reg; nup: Integer); cdecl; + procedure luaL_where(L: lua_State; level: Integer); + function luaL_error(L: lua_State; fmt: PAnsiChar; argp: Pointer): Integer; + + const DefaultLuaLibName = 'lua' + LUA_VERSION_MAJOR + LUA_VERSION_MINOR + '.dll'; @@ -727,6 +731,33 @@ begin Result := lua_tolstring(L, idx, nil); end; +procedure luaL_where(L: lua_State; level: Integer); +var + ar: lua_Debug; + msg: AnsiString; + +begin + if (lua_getstack(L, level, ar) <> 0) then // check function at level + begin + lua_getinfo(L, 'Sl', ar); // get info about it + if (ar.currentline > 0) then // is there info? + begin + msg := Format('%s:%d: ', [ar.short_src, ar.currentline]); + lua_pushlstring(L, PAnsiChar(msg), Length(msg)); + exit + end; + end; + lua_pushliteral(L, ''); // else, no information available... +end; + +function luaL_error(L: lua_State; fmt: PAnsiChar; argp: Pointer): Integer; +begin + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + lua_concat(L, 2); + Result := lua_error(L); +end; + initialization finalization diff --git a/Lua.pas b/Lua.pas index 0a0ff6a..3b624f0 100644 --- a/Lua.pas +++ b/Lua.pas @@ -40,6 +40,7 @@ type ELuaUnsupportedParameterException = class(ELuaException); ELuaUnsupportedVariableException = class(ELuaException); ELuaNoFunctionException = class(ELuaException); + ELuaNativeCodeException = class(ELuaException); TLuaLibrary = (Base, Coroutine, Table, IO, OS, StringLib, Bit32, Math, Debug, Package, All); TLuaLibraries = set of TLuaLibrary; @@ -127,6 +128,7 @@ type ['{57FD52A1-7D53-485B-A630-29841C498387}'] function GetEnumerator: ILuaTableEnumerator; + function HasValue(AKey: TLuaImplicitVariable): Boolean; function GetValue(AKey: TLuaImplicitVariable): ILuaVariable; procedure SetValue(AKey: TLuaImplicitVariable; AValue: TLuaImplicitVariable); end; @@ -165,6 +167,7 @@ type { ILuaTable } function GetEnumerator: ILuaTableEnumerator; + function HasValue(AKey: TLuaImplicitVariable): Boolean; function GetValue(AKey: TLuaImplicitVariable): ILuaVariable; procedure SetValue(AKey: TLuaImplicitVariable; AValue: TLuaImplicitVariable); end; @@ -666,6 +669,7 @@ begin LuaBoolean: Result := VariableBoolean; LuaString: Result := VariableString; LuaTable: Result := VariableTable; + LuaFunction: Result := VariableFunction; LuaUserData: Result := VariableUserData; LuaLightUserData: Result := VariableUserData; else @@ -1081,6 +1085,11 @@ begin end; +function TLuaTable.HasValue(AKey: TLuaImplicitVariable): Boolean; +begin + Result := FTable.ContainsKey(AKey); +end; + function TLuaTable.GetValue(AKey: TLuaImplicitVariable): ILuaVariable; begin Result := FTable[AKey]; @@ -1308,8 +1317,16 @@ end; procedure TLua.LoadFromFile(const AFileName: string; AAutoRun: Boolean; const AChunkName: string); +var + chunkName: string; + begin - LoadFromScript(TLuaScript.Create(TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone), soOwned), soOwned, AAutoRun, AChunkName); + if Length(AChunkName) > 0 then + chunkName := AChunkName + else + chunkName := ExtractFileName(AFileName); + + LoadFromScript(TLuaScript.Create(TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone), soOwned), soOwned, AAutoRun, chunkName); end; @@ -1670,8 +1687,13 @@ begin begin context := TLuaContext.Create(State); - RegisteredFunctions[ACookie](context); - Result := context.Result.Count; + try + RegisteredFunctions[ACookie](context); + Result := context.Result.Count; + except + on E:Exception do + Result := luaL_error(State, PAnsiChar(AnsiString(E.Message)), nil); + end; end; end; diff --git a/UnitTests/source/TestWrapper.pas b/UnitTests/source/TestWrapper.pas index f53a822..9829190 100644 --- a/UnitTests/source/TestWrapper.pas +++ b/UnitTests/source/TestWrapper.pas @@ -33,6 +33,7 @@ type procedure Input; procedure Output; procedure DelphiFunction; + procedure DelphiFunctionException; procedure LuaFunction; procedure LuaFunctionDefaultResult; procedure LuaFunctionString; @@ -197,6 +198,27 @@ begin end; +procedure TTestWrapper.DelphiFunctionException; +begin + Lua.RegisterFunction('crazyharry', + procedure(AContext: ILuaContext) + begin + raise Exception.Create('Boom!'); + end); + + try + Lua.LoadFromString('print(crazyharry("Did somebody say dynamite?"))'); + Fail('ELuaNativeCodeException expected'); + except + on E:Exception do + begin + CheckIs(E, ELuaException); + CheckEquals('[string "?"]:1: Boom!', E.Message); + end; + end; +end; + + procedure TTestWrapper.LuaFunction; var returnValues: ILuaReadParameters;