Added: Capture method

Fixed: libraries failed to load when not using TLuaLibrary.All
This commit is contained in:
Mark van Renswoude 2016-01-09 16:31:26 +00:00
parent 095b7d4289
commit 1c0051fbb5
3 changed files with 112 additions and 18 deletions

View File

@ -245,6 +245,7 @@ type
procedure SetAutoOpenLibraries(const Value: TLuaLibraries); virtual; procedure SetAutoOpenLibraries(const Value: TLuaLibraries); virtual;
protected protected
procedure CheckState; virtual; procedure CheckState; virtual;
procedure CheckIsFunction; virtual;
procedure AfterLoad; virtual; procedure AfterLoad; virtual;
function GetRegisteredFunctionCookie: Integer; virtual; function GetRegisteredFunctionCookie: Integer; virtual;
@ -262,16 +263,17 @@ type
procedure LoadFromFile(const AFileName: string; AAutoRun: Boolean = True; const AChunkName: string = ''); virtual; procedure LoadFromFile(const AFileName: string; AAutoRun: Boolean = True; const AChunkName: string = ''); virtual;
procedure LoadFromScript(AScript: TLuaScript; AOwnership: TStreamOwnership = soReference; AAutoRun: Boolean = True; const AChunkName: string = ''); virtual; procedure LoadFromScript(AScript: TLuaScript; AOwnership: TStreamOwnership = soReference; AAutoRun: Boolean = True; const AChunkName: string = ''); virtual;
function GetGlobalVariable(const AName: string): ILuaVariable; function GetGlobalVariable(const AName: string): ILuaVariable; virtual;
procedure SetGlobalVariable(const AName: string; AVariable: TLuaImplicitVariable); procedure SetGlobalVariable(const AName: string; AVariable: TLuaImplicitVariable); virtual;
procedure RegisterFunction(const AName: string; AFunction: TLuaCFunction); procedure RegisterFunction(const AName: string; AFunction: TLuaCFunction); virtual;
procedure OpenLibraries(ALibraries: TLuaLibraries); virtual; procedure OpenLibraries(ALibraries: TLuaLibraries); virtual;
{ Run or GetByteCode should only be called right after one of the { These methods should only be called right after one of the
LoadFrom methods, which must have AutoRun set to False. } LoadFrom methods, which must have AutoRun set to False. }
procedure Run; virtual; procedure Run; virtual;
procedure GetByteCode(AStream: TStream; APop: Boolean = False); virtual; procedure GetByteCode(AStream: TStream; APop: Boolean = False); virtual;
procedure Capture(const AName: string); virtual;
function Call(const AFunctionName: string): ILuaReadParameters; overload; virtual; function Call(const AFunctionName: string): ILuaReadParameters; overload; virtual;
function Call(const AFunctionName: string; AParameters: array of const): ILuaReadParameters; overload; virtual; function Call(const AFunctionName: string; AParameters: array of const): ILuaReadParameters; overload; virtual;
@ -1178,42 +1180,41 @@ begin
else else
begin begin
if TLuaLibrary.Base in ALibraries then if TLuaLibrary.Base in ALibraries then
luaopen_base(State); luaL_requiref(State, 'base', luaopen_base, 1);
if TLuaLibrary.Coroutine in ALibraries then if TLuaLibrary.Coroutine in ALibraries then
luaopen_coroutine(State); luaL_requiref(State, 'coroutine', luaopen_coroutine, 1);
if TLuaLibrary.Table in ALibraries then if TLuaLibrary.Table in ALibraries then
luaopen_table(State); luaL_requiref(State, 'table', luaopen_table, 1);
if TLuaLibrary.IO in ALibraries then if TLuaLibrary.IO in ALibraries then
luaopen_io(State); luaL_requiref(State, 'io', luaopen_io, 1);
if TLuaLibrary.OS in ALibraries then if TLuaLibrary.OS in ALibraries then
luaopen_os(State); luaL_requiref(State, 'os', luaopen_os, 1);
if TLuaLibrary.StringLib in ALibraries then if TLuaLibrary.StringLib in ALibraries then
luaopen_string(State); luaL_requiref(State, 'string', luaopen_string, 1);
if TLuaLibrary.Bit32 in ALibraries then if TLuaLibrary.Bit32 in ALibraries then
luaopen_bit32(State); luaL_requiref(State, 'bit32', luaopen_bit32, 1);
if TLuaLibrary.Math in ALibraries then if TLuaLibrary.Math in ALibraries then
luaopen_math(State); luaL_requiref(State, 'math', luaopen_math, 1);
if TLuaLibrary.Debug in ALibraries then if TLuaLibrary.Debug in ALibraries then
luaopen_debug(State); luaL_requiref(State, 'debug', luaopen_debug, 1);
if TLuaLibrary.Package in ALibraries then if TLuaLibrary.Package in ALibraries then
luaopen_package(State); luaL_requiref(State, 'package', luaopen_package, 1);
end; end;
end; end;
procedure TLua.Run; procedure TLua.Run;
begin begin
if not lua_isfunction(State, -1) then CheckIsFunction;
raise ELuaNoFunctionException.Create('No function on top of the stack, use the LoadFrom methods first');
if lua_pcall(State, 0, 0, 0) <> 0 then if lua_pcall(State, 0, 0, 0) <> 0 then
TLuaHelpers.RaiseLastLuaError(State); TLuaHelpers.RaiseLastLuaError(State);
@ -1225,9 +1226,9 @@ end;
procedure TLua.GetByteCode(AStream: TStream; APop: Boolean); procedure TLua.GetByteCode(AStream: TStream; APop: Boolean);
var var
returnCode: Integer; returnCode: Integer;
begin begin
if not lua_isfunction(State, -1) then CheckIsFunction;
raise ELuaNoFunctionException.Create('No function on top of the stack, use the LoadFrom methods first');
try try
returnCode := lua_dump(State, LuaWrapperWriter, @AStream); returnCode := lua_dump(State, LuaWrapperWriter, @AStream);
@ -1240,6 +1241,42 @@ begin
end; end;
procedure TLua.Capture(const AName: string);
var
name: PAnsiChar;
begin
CheckIsFunction;
// Create a new table to serve as the environment
lua_newtable(State);
// Set the global AName to the new table
lua_pushvalue(State, -1);
name := TLuaHelpers.AllocLuaString(AName);
try
lua_setglobal(State, name);
finally
TLuaHelpers.FreeLuaString(name);
end;
// Set the global environment as the table's metatable index, so calls to
// global functions and variables still work
lua_newtable(State);
TLuaHelpers.PushString(State, '__index');
lua_pushglobaltable(State);
lua_settable(State, -3);
lua_setmetatable(State, -2);
// Set the new table as the environment (upvalue at index 1)
lua_setupvalue(State, -2, 1);
if lua_pcall(State, 0, 0, 0) <> 0 then
TLuaHelpers.RaiseLastLuaError(State);
end;
function TLua.Call(const AFunctionName: string): ILuaReadParameters; function TLua.Call(const AFunctionName: string): ILuaReadParameters;
begin begin
Result := Call(AFunctionName, nil); Result := Call(AFunctionName, nil);
@ -1283,6 +1320,13 @@ begin
end; end;
procedure TLua.CheckIsFunction;
begin
if not lua_isfunction(State, -1) then
raise ELuaNoFunctionException.Create('No function on top of the stack, use the LoadFrom methods first');
end;
procedure TLua.AfterLoad; procedure TLua.AfterLoad;
var var
cookie: Integer; cookie: Integer;

View File

@ -394,6 +394,8 @@ var
{ open all previous libraries } { open all previous libraries }
luaL_openlibs: procedure(L: lua_State); cdecl; luaL_openlibs: procedure(L: lua_State); cdecl;
luaL_requiref: procedure(L: lua_State; modname: PAnsiChar; openf: lua_CFunction; glb: Integer); cdecl;
type type
@ -582,6 +584,7 @@ begin
Load(@luaopen_package, 'luaopen_package'); Load(@luaopen_package, 'luaopen_package');
Load(@luaL_openlibs, 'luaL_openlibs'); Load(@luaL_openlibs, 'luaL_openlibs');
Load(@luaL_requiref, 'luaL_requiref');
Load(@luaL_setfuncs, 'luaL_setfuncs'); Load(@luaL_setfuncs, 'luaL_setfuncs');
end; end;

View File

@ -25,6 +25,7 @@ type
procedure LoadAndRunFromString; procedure LoadAndRunFromString;
procedure LoadAndRunFromStream; procedure LoadAndRunFromStream;
procedure LoadMultiple; procedure LoadMultiple;
procedure LoadMultipleSharedVariable;
procedure ChunkNameInException; procedure ChunkNameInException;
procedure Input; procedure Input;
@ -45,6 +46,8 @@ type
procedure VariableFunction; procedure VariableFunction;
procedure ByteCode; procedure ByteCode;
procedure Capture;
procedure DenyRequire;
end; end;
@ -65,6 +68,7 @@ begin
FPrinted := TStringBuilder.Create; FPrinted := TStringBuilder.Create;
FLua := TLua.Create; FLua := TLua.Create;
FLua.AutoOpenLibraries := [StringLib];
FLua.RegisterFunction('print', FLua.RegisterFunction('print',
procedure(AContext: ILuaContext) procedure(AContext: ILuaContext)
begin begin
@ -112,6 +116,14 @@ begin
end; end;
procedure TTestWrapper.LoadMultipleSharedVariable;
begin
Lua.LoadFromString('message = "Hello world!"', True, 'Script1');
Lua.LoadFromString('print(message)', True, 'Script2');
CheckEquals('Hello world!', Printed.ToString);
end;
procedure TTestWrapper.Input; procedure TTestWrapper.Input;
begin begin
Lua.SetGlobalVariable('thingy', 'world'); Lua.SetGlobalVariable('thingy', 'world');
@ -377,6 +389,41 @@ begin
end; end;
procedure TTestWrapper.Capture;
begin
// Capture is a convenience method which puts a script's variables and
// functions in a global table variable. Useful for example when
// implementing a sandboxed API.
Lua.LoadFromString('message = "Hello world!"'#13#10 +
'function outputMessage()'#13#10 +
' print(message)'#13#10 +
'end', False, 'Script1');
Lua.Capture('Captured');
Lua.LoadFromString('print(Captured.message)'#13#10 +
'Captured.message = "Goodbye world!"'#13#10 +
'Captured.outputMessage()', True, 'Script2');
CheckEquals('Hello world!Goodbye world!', Printed.ToString);
end;
procedure TTestWrapper.DenyRequire;
begin
try
// This should fail, since we're not loading the Package library which
// adds the require function that can be considered a security risk.
Lua.LoadFromString('require("Test")');
Fail('ELuaException expected');
except
on E:Exception do
begin
CheckIs(E, ELuaException);
end;
end;
end;
initialization initialization
RegisterTest(TTestWrapper.Suite); RegisterTest(TTestWrapper.Suite);