diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt --- a/lldb/CMakeLists.txt +++ b/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 () diff --git a/lldb/bindings/lua/CMakeLists.txt b/lldb/bindings/lua/CMakeLists.txt --- a/lldb/bindings/lua/CMakeLists.txt +++ b/lldb/bindings/lua/CMakeLists.txt @@ -17,3 +17,10 @@ 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() diff --git a/lldb/bindings/lua/lua-typemaps.swig b/lldb/bindings/lua/lua-typemaps.swig --- a/lldb/bindings/lua/lua-typemaps.swig +++ b/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,178 @@ // 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); + $1[j++] = (char *)lua_tostring(L, -1); + lua_pop(L, 1); + } + $1[j] = 0; + } else if (lua_isnil(L, $input)) { + $1 = NULL; + } +} + +%typecheck(SWIG_TYPECHECK_STRING_ARRAY) char ** { + $1 = (lua_istable(L, $input) || lua_isnil(L, $input)) ? 1 : 0; +} + +//===----------------------------------------------------------------------===// + +// 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); + FileSP file_sp; + file_sp = std::make_shared(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) ? 1 : 0; +} + +// 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 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); + } +} + +//===----------------------------------------------------------------------===// \ No newline at end of file diff --git a/lldb/bindings/lua/lua-wrapper.swig b/lldb/bindings/lua/lua-wrapper.swig --- a/lldb/bindings/lua/lua-wrapper.swig +++ b/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."); +} %} diff --git a/lldb/bindings/lua/lua.swig b/lldb/bindings/lua/lua.swig --- a/lldb/bindings/lua/lua.swig +++ b/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; %} diff --git a/lldb/source/API/liblldb-private.exports b/lldb/source/API/liblldb-private.exports --- a/lldb/source/API/liblldb-private.exports +++ b/lldb/source/API/liblldb-private.exports @@ -4,3 +4,4 @@ _ZNK12lldb_private* init_lld* PyInit__lldb* +luaopen_lldb* diff --git a/lldb/source/API/liblldb.exports b/lldb/source/API/liblldb.exports --- a/lldb/source/API/liblldb.exports +++ b/lldb/source/API/liblldb.exports @@ -2,3 +2,4 @@ _ZNK4lldb* init_lld* PyInit__lldb* +luaopen_lldb*