siger-young updated this revision to Diff 367971. siger-young added a comment.
This update adds some tests for Lua LLDB module. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D108090/new/ https://reviews.llvm.org/D108090 Files: lldb/CMakeLists.txt lldb/bindings/lua/CMakeLists.txt lldb/bindings/lua/lua-typemaps.swig lldb/bindings/lua/lua-wrapper.swig lldb/bindings/lua/lua.swig lldb/source/API/liblldb-private.exports lldb/source/API/liblldb.exports lldb/test/API/lit.site.cfg.py.in lldb/test/API/lldbtest.py lldb/test/API/lua_api/Makefile lldb/test/API/lua_api/TestBreakpointAPI.lua lldb/test/API/lua_api/TestComprehensive.lua lldb/test/API/lua_api/TestFileHandle.lua lldb/test/API/lua_api/TestLuaAPI.py lldb/test/API/lua_api/TestProcessAPI.lua lldb/test/API/lua_api/lua_lldb_test.lua lldb/test/API/lua_api/main.c
Index: lldb/test/API/lua_api/main.c =================================================================== --- /dev/null +++ lldb/test/API/lua_api/main.c @@ -0,0 +1,35 @@ +#include <stdio.h> + +void BFunction() +{ +} + +void AFunction() +{ + printf("I am a function.\n"); +} + +int main(int argc, const char *argv[]) +{ + int inited = 0xDEADBEEF; + int sum = 0; + if(argc > 1) + { + for(int i = 0; i < argc; i++) + { + puts(argv[i]); + } + if(argc > 2) + { + return argc; + } + } + AFunction(); + for(int i = 1; i <= 100; i++) + { + BFunction(); + sum += i; + } + printf("sum = %d\n", sum); + return 0; +} Index: lldb/test/API/lua_api/lua_lldb_test.lua =================================================================== --- /dev/null +++ lldb/test/API/lua_api/lua_lldb_test.lua @@ -0,0 +1,107 @@ +-- Import all functions of luaunit +EXPORT_ASSERT_TO_GLOBALS = true +require('luaunit') + +-- Make lldb available in global +lldb = require('lldb') + +-- Global helper functions +function read_file_non_empty_lines(f) + local lines = {} + while true do + local line = f:read('*l') + if not line then break end + if line ~= '\n' then table.insert(lines, line) end + end + return lines +end + +function split_lines(str) + local lines = {} + for line in str:gmatch("[^\r\n]+") do + table.insert(lines, line) + end + return lines +end + +function get_stopped_threads(process, reason) + local threads = {} + for i = 0, process:GetNumThreads() - 1 do + local t = process:GetThreadAtIndex(i) + if t:IsValid() and t:GetStopReason() == reason then + table.insert(threads, t) + end + end + return threads +end + +function get_stopped_thread(process, reason) + local threads = get_stopped_threads(process, reason) + if #threads ~= 0 then return threads[1] + else return nil end +end + +-- Test helper + +local _M = {} +local _m = {} + +local _mt = { __index = _m } + +function _M.create_test(name, exe, output, input) + print('[lldb/lua] Doing test ' .. name) + exe = exe or os.getenv('TEST_EXE') + output = output or os.getenv('TEST_OUTPUT') + input = input or os.getenv('TEST_INPUT') + lldb.SBDebugger.Initialize() + local debugger = lldb.SBDebugger.Create() + -- Ensure that debugger is created + assertNotNil(debugger) + assertTrue(debugger:IsValid()) + + debugger:SetAsync(false) + + local lua_language = debugger:GetScriptingLanguage('lua') + assertNotNil(lua_language) + debugger:SetScriptLanguage(lua_language) + + local test = setmetatable({ + output = output, + input = input, + name = name, + exe = exe, + debugger = debugger + }, _mt) + _G[name] = test + return test +end + +function _m:create_target(exe) + local target + if not exe then exe = self.exe end + target = self.debugger:CreateTarget(exe) + -- Ensure that target is created + assertNotNil(target) + assertTrue(target:IsValid()) + return target +end + +function _m:handle_command(command, collect) + if collect == nil then collect = true end + if collect then + local ret = lldb.SBCommandReturnObject() + local interpreter = self.debugger:GetCommandInterpreter() + assertTrue(interpreter:IsValid()) + interpreter:HandleCommand(command, ret) + self.debugger:GetOutputFile():Flush() + self.debugger:GetErrorFile():Flush() + assertTrue(ret:Succeeded()) + return ret:GetOutput() + else + self.debugger:HandleCommand(command) + self.debugger:GetOutputFile():Flush() + self.debugger:GetErrorFile():Flush() + end +end + +return _M Index: lldb/test/API/lua_api/TestProcessAPI.lua =================================================================== --- /dev/null +++ lldb/test/API/lua_api/TestProcessAPI.lua @@ -0,0 +1,59 @@ +_T = require('lua_lldb_test').create_test('TestProcessAPI') + +function _T:TestProcessLaunchSimple() + local target = self:create_target() + local args = { 'arg1', 'arg2', 'arg3' } + local process = target:LaunchSimple( + -- argv + args, + -- envp + nil, + -- working directory + nil + ) + assertTrue(process:IsValid()) + local stdout = process:GetSTDOUT(1000) + assertEquals(split_lines(stdout), {self.exe, table.unpack(args)}) +end + +function _T:TestProcessLaunch() + local target = self:create_target() + local args = { 'arg1', 'arg2', 'arg3' } + local error = lldb.SBError() + local f = io.open(self.output, 'w') + f:write() + f:close() + local process = target:Launch( + -- listener + self.debugger:GetListener(), + -- argv + args, + -- envp + nil, + -- stdin + nil, + -- stdout + self.output, + -- stderr + nil, + -- working directory + nil, + -- launch flags + 0, + -- stop at entry + true, + -- error + error + ) + assertTrue(error:Success()) + assertTrue(process:IsValid()) + local threads = get_stopped_threads(process, lldb.eStopReasonSignal) + assertTrue(#threads ~= 0) + local continue = process:Continue() + assertTrue(continue:Success()) + local f = io.open(self.output, 'r') + assertEquals(read_file_non_empty_lines(f), {self.exe, table.unpack(args)}) + f:close() +end + +os.exit(LuaUnit.run()) Index: lldb/test/API/lua_api/TestLuaAPI.py =================================================================== --- /dev/null +++ lldb/test/API/lua_api/TestLuaAPI.py @@ -0,0 +1,65 @@ +""" +Test Lua API wrapper +""" + +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil +import lit.util + + +class TestLuaAPI(TestBase): + + mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True + + def get_tests(self): + tests = [] + for filename in os.listdir(): + # Ignore dot files and excluded tests. + if filename.startswith('.'): + continue + + # Ignore files that don't start with 'Test'. + if not filename.startswith('Test'): + continue + + if not os.path.isdir(filename): + base, ext = os.path.splitext(filename) + if ext == '.lua': + tests.append(filename) + return tests + + def test_lua_api(self): + if "LUA_EXECUTABLE" not in os.environ or len(os.environ["LUA_EXECUTABLE"]) == 0: + self.skipTest("Lua API tests could not find Lua executable.") + return + lua_executable = os.environ["LUA_EXECUTABLE"] + + self.build() + test_exe = self.getBuildArtifact("a.out") + test_output = self.getBuildArtifact("output") + test_input = self.getBuildArtifact("input") + + lua_lldb_cpath = "%s/lua/?.so" % configuration.lldb_libs_dir + + lua_prelude = "package.cpath = '%s;' .. package.cpath" % lua_lldb_cpath + + lua_env = { + "TEST_EXE": os.path.join(self.getBuildDir(), test_exe), + "TEST_OUTPUT": os.path.join(self.getBuildDir(), test_output), + "TEST_INPUT": os.path.join(self.getBuildDir(), test_input) + } + + for lua_test in self.get_tests(): + cmd = [lua_executable] + ["-e", lua_prelude] + [lua_test] + out, err, exitCode = lit.util.executeCommand(cmd, env=lua_env) + + # Redirect Lua output + print(out) + print(err, file=sys.stderr) + + self.assertTrue( + exitCode == 0, + "Lua test '%s' failure." % lua_test + ) Index: lldb/test/API/lua_api/TestFileHandle.lua =================================================================== --- /dev/null +++ lldb/test/API/lua_api/TestFileHandle.lua @@ -0,0 +1,37 @@ +_T = require('lua_lldb_test').create_test('TestFileHandle') + +function _T:TestLegacyFileOutScript() + local f = io.open(self.output, 'w') + self.debugger:SetOutputFile(f) + self:handle_command('script print(1+1)') + self.debugger:GetOutputFileHandle():write('FOO\n') + self.debugger:GetOutputFileHandle():flush() + f:close() + + f = io.open(self.output, 'r') + assertEquals(read_file_non_empty_lines(f), {'2', 'FOO'}) + f:close() +end + +function _T:TestLegacyFileOut() + local f = io.open(self.output, 'w') + self.debugger:SetOutputFile(f) + self:handle_command('p/x 3735928559', false) + f:close() + + f = io.open(self.output, 'r') + assertStrContains(f:read('*l'), 'deadbeef') + f:close() +end + +function _T:TestLegacyFileErr() + local f = io.open(self.output, 'w') + self.debugger:SetErrorFile(f) + self:handle_command('lol', false) + + f = io.open(self.output, 'r') + assertStrContains(f:read('*l'), 'is not a valid command') + f:close() +end + +os.exit(LuaUnit.run()) Index: lldb/test/API/lua_api/TestComprehensive.lua =================================================================== --- /dev/null +++ lldb/test/API/lua_api/TestComprehensive.lua @@ -0,0 +1,96 @@ +_T = require('lua_lldb_test').create_test('TestComprehensive') + +function _T:Test0_CreateTarget() + self.target = self:create_target() + assertTrue(self.target:IsValid()) +end + +function _T:Test1_Breakpoint() + self.main_bp = self.target:BreakpointCreateByName('main', 'a.out') + self.loop_bp = self.target:BreakpointCreateByLocation('main.c', 28) + assertTrue(self.main_bp:IsValid() and self.main_bp:GetNumLocations() == 1) + assertTrue(self.loop_bp:IsValid() and self.loop_bp:GetNumLocations() == 1) +end + +function _T:Test2_Launch() + local error = lldb.SBError() + self.args = { 'arg' } + self.process = self.target:Launch( + self.debugger:GetListener(), + self.args, + nil, + nil, + self.output, + nil, + nil, + 0, + false, + error + ) + assert(error:Success()) + assertTrue(self.process:IsValid()) +end + +function _T:Test3_BreakpointFindVariables() + -- checking "argc" value + local thread = get_stopped_thread(self.process, lldb.eStopReasonBreakpoint) + assertNotNil(thread) + assertTrue(thread:IsValid()) + local frame = thread:GetFrameAtIndex(0) + assertTrue(frame:IsValid()) + local error = lldb.SBError() + local var_argc = frame:FindVariable('argc') + assertTrue(var_argc:IsValid()) + local var_argc_value = var_argc:GetValueAsSigned(error, 0) + assertTrue(error:Success()) + assertEquals(var_argc_value, 2) + + -- checking "inited" value + local continue = self.process:Continue() + assertTrue(continue:Success()) + thread = get_stopped_thread(self.process, lldb.eStopReasonBreakpoint) + assertNotNil(thread) + assertTrue(thread:IsValid()) + frame = thread:GetFrameAtIndex(0) + assertTrue(frame:IsValid()) + error = lldb.SBError() + local var_inited = frame:FindVariable('inited') + assertTrue(var_inited:IsValid()) + self.var_inited = var_inited + local var_inited_value = var_inited:GetValueAsUnsigned(error, 0) + assertTrue(error:Success()) + assertEquals(var_inited_value, 0xDEADBEEF) +end + +function _T:Test3_RawData() + local error = lldb.SBError() + local address = self.var_inited:GetAddress() + assertTrue(address:IsValid()) + local size = self.var_inited:GetByteSize() + local raw_data = self.process:ReadMemory(address:GetOffset(), size, error) + assertTrue(error:Success()) + assertTrue(raw_data == "\xEF\xBE\xAD\xDE" or raw_data == "\xDE\xAD\xBE\xEF") +end + +function _T:Test4_ProcessExit() + self.loop_bp:SetAutoContinue(true) + local continue = self.process:Continue() + assertTrue(continue:Success()) + assertTrue(self.process:GetExitStatus() == 0) +end + +function _T:Test5_FileOutput() + local f = io.open(self.output, 'r') + assertEquals( + read_file_non_empty_lines(f), + { + self.exe, + table.unpack(self.args), + 'I am a function.', + 'sum = 5050' + } + ) + f:close() +end + +os.exit(LuaUnit.run()) Index: lldb/test/API/lua_api/TestBreakpointAPI.lua =================================================================== --- /dev/null +++ lldb/test/API/lua_api/TestBreakpointAPI.lua @@ -0,0 +1,52 @@ +_T = require('lua_lldb_test').create_test('TestBreakpointAPI') + +function _T:TestBreakpointIsValid() + local target = self:create_target() + local breakpoint = target:BreakpointCreateByName('AFunction', 'a.out') + assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1) + local did_delete = target:BreakpointDelete(breakpoint:GetID()) + assertTrue(did_delete) + local del_bkpt = target:FindBreakpointByID(breakpoint:GetID()) + assertFalse(del_bkpt:IsValid()) + assertFalse(breakpoint:IsValid()) +end + +function _T:TestTargetDelete() + local target = self:create_target() + local breakpoint = target:BreakpointCreateByName('AFunction', 'a.out') + assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1) + local location = breakpoint:GetLocationAtIndex(0) + assertTrue(location:IsValid()) + assertEquals(target, breakpoint:GetTarget()) + assertTrue(self.debugger:DeleteTarget(target)) + assertFalse(breakpoint:IsValid()) + assertFalse(location:IsValid()) +end + +function _T:TestBreakpointHitCount() + local target = self:create_target() + local breakpoint = target:BreakpointCreateByName('BFunction', 'a.out') + assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1) + breakpoint:SetAutoContinue(true) + target:LaunchSimple(nil, nil, nil) + assertEquals(breakpoint:GetHitCount(), 100) +end + +function _T:TestBreakpointFrame() + local target = self:create_target() + local breakpoint = target:BreakpointCreateByName('main', 'a.out') + assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1) + local process = target:LaunchSimple({ 'arg1', 'arg2' }, nil, nil) + local thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint) + assertNotNil(thread) + assertTrue(thread:IsValid()) + local frame = thread:GetFrameAtIndex(0) + assertTrue(frame:IsValid()) + local error = lldb.SBError() + local var_argc = frame:FindVariable('argc') + local var_argc_value = var_argc:GetValueAsSigned(error, 0) + assert(error:Success()) + assertEquals(var_argc_value, 3) +end + +os.exit(LuaUnit.run()) Index: lldb/test/API/lua_api/Makefile =================================================================== --- /dev/null +++ lldb/test/API/lua_api/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules Index: lldb/test/API/lldbtest.py =================================================================== --- lldb/test/API/lldbtest.py +++ lldb/test/API/lldbtest.py @@ -50,11 +50,17 @@ # build with. executable = test.config.python_executable + isLuaTest = testFile == test.config.lua_test_entry + # On Windows, the system does not always correctly interpret # shebang lines. To make sure we can execute the tests, add # python exe as the first parameter of the command. cmd = [executable] + self.dotest_cmd + [testPath, '-p', testFile] + if isLuaTest: + luaExecutable = test.config.lua_executable + cmd.extend(['--env', 'LUA_EXECUTABLE=%s' % luaExecutable]) + if 'lldb-repro-capture' in test.config.available_features or \ 'lldb-repro-replay' in test.config.available_features: reproducer_path = os.path.join( Index: lldb/test/API/lit.site.cfg.py.in =================================================================== --- lldb/test/API/lit.site.cfg.py.in +++ lldb/test/API/lit.site.cfg.py.in @@ -20,6 +20,8 @@ config.lldb_build_directory = "@LLDB_TEST_BUILD_DIRECTORY@" config.lldb_reproducer_directory = os.path.join("@LLDB_TEST_BUILD_DIRECTORY@", "reproducers") config.python_executable = "@Python3_EXECUTABLE@" +config.lua_executable = "@Lua_EXECUTABLE@" +config.lua_test_entry = "TestLuaAPI.py" config.dotest_args_str = "@LLDB_DOTEST_ARGS@" config.lldb_enable_python = @LLDB_ENABLE_PYTHON@ config.dotest_lit_args_str = None Index: lldb/source/API/liblldb.exports =================================================================== --- lldb/source/API/liblldb.exports +++ lldb/source/API/liblldb.exports @@ -2,3 +2,4 @@ _ZNK4lldb* init_lld* PyInit__lldb* +luaopen_lldb* Index: lldb/source/API/liblldb-private.exports =================================================================== --- lldb/source/API/liblldb-private.exports +++ lldb/source/API/liblldb-private.exports @@ -4,3 +4,4 @@ _ZNK12lldb_private* init_lld* PyInit__lldb* +luaopen_lldb* Index: lldb/bindings/lua/lua.swig =================================================================== --- lldb/bindings/lua/lua.swig +++ lldb/bindings/lua/lua.swig @@ -17,6 +17,10 @@ #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "../bindings/lua/lua-swigsafecast.swig" + +// required headers for typemaps +#include "lldb/Host/File.h" + using namespace lldb_private; using namespace lldb; %} Index: lldb/bindings/lua/lua-wrapper.swig =================================================================== --- lldb/bindings/lua/lua-wrapper.swig +++ lldb/bindings/lua/lua-wrapper.swig @@ -6,6 +6,19 @@ %} +%runtime %{ +#ifdef __cplusplus +extern "C" { +#endif + +void LLDBSwigLuaCallLuaLogOutputCallback(const char *str, void *baton); +int LLDBSwigLuaCloseFileHandle(lua_State *L); + +#ifdef __cplusplus +} +#endif +%} + %wrapper %{ // This function is called from Lua::CallBreakpointCallback @@ -88,5 +101,20 @@ return stop; } +SWIGEXPORT void +LLDBSwigLuaCallLuaLogOutputCallback(const char *str, void *baton) { + lua_State *L = (lua_State *)baton; + + lua_pushlightuserdata(L, (void *)&LLDBSwigLuaCallLuaLogOutputCallback); + lua_gettable(L, LUA_REGISTRYINDEX); + + // FIXME: There's no way to report errors back to the user + lua_pushstring(L, str); + lua_pcall(L, 1, 0, 0); +} + +int LLDBSwigLuaCloseFileHandle(lua_State *L) { + return luaL_error(L, "You cannot close a file handle used by lldb."); +} %} Index: lldb/bindings/lua/lua-typemaps.swig =================================================================== --- lldb/bindings/lua/lua-typemaps.swig +++ lldb/bindings/lua/lua-typemaps.swig @@ -12,7 +12,7 @@ // Primitive integer mapping %typemap(in,checkfn="lua_isinteger") TYPE -%{ $1 = (TYPE)lua_tointeger(L, $input); %} +%{ $1 = ($type)lua_tointeger(L, $input); %} %typemap(in,checkfn="lua_isinteger") const TYPE&($basetype temp) %{ temp=($basetype)lua_tointeger(L,$input); $1=&temp;%} %typemap(out) TYPE @@ -54,6 +54,7 @@ LLDB_NUMBER_TYPEMAP(long long); LLDB_NUMBER_TYPEMAP(unsigned long long); LLDB_NUMBER_TYPEMAP(signed long long); +LLDB_NUMBER_TYPEMAP(enum SWIGTYPE); %apply unsigned long { size_t }; %apply const unsigned long & { const size_t & }; @@ -77,7 +78,7 @@ %typemap(in) (char *dst, size_t dst_len) { $2 = luaL_checkinteger(L, $input); if ($2 <= 0) { - return luaL_error(L, "Positive integer expected"); + return luaL_error(L, "Positive integer expected"); } $1 = (char *) malloc($2); } @@ -86,6 +87,9 @@ // as char data instead of byte data. %typemap(in) (void *char_buf, size_t size) = (char *dst, size_t dst_len); +// Also SBProcess::ReadMemory. +%typemap(in) (void *buf, size_t size) = (char *dst, size_t dst_len); + // Return the char buffer. Discarding any previous return result %typemap(argout) (char *dst, size_t dst_len) { lua_pop(L, 1); // Blow away the previous result @@ -102,4 +106,195 @@ // as char data instead of byte data. %typemap(argout) (void *char_buf, size_t size) = (char *dst, size_t dst_len); +// Also SBProcess::ReadMemory. +%typemap(argout) (void *buf, size_t size) = (char *dst, size_t dst_len); + +//===----------------------------------------------------------------------===// + +// Typemap for handling a snprintf-like API like SBThread::GetStopDescription. + +%typemap(in) (char *dst_or_null, size_t dst_len) { + $2 = luaL_checkinteger(L, $input); + if ($2 <= 0) { + return luaL_error(L, "Positive integer expected"); + } + $1 = (char *)malloc($2); +} + +%typemap(argout) (char *dst_or_null, size_t dst_len) { + lua_pop(L, 1); // Blow away the previous result + lua_pushlstring(L, (const char *)$1, $result); + free($1); + // SWIG_arg was already incremented +} + +//===----------------------------------------------------------------------===// + +// Typemap for handling SBModule::GetVersion + +%typemap(in) (uint32_t *versions, uint32_t num_versions) { + $2 = 99; + $1 = (uint32_t *)malloc(sizeof(uint32_t) * $2); +} + +%typemap(argout) (uint32_t *versions, uint32_t num_versions) { + uint32_t count = result; + if (count >= $2) + count = $2; + lua_newtable(L); + int i = 0; + while (i++ < count) { + lua_pushinteger(L, $1[i - 1]); + lua_seti(L, -2, i); + } + SWIG_arg++; + free($1); +} + +//===----------------------------------------------------------------------===// + +// Typemap for handling SBDebugger::SetLoggingCallback + +%typemap(in) (lldb::LogOutputCallback log_callback, void *baton) { + $1 = LLDBSwigLuaCallLuaLogOutputCallback; + $2 = (void *)L; + + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_settop(L, 2); + + lua_pushlightuserdata(L, (void *)&LLDBSwigLuaCallLuaLogOutputCallback); + lua_insert(L, 2); + lua_settable(L, LUA_REGISTRYINDEX); +} + +//===----------------------------------------------------------------------===// + +// Typemap for handling SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len) + +%typemap(in) (const char *cstr, uint32_t cstr_len) { + $1 = (char *)luaL_checklstring(L, $input, (size_t *)&$2); +} + +// Typemap for handling SBProcess::PutSTDIN + +%typemap(in) (const char *src, size_t src_len) { + $1 = (char *)luaL_checklstring(L, $input, &$2); +} + +// Typemap for handling SBProcess::WriteMemory, SBTarget::GetInstructions... + +%typemap(in) (const void *buf, size_t size), + (const void *data, size_t data_len) { + $1 = (void *)luaL_checklstring(L, $input, &$2); +} + +//===----------------------------------------------------------------------===// + +// Typemap for handling char ** in SBTarget::LaunchSimple, SBTarget::Launch... + +%typemap(in) char ** { + if (lua_istable(L, $input)) { + size_t size = lua_rawlen(L, $input); + $1 = (char **)malloc((size + 1) * sizeof(char *)); + int i = 0, j = 0; + while (i++ < size) { + lua_rawgeti(L, $input, i); + if (!lua_isstring(L, -1)) { + lua_pop(L, 1); + return luaL_error(L, "List should contains only strings"); + } + $1[j++] = (char *)lua_tostring(L, -1); + lua_pop(L, 1); + } + $1[j] = 0; + } else if (lua_isnil(L, $input)) { + $1 = NULL; + } +} + +%typemap(freearg) char ** { + free((char *) $1); +} + +%typecheck(SWIG_TYPECHECK_STRING_ARRAY) char ** { + $1 = (lua_istable(L, $input) || lua_isnil(L, $input)); +} + +//===----------------------------------------------------------------------===// + +// Typemap for file handles (e.g. used in SBDebugger::SetOutputFile) + +%typemap(in) lldb::FileSP { + luaL_Stream *p = (luaL_Stream *)luaL_checkudata(L, $input, LUA_FILEHANDLE); + lldb::FileSP file_sp; + file_sp = std::make_shared<lldb_private::NativeFile>(p->f, false); + if (!file_sp->IsValid()) + return luaL_error(L, "Invalid file"); + $1 = file_sp; +} + +%typecheck(SWIG_TYPECHECK_POINTER) lldb::FileSP { + $1 = (lua_isuserdata(L, $input)) && + (luaL_testudata(L, $input, LUA_FILEHANDLE) != nullptr); +} + +// Typemap for file handles (e.g. used in SBDebugger::GetOutputFileHandle) + +%typemap(out) lldb::FileSP { + lldb::FileSP &sp = $1; + if (sp && sp->IsValid()) { + luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream)); + p->closef = &LLDBSwigLuaCloseFileHandle; + p->f = sp->GetStream(); + luaL_setmetatable(L, LUA_FILEHANDLE); + SWIG_arg++; + } +} + +//===----------------------------------------------------------------------===// + +// Typemap for SBData::CreateDataFromUInt64Array, SBData::SetDataFromUInt64Array ... + +%typemap(in) (uint64_t* array, size_t array_len), + (uint32_t* array, size_t array_len), + (int64_t* array, size_t array_len), + (int32_t* array, size_t array_len), + (double* array, size_t array_len) { + if (lua_istable(L, $input)) { + $2 = lua_rawlen(L, $input); + $1 = ($1_ltype)malloc(($2) * sizeof($*1_type)); + int i = 0, j = 0; + while (i++ < $2) { + lua_rawgeti(L, $input, i); + $1[j++] = ($*1_ltype)lua_tonumber(L, -1); + lua_pop(L, 1); + } + } else if (lua_isnil(L, $input)) { + $1 = NULL; + $2 = 0; + } +} + +%typemap(in) (uint64_t* array, size_t array_len), + (uint32_t* array, size_t array_len), + (int64_t* array, size_t array_len), + (int32_t* array, size_t array_len), + (double* array, size_t array_len) { + free($1); +} + +//===----------------------------------------------------------------------===// + +// Typemap for SBCommandReturnObject::PutCString + +%typemap(in) (const char *string, int len) { + if (lua_isnil(L, $input)) { + $1 = NULL; + $2 = 0; + } + else { + $1 = (char *)luaL_checklstring(L, $input, (size_t *)&$2); + } +} + //===----------------------------------------------------------------------===// Index: lldb/bindings/lua/CMakeLists.txt =================================================================== --- lldb/bindings/lua/CMakeLists.txt +++ lldb/bindings/lua/CMakeLists.txt @@ -17,3 +17,34 @@ add_custom_target(swig_wrapper_lua ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapLua.cpp ) + +function(create_lua_package swig_target working_dir pkg_dir) + cmake_parse_arguments(ARG "NOINIT" "" "FILES" ${ARGN}) + add_custom_command(TARGET ${swig_target} POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -E make_directory ${pkg_dir} + WORKING_DIRECTORY ${working_dir}) +endfunction() + +function(finish_swig_lua swig_target lldb_lua_bindings_dir lldb_lua_target_dir) + add_custom_target(${swig_target} ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -E make_directory ${lldb_lua_target_dir} + DEPENDS swig_wrapper_lua + COMMENT "Lua LLDB Python API") + create_relative_symlink(${swig_target} + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/liblldb${CMAKE_SHARED_LIBRARY_SUFFIX}" + ${lldb_lua_target_dir} + "lldb.so") + set(lldb_lua_library_target "${swig_target}-library") + add_custom_target(${lldb_lua_library_target}) + add_dependencies(${lldb_lua_library_target} ${swig_target}) + install(DIRECTORY ${lldb_lua_target_dir}/ + DESTINATION ${LLDB_LUA_INSTALL_PATH} + COMPONENT ${lldb_lua_library_target}) + # Ensure we do the Lua post-build step when building lldb. + add_dependencies(lldb ${swig_target}) + + set(lldb_lua_library_install_target "install-${lldb_lua_library_target}") + add_llvm_install_targets(${lldb_lua_library_install_target} + COMPONENT ${lldb_lua_library_target} + DEPENDS ${lldb_lua_library_target}) +endfunction() Index: lldb/CMakeLists.txt =================================================================== --- lldb/CMakeLists.txt +++ lldb/CMakeLists.txt @@ -51,6 +51,24 @@ CACHE STRING "Path where Python modules are installed, relative to install prefix") endif () +if (LLDB_ENABLE_LUA) + # FIXME: Lua 5.3 is hardcoded but it should support 5.3+! + find_program(Lua_EXECUTABLE lua5.3) + if (NOT Lua_EXECUTABLE) + message(FATAL_ERROR "Lua executable not found") + else () + execute_process( + COMMAND ${Lua_EXECUTABLE} + -e "for w in string.gmatch(package.cpath, ';?([^;]+);?') do \ + if string.match(w, '%?%.so') then print(string.sub(w, 1, #w - 4)) break end end" + OUTPUT_VARIABLE LLDB_LUA_DEFAULT_INSTALL_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE) + file(TO_CMAKE_PATH ${LLDB_LUA_DEFAULT_INSTALL_PATH} LLDB_LUA_DEFAULT_INSTALL_PATH) + set(LLDB_LUA_INSTALL_PATH ${LLDB_LUA_DEFAULT_INSTALL_PATH} + CACHE STRING "Path where Lua modules are installed") + endif () +endif () + if (LLDB_ENABLE_PYTHON OR LLDB_ENABLE_LUA) add_subdirectory(bindings) endif () @@ -94,6 +112,12 @@ finish_swig_python("lldb-python" "${lldb_python_bindings_dir}" "${lldb_python_target_dir}") endif() +if (LLDB_ENABLE_LUA) + set(lldb_lua_target_dir "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_INSTALL_LIBDIR}/lua") + get_target_property(lldb_lua_bindings_dir swig_wrapper_lua BINARY_DIR) + finish_swig_lua("lldb-lua" "${lldb_lua_bindings_dir}" "${lldb_lua_target_dir}") +endif() + option(LLDB_INCLUDE_TESTS "Generate build targets for the LLDB unit tests." ${LLVM_INCLUDE_TESTS}) if(LLDB_INCLUDE_TESTS) add_subdirectory(test)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits