axmol/templates/lua-template-runtime/frameworks/runtime-src/Classes/runtime/lua_debugger.c

3504 lines
150 KiB
C

/* lua_debugger.c */
#include "lua.h"
#include "lauxlib.h"
#include "lua_debugger.h"
/* debugger */
static const char *lua_m_debugger =
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Debugger using DBGp protocol.\n"
"-------------------------------------------------------------------------------\n"
"-- The module returns a single init function which takes 6 parameters (IDEHOST, IDEPORT, IDEKEY, TRANSPORT, PLATFORM, WORKINGDIR).\n"
"--\n"
"-- IDEHOST: the host name or the ip address of the DBGP server (so your ide)\n"
"-- if HOST is nil, the DBGP_IDEHOST env var is used.\n"
"-- if the env var is nil, the default value '127.0.0.1' is used.\n"
"--\n"
"-- IDEPORT: the port of the DBGP server (must be configure in the IDE)\n"
"-- if PORT is nil, the DBGP_IDEPORT env var is used.\n"
"-- if the env var is nil, the default value '10000' is used.\n"
"--\n"
"-- IDEIDEKEY: a string which is used as session key\n"
"-- if IDEKEY is nil, the DBGP_IDEKEY env var is used.\n"
"-- if the env var is nil, the default value 'luaidekey' is used.\n"
"--\n"
"-- TRANSPORT: (advanced optional parameter) the module name of which implement the transport interface used to do the connection with the server.\n"
"-- by default the debugger use an internal implementation based on luasocket, but if can not use it, you could implement or use another transport layer implementation.\n"
"-- if TRANSPORT is nil, the DBGP_TRANSPORT env var is used.\n"
"-- if the env var is nil, the default value 'debugger.transport.luasocket' is used : this is the default implementation based on luasocket.\n"
"--\n"
"-- PLATFORM: (advanced optional parameter) 'unix' or 'win32' string which define the kind of platform on which the program to debug is executed.\n"
"-- by default the debugger will try to guess it and surely success, if for some reasons it fails you could help it by precise the execution platform.\n"
"-- if PLATFORM is nil, the DBGP_PLATFORM env var is used.\n"
"-- if the env var is nil, the debugger will try to guess it.\n"
"--\n"
"-- WORKINGDIR: (advanced optional parameter) the working directory in which the program to debug is executed.\n"
"-- by default the debugger will try to guess it and surely success, if for some reasons it fails you could help it by precise the working directory.\n"
"-- if WORKINGDIR is nil, the DBGP_WORKINGDIR env var is used.\n"
"-- if the env var is nil, the debugger will try to guess it.\n"
"--\n"
"-------------------------------------------------------------------------------\n"
"-- Known Issues:\n"
"-- * Functions cannot be created using the debugger and then called in program because their environment is mapped directly to\n"
"-- a debugger internal structure which cannot be persisted (i.e. used outside of the debug_hook).\n"
"-- * The DLTK client implementation does not handle context for properties. As a workaround, the context is encoded into the\n"
"-- fullname attribute of each property and is used likewise in property_get commands. The syntax is \"<context ID>|<full name>\"\n"
"-- * Dynamic code (compiled with load or loadstring) is not handled (the debugger will step over it, like C code)\n"
"-- Design notes:\n"
"-- * The whole debugger state is kept in a (currently) unique session table in order to ease eventual adaptation to a multi-threaded\n"
"-- model, as DBGp needs one connection per thread.\n"
"-- * Full names of properties are base64 encoded because they can contain arbitrary data (spaces, escape characters, ...), this makes\n"
"-- command parsing munch easier and faster\n"
"-- * This debugger supports asynchronous commands: any command can be done at any time, but some of them (continuations) can lead to\n"
"-- inconsistent states. In addition, this have a quite big overhead (~66%), if performance is an issue, a custom command to disable\n"
"-- async mode could be done.\n"
"-- * All commands are implemented in table commands, see this comments on this table to additional details about commands implementation\n"
"-- * The environments in which are evaluated user code (property_* and eval commands, conditional breakpoints, ...) is a read/write\n"
"-- mapping of the local environment of a given stack level (can be accessed with variable names). See Context for additional details.\n"
"-- Context instantiation is pooled inside a debugging loop with ContextManager (each stack level is instantiated only once).\n"
"-- * Output redirection is done by redefining print and some values inside the io table. See \"Output redirection handling\" for details.\n"
"-- Todo list:\n"
"-- * Override I/O in init function instead of on module loading.\n"
"-- * Allow to break programatically (debugger.break()).\n"
"-- * Break-on-error feature (break if an error is thrown and there is no pcall in stack to handle it).\n"
"-- * Use new 5.2 facilities to provide informations about function (arguments names, vararg, ...)\n"
"-- * Allow to see ... content for vararg functions (5.2 only)\n"
"-- * Inspect LuaJIT C data (http://lua-users.org/lists/lua-l/2011-02/msg01012.html)-- /!\\ This file is auto-generated. Do not alter manually /!\\\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Submodules body\n"
"--------------------------------------------------------------------------------\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.transport.apr\n"
"package.preload[\"debugger.transport.apr\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Apache Portable Runtime backend for DBGP debugger.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local apr = require \"apr\"\n"
"\n"
"-- base 64 wrapping\n"
"function b64_wrap(src)\n"
" local t = {}\n"
" local b64_src = mime.b64(src)\n"
" for i=1, #b64_src, 76 do t[#t+1] = b64_src:sub(i, i+75)..\"\\r\\n\" end\n"
" return table.concat(t)\n"
"end\n"
"\n"
"-- implements a subset of LuaSocket API using APR\n"
"local SOCKET_MT = {\n"
" connect = function(self, address, port) return self.skt:connect(address, port) end,\n"
" receive = function(self, n) return self.skt:read(n) end, -- only numeric read is used\n"
" send = function(self, data) return self.skt:write(data) end,\n"
" close = function(self) return self.skt:close() end,\n"
" settimeout = function(self, sec)\n"
" if sec == nil then self.skt:timeout_set(true)\n"
" elseif sec == 0 then self.skt:timeout_set(false)\n"
" else self.skt:timeout_set(math.floor(sec * 1000000)) end\n"
" end\n"
"}\n"
"SOCKET_MT.__index = SOCKET_MT\n"
"\n"
"return {\n"
" create = function()\n"
" local skt, err = apr.socket_create('tcp')\n"
" if not skt then return nil, err end\n"
" return setmetatable({skt = skt}, SOCKET_MT)\n"
" end,\n"
" sleep = apr.sleep, -- exact same API as LuaSocket\n"
"\n"
" -- Base64 related functions\n"
" --- Encodes a string into Base64 with line wrapping\n"
" -- @param data (string) data to encode\n"
" -- @return base64 encoded string\n"
" b64 = function(data)\n"
" t = {}\n"
" local b64_data = apr.base64_encode(data)\n"
" for i=1, #b64_data, 76 do t[#t+1] = b64_data:sub(i, i+75)..\"\\r\\n\" end\n"
" return table.concat(t)\n"
" end,\n"
"\n"
" --- Encodes a string into Base64, without any extra parsing (wrapping, ...)\n"
" -- @param data (string) data to encode\n"
" -- @return decoded string\n"
" rawb64 = apr.base64_encode,\n"
"\n"
" --- Decodes base64 data\n"
" -- @param data (string) base64 encoded data\n"
" -- @return decoded string\n"
" unb64 = apr.base64_decode,\n"
"}\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.transport.apr\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.transport.luasocket\n"
"package.preload[\"debugger.transport.luasocket\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- LuaSocket backend for DBGP debugger.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"-- in order to be as lightweight as possible with Luasocket, core API is used\n"
"-- directly (to no add yet another layer)\n"
"\n"
"--FIXME: remove this hack as soon as luasocket officially support 5.2\n"
"if _VERSION == \"Lua 5.2\" then\n"
" table.getn = function(t) return t and #t end\n"
"end\n"
"\n"
"local socket = require \"socket\"\n"
"local mime = require \"mime\"\n"
"local ltn12 = require \"ltn12\"\n"
"local reg = debug.getregistry()\n"
"\n"
"\n"
"return {\n"
" create = socket.tcp,\n"
" sleep = socket.sleep,\n"
"\n"
" -- Base64 related functions\n"
" --- Encodes a string into Base64 with line wrapping\n"
" -- @param data (string) data to encode\n"
" -- @return base64 encoded string\n"
" b64 = function(data)\n"
" local filter = ltn12.filter.chain(mime.encode(\"base64\"), mime.wrap(\"base64\"))\n"
" local sink, output = ltn12.sink.table()\n"
" ltn12.pump.all(ltn12.source.string(data), ltn12.sink.chain(filter, sink))\n"
" return table.concat(output)\n"
" end,\n"
"\n"
" --- Encodes a string into Base64, without any extra parsing (wrapping, ...)\n"
" -- @param data (string) data to encode\n"
" -- @return decoded string\n"
" rawb64 = function(data)\n"
" return (mime.b64(data)) -- first result of the low-level function is fine here\n"
" end,\n"
"\n"
" --- Decodes base64 data\n"
" -- @param data (string) base64 encoded data\n"
" -- @return decoded string\n"
" unb64 = function(data)\n"
" return (mime.unb64(data)) -- first result of the low-level function is fine here\n"
" end,\n"
"}\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.transport.luasocket\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.transport.luasocket_sched\n"
"package.preload[\"debugger.transport.luasocket_sched\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- LuaSocket with LuaSched backend for DBGP debugger.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"-- As LuaShed totally hides blocking functions, this module MUST be loaded on the very start of the program\n"
"-- (before loading sched) to catch references to blocking functions.\n"
"\n"
"local socketcore = require\"socket.core\"\n"
"local debug = require \"debug\"\n"
"local reg = debug.getregistry()\n"
"\n"
"local blockingcreate = socketcore.tcp\n"
"local blockingsleep = socketcore.sleep\n"
"\n"
"local blockingconnect = reg[\"tcp{master}\"].__index.connect\n"
"local blockingreceive = reg[\"tcp{client}\"].__index.receive\n"
"local blockingsend = reg[\"tcp{client}\"].__index.send\n"
"local blockingsettimeout = reg[\"tcp{master}\"].__index.settimeout\n"
"local blockingclose = reg[\"tcp{master}\"].__index.close\n"
"\n"
"-- we cannot set a new metatable directly on socket object, so wrap it into a new table\n"
"-- and forward all calls.\n"
"local blockingtcp = {\n"
" connect = function(self, address, port) return blockingconnect(self.skt, address, port) end,\n"
" receive = function(self, n) return blockingreceive(self.skt, n) end,\n"
" send = function(self, data) return blockingsend(self.skt, data) end,\n"
" settimeout = function(self, sec) return blockingsettimeout(self.skt, sec) end,\n"
" close = function(self) return blockingclose(self.skt) end,\n"
"}\n"
"\n"
"blockingtcp.__index = blockingtcp\n"
"\n"
"local mime = require \"mime\"\n"
"local ltn12 = require \"ltn12\"\n"
"\n"
"-- verify that the socket function are the real ones and not sched not blocking versions\n"
"assert(debug.getinfo(blockingcreate, \"S\").what == \"C\", \"The debugger needs the real socket functions !\")\n"
"-- cleanup the package.loaded table (socket.core adds socket field into it)\n"
"package.loaded.socket = nil\n"
"\n"
"return {\n"
" create = function() return setmetatable({ skt = blockingcreate() }, blockingtcp) end,\n"
" sleep = blockingsleep,\n"
"\n"
" -- Base64 related functions\n"
" --- Encodes a string into Base64 with line wrapping\n"
" -- @param data (string) data to encode\n"
" -- @return base64 encoded string\n"
" b64 = function(data)\n"
" local filter = ltn12.filter.chain(mime.encode(\"base64\"), mime.wrap(\"base64\"))\n"
" local sink, output = ltn12.sink.table()\n"
" ltn12.pump.all(ltn12.source.string(data), ltn12.sink.chain(filter, sink))\n"
" return table.concat(output)\n"
" end,\n"
"\n"
" --- Encodes a string into Base64, without any extra parsing (wrapping, ...)\n"
" -- @param data (string) data to encode\n"
" -- @return decoded string\n"
" rawb64 = function(data)\n"
" return (mime.b64(data)) -- first result of the low-level function is fine here\n"
" end,\n"
"\n"
" --- Decodes base64 data\n"
" -- @param data (string) base64 encoded data\n"
" -- @return decoded string\n"
" unb64 = function(data)\n"
" return (mime.unb64(data)) -- first result of the low-level function is fine here\n"
" end,\n"
"}\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.transport.luasocket_sched\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.commands\n"
"package.preload[\"debugger.commands\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Commands handlers for DBGp protocol.\n"
"-------------------------------------------------------------------------------\n"
"-- Debugger command functions. Each function handle a different command.\n"
"-- A command function is called with 3 arguments\n"
"-- 1. the debug session instance\n"
"-- 2. the command arguments as table\n"
"-- 3. the command data, if any\n"
"-- The result is either :\n"
"-- * true (or any value evaluated to true) : the debugger will resume the execution of the application (continuation command)\n"
"-- * false : only in async mode, the debugger WILL wait for further commands instead of continuing (typically, break command)\n"
"-- * nil/no return : in sync mode, the debugger will wait for another command. In async mode the debugger will continue the execution\n"
"\n"
"local cowrap, coyield = coroutine.wrap, coroutine.yield\n"
"local debug = require \"debug\"\n"
"\n"
"local core = require \"debugger.core\"\n"
"local dbgp = require \"debugger.dbgp\"\n"
"local util = require \"debugger.util\"\n"
"local platform = require \"debugger.platform\"\n"
"local introspection = require \"debugger.introspection\"\n"
"local context = require \"debugger.context\"\n"
"local log = util.log\n"
"\n"
"local M = { } -- command handlers table\n"
"\n"
"--- Gets the coroutine behind an id\n"
"-- Throws errors on unknown identifiers\n"
"-- @param coro_id (string or nil) Coroutine identifier or nil (current coroutine)\n"
"-- @return Coroutine instance or nil (if coro_id was nil or if coroutine is the current coroutine)\n"
"local function get_coroutine(self, coro_id)\n"
" if coro_id then\n"
" local coro = dbgp.assert(399, core.active_coroutines.from_id[tonumber(coro_id)], \"No such coroutine\")\n"
" dbgp.assert(399, coroutine.status(coro) ~= \"dead\", \"Coroutine is dead\")\n"
" if coro ~= self.coro[1] then return util.ForeignThread(coro) end\n"
" end\n"
" return self.coro\n"
"end\n"
"\n"
"M[\"break\"] = function(self, args)\n"
" self.state = \"break\"\n"
" -- send response to previous command\n"
" core.previous_context_response(self)\n"
" -- and then response to break command itself\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = \"break\", transaction_id = args.i, success = 1 } } )\n"
" return false\n"
"end\n"
"\n"
"function M.status(self, args)\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = {\n"
" command = \"status\",\n"
" reason = \"ok\",\n"
" status = self.state,\n"
" transaction_id = args.i } } )\n"
"end\n"
"\n"
"function M.stop(self, args)\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = {\n"
" command = \"stop\",\n"
" reason = \"ok\",\n"
" status = \"stopped\",\n"
" transaction_id = args.i } } )\n"
" self.skt:close()\n"
" os.exit(1)\n"
"end\n"
"\n"
"function M.feature_get(self, args)\n"
" local name = args.n\n"
" local response = util.features[name] or (not not M[name])\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = {\n"
" command = \"feature_get\",\n"
" feature_name = name,\n"
" supported = response and \"1\" or \"0\",\n"
" transaction_id = args.i },\n"
" tostring(response) } )\n"
"end\n"
"\n"
"function M.feature_set(self, args)\n"
" local name, value = args.n, args.v\n"
" local success = pcall(function() util.features[name] = value end)\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = {\n"
" command = \"feature_set\",\n"
" feature = name,\n"
" success = success and 1 or 0,\n"
" transaction_id = args.i\n"
" } } )\n"
"end\n"
"\n"
"function M.typemap_get(self, args)\n"
" local function gentype(name, type, xsdtype)\n"
" return { tag = \"map\", atts = { name = name, type = type, [\"xsi:type\"] = xsdtype } }\n"
" end\n"
"\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = {\n"
" command = \"typemap_get\",\n"
" transaction_id = args.i,\n"
" [\"xmlns:xsi\"] = \"http://www.w3.org/2001/XMLSchema-instance\",\n"
" [\"xmlns:xsd\"] = \"http://www.w3.org/2001/XMLSchema\",\n"
" },\n"
" gentype(\"nil\", \"null\"),\n"
" gentype(\"boolean\", \"bool\", \"xsd:boolean\"),\n"
" gentype(\"number\", \"float\", \"xsd:float\"),\n"
" gentype(\"string\", \"string\", \"xsd:string\"),\n"
" gentype(\"function\", \"resource\"),\n"
" gentype(\"userdata\", \"resource\"),\n"
" gentype(\"thread\", \"resource\"),\n"
" gentype(\"table\", \"hash\"),\n"
" gentype(\"sequence\", \"array\"), -- artificial type to represent sequences (1-n continuous indexes)\n"
" gentype(\"multival\", \"array\"), -- used to represent return values\n"
" } )\n"
"end\n"
"\n"
"function M.run(self)\n"
" core.prev_break_line = nil\n"
" return true\n"
"end\n"
"\n"
"function M.step_over(self)\n"
" core.events.register(\"over\")\n"
" return true\n"
"end\n"
"\n"
"function M.step_out(self)\n"
" core.events.register(\"out\")\n"
" return true\n"
"end\n"
"\n"
"function M.step_into(self)\n"
" core.events.register(\"into\")\n"
" return true\n"
"end\n"
"\n"
"function M.eval(self, args, data)\n"
" log(\"DEBUG\", \"Going to eval \"..data)\n"
" local result, err, success\n"
" local env = self.stack(self.coro, 0)\n"
" -- first, try to load as expression\n"
" -- DBGp does not support stack level here, see http://bugs.activestate.com/show_bug.cgi?id=81178\n"
" local func, err = util.loadin(\"return \"..data, env)\n"
"\n"
" -- if it is not an expression, try as statement (assignment, ...)\n"
" if not func then\n"
" func, err = util.loadin(data, env)\n"
" end\n"
"\n"
" if func then\n"
" success, result = pcall(function() return introspection.Multival(func()) end)\n"
" if not success then err = result end\n"
" end\n"
"\n"
" local response = { tag = \"response\", attr = { command = \"eval\", transaction_id = args.i } }\n"
" if not err then\n"
" local nresults = result.n\n"
" if nresults == 1 then result = result[1] end\n"
"\n"
" -- store result for further use (property_*)\n"
" -- TODO: this could be optimized: this is only used for Expressions view and totally useless for interactive console,\n"
" -- so storing result or not could be set by an argument\n"
" local idx\n"
" if nresults > 0 then\n"
" local cache = env[context.Context[-1]]\n"
" idx = #cache + 1\n"
" cache[idx] = result\n"
" end\n"
"\n"
" -- As of Lua 5.1, the maximum stack size (and result count) is 8000, this limit is used to fit all results in one page\n"
" response[1] = introspection.make_property(-1, result, idx or \"\", nil, 1, 8000, 0, nil)\n"
" response.attr.success = 1\n"
" else\n"
" response.attr.success = 0\n"
" response[1] = dbgp.make_error(206, err)\n"
" end\n"
" dbgp.send_xml(self.skt, response)\n"
"end\n"
"\n"
"function M.breakpoint_set(self, args, data)\n"
" if args.o and not core.breakpoints.hit_conditions[args.o] then dbgp.error(200, \"Invalid hit_condition operator: \"..args.o) end\n"
" local filename, lineno = args.f, tonumber(args.n)\n"
" --if it is not in current project path, ignore the breakpoint_set command, send empty response to avoid blocking (add by guanyu)\n"
" if not filename:find(platform.base_dir,1,true) then\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = \"breakpoint_set\", transaction_id = args.i} } )\n"
" return nil;\n"
" end\n"
" local bp = {\n"
" type = args.t,\n"
" state = args.s or \"enabled\",\n"
" temporary = args.r == \"1\", -- \"0\" or nil makes this property false\n"
" hit_count = 0,\n"
" filename = filename,\n"
" lineno = lineno,\n"
" hit_value = tonumber(args.h or 0),\n"
" hit_condition = args.o or \">=\",\n"
" }\n"
"\n"
" if args.t == \"conditional\" then\n"
" bp.expression = data\n"
" -- the expression is compiled only once\n"
" bp.condition = dbgp.assert(207, loadstring(\"return (\" .. data .. \")\"))\n"
" elseif args.t ~= \"line\" then dbgp.error(201, \"BP type \" .. args.t .. \" not yet supported\") end\n"
"\n"
" local bpid = core.breakpoints.insert(bp)\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = \"breakpoint_set\", transaction_id = args.i, state = bp.state, id = bpid } } )\n"
"end\n"
"\n"
"function M.breakpoint_get(self, args)\n"
" dbgp.send_xml(self.skt, { tag = \"response\",\n"
" attr = { command = \"breakpoint_get\", transaction_id = args.i },\n"
" dbgp.assert(205, core.breakpoints.get_xml(tonumber(args.d))) })\n"
"end\n"
"\n"
"function M.breakpoint_list(self, args)\n"
" local bps = { tag = \"response\", attr = { command = \"breakpoint_list\", transaction_id = args.i } }\n"
" for id, bp in pairs(core.breakpoints.get()) do bps[#bps + 1] = core.breakpoints.get_xml(id) end\n"
" dbgp.send_xml(self.skt, bps)\n"
"end\n"
"\n"
"function M.breakpoint_update(self, args)\n"
" local bp = core.breakpoints.get(tonumber(args.d))\n"
" if not bp then dbgp.error(205, \"No such breakpint \"..args.d) end\n"
" if args.o and not core.breakpoints.hit_conditions[args.o] then dbgp.error(200, \"Invalid hit_condition operator: \"..args.o) end\n"
" local response = { tag = \"response\", attr = { command = \"breakpoint_update\", transaction_id = args.i } }\n"
" local needUpdate = false\n"
" local oldline = bp.lineno\n"
" if tonumber(args.n) ~= tonumber(bp.lineno) then\n"
" needUpdate = true\n"
" end\n"
" bp.state = args.s or bp.state\n"
" bp.lineno = tonumber(args.n or bp.lineno)\n"
" bp.hit_value = tonumber(args.h or bp.hit_value)\n"
" bp.hit_condition = args.o or bp.hit_condition\n"
" if needUpdate then\n"
" core.breakpoints.update(oldline, bp)\n"
" end\n"
" dbgp.send_xml(self.skt, response)\n"
"end\n"
"\n"
"function M.breakpoint_remove(self, args)\n"
" local response = { tag = \"response\", attr = { command = \"breakpoint_remove\", transaction_id = args.i } }\n"
" -- args.d may be nil, because the breakpoint does not belong to current project (add by guanyu)\n"
" if args.d and not core.breakpoints.remove(tonumber(args.d)) then dbgp.error(205, \"No such breakpint \"..args.d) end\n"
" dbgp.send_xml(self.skt, response)\n"
"end\n"
"\n"
"function M.stack_depth(self, args)\n"
" local depth = 0\n"
" local coro = get_coroutine(self, args.o)\n"
" for level = 0, math.huge do\n"
" local info = coro:getinfo(level, \"St\")\n"
" if not info then break end -- end of stack\n"
" depth = depth + 1\n"
" if info.istailcall then depth = depth + 1 end -- a 'fake' level is added in that case\n"
" if info.what == \"main\" then break end -- levels below main chunk are not interesting\n"
" end\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = \"stack_depth\", transaction_id = args.i, depth = depth} } )\n"
"end\n"
"\n"
"function M.stack_get(self, args) -- TODO: dynamic code\n"
" -- special URIs to identify unreachable stack levels\n"
" local what2uri = {\n"
" tail = \"tailreturn:/\",\n"
" C = \"ccode:/\",\n"
" }\n"
"\n"
" local function make_level(info, level)\n"
" local attr = { level = level, where = info.name, type=\"file\" }\n"
" local uri = platform.get_uri(info.source)\n"
" if uri and info.currentline then -- reachable level\n"
" attr.filename = uri\n"
" attr.lineno = info.currentline\n"
" else\n"
" attr.filename = what2uri[info.what] or \"unknown:/\"\n"
" attr.lineno = -1\n"
" end\n"
" return { tag = \"stack\", attr = attr }\n"
" end\n"
"\n"
" local node = { tag = \"response\", attr = { command = \"stack_get\", transaction_id = args.i} }\n"
" local coro = get_coroutine(self, args.o)\n"
"\n"
" if args.d then\n"
" local stack_level = tonumber(args.d)\n"
" node[#node+1] = make_level(coro:getinfo(stack_level, \"nSl\"), stack_level)\n"
" else\n"
" for i=0, math.huge do\n"
" local info = coro:getinfo(i, \"nSlt\")\n"
" if not info then break end\n"
" node[#node+1] = make_level(info, i)\n"
" -- add a fake level of stack for tail calls (tells user that the function has not been called directly)\n"
" if info.istailcall then\n"
" node[#node+1] = { tag = \"stack\", attr = { level=i, type=\"file\", filename=\"tailreturn:/\", lineno=-1 } }\n"
" end\n"
" if info.what == \"main\" then break end -- levels below main chunk are not interesting\n"
" end\n"
" end\n"
"\n"
" dbgp.send_xml(self.skt, node)\n"
"end\n"
"\n"
"--- Lists all active coroutines.\n"
"-- Returns a list of active coroutines with their id (an arbitrary string) to query stack and properties. The id is\n"
"-- guaranteed to be unique and stable for all coroutine life (they can be reused as long as coroutine exists).\n"
"-- Others commands such as stack_get or property_* commands takes an additional -o switch to query a particular cOroutine.\n"
"-- If the switch is not given, running coroutine will be used.\n"
"-- In case of error on coroutines (most likely coroutine not found or dead), an error 399 is thrown.\n"
"-- Note there is an important limitation due to Lua 5.1 coroutine implementation: you cannot query main \"coroutine\" from\n"
"-- another one, so main coroutine is not in returned list (this will change with Lua 5.2).\n"
"--\n"
"-- This is a non-standard command. The returned XML has the following strucuture:\n"
"-- <response command=\"coroutine_list\" transaction_id=\"0\">\n"
"-- <coroutine name=\"<some printtable name>\" id=\"<coroutine id>\" running=\"0|1\" />\n"
"-- ...\n"
"-- </response>\n"
"function M.coroutine_list(self, args)\n"
" local running = self.coro[1]\n"
" local coroutines = { tag = \"response\", attr = { command = \"coroutine_list\", transaction_id = args.i } }\n"
" -- as any operation on main coroutine will fail, it is not yet listed\n"
" -- coroutines[1] = { name = \"coroutine\", attr = { id = 0, name = \"main\", running = (running == nil) and \"1\" or \"0\" } }\n"
" for id, coro in pairs(core.active_coroutines.from_id) do\n"
" if id ~= \"n\" then\n"
" coroutines[#coroutines + 1] = { tag = \"coroutine\", attr = { id = id, name = tostring(coro), running = (coro == running) and \"1\" or \"0\" } }\n"
" end\n"
" end\n"
" dbgp.send_xml(self.skt, coroutines)\n"
"end\n"
"\n"
"function M.context_names(self, args)\n"
" local coro = get_coroutine(self, args.o)\n"
" local level = tonumber(args.d or 0)\n"
" local info = coro:getinfo(level, \"f\") or dbgp.error(301, \"No such stack level \"..tostring(level))\n"
"\n"
" -- All contexts are always passed, even if empty. This is how DLTK expect context, what about others ?\n"
" local contexts = {\n"
" tag = \"response\", attr = { command = \"context_names\", transaction_id = args.i },\n"
" { tag = \"context\", attr = { name = \"Local\", id = 0 } },\n"
" { tag = \"context\", attr = { name = \"Upvalue\", id = 2 } },\n"
" { tag = \"context\", attr = { name = \"Global\", id = 1 } },\n"
" }\n"
"\n"
" dbgp.send_xml(self.skt, contexts)\n"
"end\n"
"\n"
"function M.context_get(self, args)\n"
" local cxt_num = tonumber(args.c or 0)\n"
" local cxt_id = context.Context[cxt_num] or dbgp.error(302, \"No such context: \"..tostring(cxt_num))\n"
" local level = tonumber(args.d or 0)\n"
" local coro = get_coroutine(self, args.o)\n"
" local cxt = self.stack(coro, level)\n"
"\n"
" local properties = { tag = \"response\", attr = { command = \"context_get\", transaction_id = args.i, context = context} }\n"
" -- iteration over global is different (this could be unified in Lua 5.2 thanks to __pairs metamethod)\n"
" for name, val in (cxt_num == 1 and next or getmetatable(cxt[cxt_id]).iterator), cxt[cxt_id], nil do\n"
" -- the DBGp specification is not clear about the depth of a context_get, but a recursive get could be *really* slow in Lua\n"
" properties[#properties + 1] = introspection.make_property(cxt_num, val, name, nil, 0, util.features.max_children, 0,\n"
" util.features.max_data, cxt_num ~= 1)\n"
" end\n"
"\n"
" dbgp.send_xml(self.skt, properties)\n"
"end\n"
"\n"
"-------------------------------------------------------------------------------\n"
"-- Property_* commands\n"
"-------------------------------------------------------------------------------\n"
"-- This in the environment in which properties are get or set.\n"
"-- It notably contain a collection of proxy table which handle transparentely get/set operations on special fields\n"
"-- and the cache of complex keys.\n"
"local property_evaluation_environment = {\n"
" key_cache = introspection.key_cache,\n"
" metatable = setmetatable({ }, {\n"
" __index = function(self, tbl) return getmetatable(tbl) end,\n"
" __newindex = function(self, tbl, mt) return setmetatable(tbl, mt) end,\n"
" }),\n"
" environment = util.eval_env,\n"
"}\n"
"-- to allows to be set as metatable\n"
"property_evaluation_environment.__index = property_evaluation_environment\n"
"\n"
"function M.property_get(self, args)\n"
" --TODO BUG ECLIPSE TOOLSLINUX-99 352316\n"
" local cxt_num, name = assert(util.unb64(args.n):match(\"^(%-?%d+)|(.*)$\"))\n"
" cxt_num = tonumber(args.c or cxt_num)\n"
" local cxt_id = context.Context[cxt_num] or dbgp.error(302, \"No such context: \"..tostring(cxt_num))\n"
" local level = tonumber(args.d or 0)\n"
" local coro = get_coroutine(self, args.o)\n"
" local size = tonumber(args.m or util.features.max_data)\n"
" if size < 0 then size = nil end -- call from property_value\n"
" local page = tonumber(args.p or 0)\n"
" local cxt = self.stack(coro, level)\n"
" local chunk = dbgp.assert(206, util.loadin(\"return \"..name, property_evaluation_environment))\n"
" local prop = select(2, dbgp.assert(300, pcall(chunk, cxt[cxt_id])))\n"
" local response = introspection.make_property(cxt_num, prop, name, name, util.features.max_depth, util.features.max_children, page, size)\n"
" -- make_property is not able to flag special variables as such when they are at root of property\n"
" -- special variables queries are in the form \"<proxy name>[(...)[a][b]<...>]\"\n"
" -- TODO: such parsing is far from perfect\n"
" if name:match(\"^[%w_]+%[.-%b[]%]$\") == name then response.attr.type = \"special\" end\n"
" dbgp.send_xml(self.skt, { tag = \"response\",\n"
" attr = { command = \"property_get\", transaction_id = args.i, context = context},\n"
" response } )\n"
"end\n"
"\n"
"function M.property_value(self, args)\n"
" args.m = -1\n"
" M.property_get(self, args)\n"
"end\n"
"\n"
"function M.property_set(self, args, data)\n"
" local cxt_num, name = assert(util.unb64(args.n):match(\"^(%-?%d+)|(.*)$\"))\n"
" cxt_num = tonumber(args.c or cxt_num)\n"
" local cxt_id = context.Context[cxt_num] or dbgp.error(302, \"No such context: \"..tostring(cxt_num))\n"
" local level = tonumber(args.d or 0)\n"
" local coro = get_coroutine(self, args.o)\n"
" local cxt = self.stack(coro, level)\n"
"\n"
" -- evaluate the new value in the local context\n"
" local value = select(2, dbgp.assert(206, pcall(dbgp.assert(206, util.loadin(\"return \"..data, cxt)))))\n"
"\n"
" local chunk = dbgp.assert(206, util.loadin(name .. \" = value\", setmetatable({ value = value }, property_evaluation_environment)))\n"
" dbgp.assert(206, pcall(chunk, cxt[cxt_id]))\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { success = 1, transaction_id = args.i } } )\n"
"end\n"
"\n"
"--TODO dynamic code handling\n"
"-- The DBGp specification is not clear about the line number meaning, this implementation is 1-based and numbers are inclusive\n"
"function M.source(self, args)\n"
" local path\n"
" if args.f then\n"
" path = platform.get_path(args.f)\n"
" else\n"
" path = self.coro:getinfo(0, \"S\").source\n"
" assert(path:sub(1,1) == \"@\")\n"
" path = path:sub(2)\n"
" end\n"
" local file, err = io.open(path)\n"
" if not file then dbgp.error(100, err, { success = 0 }) end\n"
" -- Try to identify compiled files\n"
" if file:read(1) == \"\\033\" then dbgp.error(100, args.f..\" is bytecode\", { success = 0 }) end\n"
" file:seek(\"set\", 0)\n"
"\n"
"\n"
" local srclines = { }\n"
" local beginline, endline, currentline = tonumber(args.b or 0), tonumber(args.e or math.huge), 0\n"
" for line in file:lines() do\n"
" currentline = currentline + 1\n"
" if currentline >= beginline and currentline <= endline then\n"
" srclines[#srclines + 1] = line\n"
" elseif currentline >= endline then break end\n"
" end\n"
" file:close()\n"
" srclines[#srclines + 1] = \"\" -- to add a trailing \\n\n"
"\n"
" dbgp.send_xml(self.skt, { tag = \"response\",\n"
" attr = { command = \"source\", transaction_id = args.i, success = 1},\n"
" util.b64(table.concat(srclines, \"\\n\")) })\n"
"end\n"
"\n"
"-- Factory for both stdout and stderr commands, change file descriptor in io\n"
"local function output_command_handler_factory(mode)\n"
" return function(self, args)\n"
" if args.c == \"0\" then -- disable\n"
" io[mode] = io.base[mode]\n"
" else\n"
" io[mode] = setmetatable({ skt = self.skt, mode = mode }, args.c == \"1\" and core.copy_output or core.redirect_output)\n"
" end\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = mode, transaction_id = args.i, success = \"1\" } } )\n"
" end\n"
"end\n"
"\n"
"M.stdout = output_command_handler_factory(\"stdout\")\n"
"M.stderr = output_command_handler_factory(\"stderr\")\n"
"\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.commands\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.context\n"
"package.preload[\"debugger.context\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Context handling: allows to evaluate code snippets in the context of a function\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local M = { }\n"
"\n"
"local dbgp = require \"debugger.dbgp\"\n"
"local util = require \"debugger.util\"\n"
"\n"
"-- make unique object to access contexts\n"
"local LOCAL, UPVAL, GLOBAL, EVAL, STORE, HANDLE = {}, {}, {}, {}, {}, {}\n"
"\n"
"local getglobals\n"
"if _VERSION == \"Lua 5.1\" then\n"
" getglobals = function(f) return getfenv(f) end\n"
"elseif _VERSION == \"Lua 5.2\" then\n"
" getglobals = function(f, cxt)\n"
" -- 'global' environment: this is either the local _ENV or upvalue _ENV. A special case happen when a\n"
" -- function does not reference any global variable: the upvalue _ENV may not exist at all. In this case,\n"
" -- global environment is not relevant so it is fixed to an empty table. Another solution would be to set it\n"
" -- to the environment from above stack level but it would require some overhead (especially if multiple\n"
" -- levels must be instantiated)\n"
" if cxt[LOCAL][STORE][\"_ENV\"] then return cxt[LOCAL][\"_ENV\"]\n"
" elseif cxt[UPVAL][STORE][\"_ENV\"] then return cxt[UPVAL][\"_ENV\"]\n"
" else return { } end\n"
" end\n"
"end\n"
"\n"
"--- Captures variables for given stack level. The capture contains local, upvalues and global variables.\n"
"-- The capture can be seen as a proxy table to the stack level: any value can be queried or set no matter\n"
"-- it is a local or an upvalue.\n"
"-- The individual local and upvalues context are also available and can be queried and modified with indexed notation too.\n"
"-- These objects are NOT persistant and must not be used outside the debugger loop which instanciated them !\n"
"M.Context = {\n"
" -- Context identifiers can be accessed by their DBGp context ID\n"
" [0] = LOCAL,\n"
" [1] = GLOBAL, -- DLTK internal ID for globals is 1\n"
" [2] = UPVAL,\n"
" -- EVAL is used to keep results from eval in cache in order to browse or modify them, results are stored as sequence\n"
" [-1] = EVAL,\n"
" STORE = STORE,\n"
"\n"
" -- gets a variable by name with correct handling of Lua scope chain\n"
" -- the or chain does not work here beacause __index metamethod would raise an error instead of returning nil\n"
" __index = function(self, k)\n"
" if self[LOCAL][STORE][k] then return self[LOCAL][k]\n"
" elseif self[UPVAL][STORE][k] then return self[UPVAL][k]\n"
" else return self[GLOBAL][k] end\n"
" end,\n"
" __newindex = function(self, k, v)\n"
" if self[LOCAL][STORE][k] then self[LOCAL][k] = v\n"
" elseif self[UPVAL][STORE][k] then self[UPVAL][k] = v\n"
" else self[GLOBAL][k] = v end\n"
" end,\n"
"\n"
" -- debug only !!\n"
" __tostring = function(self)\n"
" local buf = { \"Locals: \\n\" }\n"
" for k,v in pairs(self[LOCAL][STORE]) do\n"
" buf[#buf+1] = \"\\t\"..tostring(k)..\"(\"..tostring(v)..\")=\"..tostring(self[LOCAL][k])..\"\\n\"\n"
" end\n"
" buf[#buf+1] = \"Upvalues: \\n\"\n"
" for k,v in pairs(self[UPVAL][STORE]) do\n"
" buf[#buf+1] = \"\\t\"..tostring(k)..\"(\"..tostring(v)..\")=\"..tostring(self[UPVAL][k])..\"\\n\"\n"
" end\n"
" return table.concat(buf)\n"
" end,\n"
"\n"
" LocalContext = {\n"
" __index = function(self, k)\n"
" local index = self[STORE][k]\n"
" if not index then error(\"The local \"..tostring(k)..\" does not exists.\") end\n"
" local handle = self[HANDLE]\n"
" return select(2, handle.coro:getlocal(handle.level, index))\n"
" end,\n"
" __newindex = function(self, k, v)\n"
" local index = self[STORE][k]\n"
" if index then\n"
" local handle = self[HANDLE]\n"
" handle.coro:setlocal(handle.level, index, v)\n"
" else error(\"Cannot set local \" .. k) end\n"
" end,\n"
" -- Lua 5.2 ready :)\n"
" --__pairs = function(self) return getmetatable(self).iterator, self, nil end,\n"
" iterator = function(self, prev)\n"
" local key, index = next(self[STORE], prev)\n"
" if key then return key, self[key] else return nil end\n"
" end,\n"
" },\n"
"\n"
" UpvalContext = {\n"
" __index = function(self, k)\n"
" local index = self[STORE][k]\n"
" if not index then error(\"The local \"..tostring(k)..\" does not exitsts.\") end\n"
" return select(2, debug.getupvalue(self[HANDLE], index))\n"
" end,\n"
" __newindex = function(self, k, v)\n"
" local index = self[STORE][k]\n"
" if index then debug.setupvalue(self[HANDLE], index, v)\n"
" else error(\"Cannot set upvalue \" .. k) end\n"
" end,\n"
" -- Lua 5.2 ready :)\n"
" -- __pairs = function(self) return getmetatable(self).iterator, self, nil end,\n"
" iterator = function(self, prev)\n"
" local key, index = next(self[STORE], prev)\n"
" if key then return key, self[key] else return nil end\n"
" end,\n"
" },\n"
"\n"
" --- Context constructor\n"
" -- @param coro (util.*Thread instance) coroutine to map to\n"
" -- @param level (number) stack level do dump (script stack level)\n"
" new = function(cls, coro, level)\n"
" local locals, upvalues = {}, {}\n"
" if level < 0 then dbgp.error(301, \"No such stack level: \"..tostring(level)) end\n"
" local func = (coro:getinfo(level, \"f\") or dbgp.error(301, \"No such stack level: \"..tostring(level))).func\n"
"\n"
" -- local variables\n"
" for i=1, math.huge do\n"
" local name, val = coro:getlocal(level, i)\n"
" if not name then break\n"
" elseif name:sub(1,1) ~= \"(\" then -- skip internal values\n"
" locals[name] = i\n"
" end\n"
" end\n"
"\n"
" -- upvalues\n"
" for i=1, math.huge do\n"
" local name, val = debug.getupvalue(func, i)\n"
" if not name then break end\n"
" upvalues[name] = i\n"
" end\n"
"\n"
" locals = setmetatable({ [STORE] = locals, [HANDLE] = { level = level, coro = coro } }, cls.LocalContext)\n"
" upvalues = setmetatable({ [STORE] = upvalues, [HANDLE] = func }, cls.UpvalContext)\n"
"\n"
" local result = setmetatable({ [LOCAL] = locals, [UPVAL] = upvalues, [EVAL] = {} }, cls)\n"
" rawset(result, GLOBAL, getglobals(func, result))\n"
" return result\n"
" end,\n"
"}\n"
"\n"
"--- Handle caching of all instantiated context.\n"
"-- Returns a function which takes 2 parameters: thread and stack level and returns the corresponding context. If this\n"
"-- context has been already queried there is no new instantiation. A ContextManager is valid only during the debug loop\n"
"-- on which it has been instantiated. References to a ContextManager must be lost after the end of debug loop (so\n"
"-- threads can be collected).\n"
"-- If a context cannot be instantiated, an 301 DBGP error is thrown.\n"
"function M.ContextManager()\n"
" local cache = { }\n"
" return function(thread, level)\n"
" -- the real coroutine is used as key (not the wrapped instance as its unicity is not guaranteed)\n"
" -- otherwise, true is used to identify current thread (as nil is not a valid table key)\n"
" local key = thread[1] or true\n"
" local thread_contexts = cache[key]\n"
" if not thread_contexts then\n"
" thread_contexts = { }\n"
" cache[key] = thread_contexts\n"
" end\n"
"\n"
" local context = thread_contexts[level]\n"
" if not context then\n"
" context = M.Context:new(thread, level)\n"
" thread_contexts[level] = context\n"
" end\n"
"\n"
" return context\n"
" end\n"
"end\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.context\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.dbgp\n"
"package.preload[\"debugger.dbgp\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- DBGp protocol utility function (parsing, error handling, XML generation).\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local util = require \"debugger.util\"\n"
"\n"
"local error, setmetatable, type, pairs, ipairs, tostring, tconcat =\n"
" error, setmetatable, type, pairs, ipairs, tostring, table.concat\n"
"\n"
"local M = { }\n"
"\n"
"--- Parses the DBGp command arguments and returns it as a Lua table with key/value pairs.\n"
"-- For example, the sequence <code>-i 5 -j foo</code> will result in <code>{i=5, j=foo}</code>\n"
"-- @param cmd_args (string) sequence of arguments\n"
"-- @return table described above\n"
"function M.arg_parse(cmd_args)\n"
" local args = {}\n"
" for arg, val in cmd_args:gmatch(\"%-(%w) (%S+)\") do\n"
" args[arg] = val\n"
" end\n"
" return args\n"
"end\n"
"\n"
"--- Parses a command line\n"
"-- @return commande name (string)\n"
"-- @retrun arguments (table)\n"
"-- @return data (string, optional)\n"
"function M.cmd_parse(cmd)\n"
" local cmd_name, args, data\n"
" if cmd:find(\"--\", 1, true) then -- there is a data part\n"
" cmd_name, args, data = cmd:match(\"^(%S+)%s+(.*)%s+%-%-%s*(.*)$\")\n"
" data = util.unb64(data)\n"
" else\n"
" cmd_name, args = cmd:match(\"^(%S+)%s+(.*)$\")\n"
" end\n"
" return cmd_name, M.arg_parse(args), data\n"
"end\n"
"\n"
"--- Returns the packet read from socket, or nil followed by an error message on errors.\n"
"function M.read_packet(skt)\n"
" local size = {}\n"
" while true do\n"
" local byte, err = skt:receive(1)\n"
" if not byte then return nil, err end\n"
" if byte == \"\\000\" then break end\n"
" size[#size+1] = byte\n"
" end\n"
" return tconcat(size)\n"
"end\n"
"\n"
"M.DBGP_ERR_METATABLE = {} -- unique object used to identify DBGp errors\n"
"\n"
"--- Throws a correct DBGp error which result in a fine tuned error message to the server.\n"
"-- It is intended to be called into a command to make a useful error message, a standard Lua error\n"
"-- result in a code 998 error (internal debugger error).\n"
"-- @param code numerical error code\n"
"-- @param message message string (optional)\n"
"-- @param attr extra attributes to add to the response tag (optional)\n"
"function M.error(code, message, attr)\n"
" error(setmetatable({ code = code, message = message, attr = attr or {} }, M.DBGP_ERR_METATABLE), 2)\n"
"end\n"
"\n"
"--- Like core assert but throws a DBGp error if condition is not met.\n"
"-- @param code numerical error code thrown if condition is not met.\n"
"-- @param message condition to test\n"
"-- @param ... will be used as error message if test fails.\n"
"function M.assert(code, success, ...)\n"
" if not success then M.error(code, (...)) end\n"
" return success, ...\n"
"end\n"
"\n"
"-- -----------------\n"
"-- Outgoing data\n"
"-- -----------------\n"
"local xmlattr_specialchars = { ['\"'] = \"&quot;\", [\"<\"] = \"&lt;\", [\"&\"] = \"&amp;\" }\n"
"--- Very basic XML generator\n"
"-- Generates a XML string from a Lua Object Model (LOM) table.\n"
"-- See http://matthewwild.co.uk/projects/luaexpat/lom.html\n"
"function M.lom2str(xml)\n"
" local pieces = { } -- string buffer\n"
"\n"
" local function generate(node)\n"
" pieces[#pieces + 1] = \"<\"..node.tag\n"
" pieces[#pieces + 1] = \" \"\n"
" -- attribute ordering is not honored here\n"
" for attr, val in pairs(node.attr or {}) do\n"
" if type(attr) == \"string\" then\n"
" pieces[#pieces + 1] = attr .. '=\"' .. tostring(val):gsub('[\"&<]', xmlattr_specialchars) .. '\"'\n"
" pieces[#pieces + 1] = \" \"\n"
" end\n"
" end\n"
" pieces[#pieces] = nil -- remove the last separator (useless)\n"
"\n"
" if node[1] then\n"
" pieces[#pieces + 1] = \">\"\n"
" for _, child in ipairs(node) do\n"
" if type(child) == \"table\" then generate(child)\n"
" else pieces[#pieces + 1] = \"<![CDATA[\" .. tostring(child) .. \"]]>\" end\n"
" end\n"
" pieces[#pieces + 1] = \"</\" .. node.tag .. \">\"\n"
" else\n"
" pieces[#pieces + 1] = \"/>\"\n"
" end\n"
" end\n"
"\n"
" generate(xml)\n"
" return tconcat(pieces)\n"
"end\n"
"\n"
"function M.send_xml(skt, resp)\n"
" if not resp.attr then resp.attr = {} end\n"
" resp.attr.xmlns = \"urn:debugger_protocol_v1\"\n"
"\n"
" local data = '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\\n'..M.lom2str(resp)\n"
" util.log(\"DEBUG\", \"Send \" .. data)\n"
" skt:send(tostring(#data)..\"\\000\"..data..\"\\000\")\n"
"end\n"
"\n"
"--- Return an XML tag describing a debugger error, with an optional message\n"
"-- @param code (number) error code (see DBGp specification)\n"
"-- @param msg (string, optional) textual description of error\n"
"-- @return table, suitable to be converted into XML\n"
"function M.make_error(code, msg)\n"
" local elem = { tag = \"error\", attr = { code = code } }\n"
" if msg then\n"
" elem[1] = { tostring(msg), tag = \"message\" }\n"
" end\n"
" return elem\n"
"end\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.dbgp\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.introspection\n"
"package.preload[\"debugger.introspection\"] = function(...)\n"
"-- ----------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Julien Desgats - initial API and implementation\n"
"-- ----------------------------------------------------------------------------\n"
"-- Properties generation. Generate a LOM table with data from introspection.\n"
"-- ----------------------------------------------------------------------------\n"
"\n"
"local debug = require \"debug\"\n"
"local platform = require \"debugger.platform\"\n"
"local util = require \"debugger.util\"\n"
"\n"
"local tostring, type, assert, next, rawget, getmetatable, setmetatable, getfenv, select, coyield, cocreate, costatus, coresume, sformat, tconcat =\n"
" tostring, type, assert, next, rawget, getmetatable, setmetatable, getfenv, select, coroutine.yield, coroutine.create, coroutine.status, coroutine.resume, string.format, table.concat\n"
"\n"
"local MULTIVAL_MT = { __tostring = function() return \"\" end }\n"
"local probes = { }\n"
"\n"
"-- ---------- --\n"
"-- Public API --\n"
"-- ---------- --\n"
"\n"
"---\n"
"-- Introspection logic. This module implements Lua objects introspection and\n"
"-- generates a [DBGP](http://xdebug.org/docs-dbgp.php) compatible\n"
"-- [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html) data scructure.\n"
"-- @module debugger.introspection\n"
"local M = { }\n"
"\n"
"---\n"
"-- Represent the actual data to send to the debugger.\n"
"-- Full XML specification can be found in [DBGP specification](http://xdebug.org/docs-dbgp.php#properties-variables-and-values).\n"
"-- Modifying properties after their generation is possible (as actual data serialization/sending is delayed)\n"
"-- but should be used with care. The XML structure uses the [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html)\n"
"-- format, refer to these documents to get more informations about fields.\n"
"--\n"
"-- In addition to table fields, it has an array part, `[1]` being the string representation (base64 encoded),\n"
"-- possibly followed by chlid properties (@{#DBGPProperty} themselves)\n"
"--\n"
"-- @field #string tag Always \"property\"\n"
"-- @field #table attr XML attributes, see DBGP specification\n"
"-- @type DBGPProperty\n"
"\n"
"---\n"
"-- Inpectors table, contain all inspector functions.\n"
"-- Keys are either type names (`string`, `number`, ...) or metatables\n"
"-- that have a custom inspector attached.\n"
"-- @field [parent=#debugger.introspection] #table inspectors\n"
"M.inspectors = { }\n"
"\n"
"---\n"
"-- Generate a DBGP property if needed. If data is in data pagination and recursion depth ranges,\n"
"-- and send a property to the debugger, otherwise drop current property.\n"
"-- @param #string name Property name (displayed in IDE)\n"
"-- @param #string typename Type name (displayed in IDE)\n"
"-- @param #string repr Value string representation\n"
"-- @param #DBGPProperty parent Parent property\n"
"-- @param #string fullname Lua expression used to get value back in further calls\n"
"-- @return #table description\n"
"-- @function [parent=#debugger.introspection] property\n"
"M.property = coyield\n"
"\n"
"---\n"
"-- Adds a probe that will be called for every unknown table/userdata.\n"
"-- @param #function probe Inspector function to call.\n"
"-- @function [parent=#debugger.introspection] add_probe\n"
"M.add_probe = function(probe) probes[#probes + 1] = probe end\n"
"\n"
"---\n"
"-- Inspects a Lua value by dispatching it to correct inspector. Inspector functions have the same API.\n"
"-- @param #string name Property name (will be displayed by IDE)\n"
"-- @param value Value to inspect\n"
"-- @param #table parent Parent property (LOM table of the )\n"
"-- @param #string fullname Expression used to retrieve `value` for further debugger calls\n"
"-- @return #DBGPProperty The inspected value as returned by @{debugger.introspection#debugger.introspection.property}.\n"
"-- @return #nil If the value has not been inspected\n"
"-- @function [parent=#debugger.introspection] inspect\n"
"M.inspect = function(name, value, parent, fullname)\n"
" return (M.inspectors[type(value)] or M.inspectors.default)(name, value, parent, fullname)\n"
"end\n"
"\n"
"-- ----------------- --\n"
"-- Utility functions --\n"
"-- ----------------- --\n"
"\n"
"local function default_inspector(name, value, parent, fullname)\n"
" return M.property(name, type(value), tostring(value), parent, fullname)\n"
"end\n"
"\n"
"-- Inspects types that can have a metatable (table and userdata). Returns\n"
"-- 1) generated property\n"
"-- 2) boolean indicating whether a custom inspector has been called (in that case, do not process value any further)\n"
"local function metatable_inspector(name, value, parent, fullname)\n"
" local mt = getmetatable(value)\n"
" do\n"
" -- find by metatable\n"
" local custom = M.inspectors[mt]\n"
" if custom then return custom(name, value, parent, fullname), true end\n"
" -- or else call probes\n"
" for i=1, #probes do\n"
" local prop = probes[i](name, value, parent, fullname)\n"
" if prop then return prop, true end\n"
" end\n"
" end\n"
"\n"
" local prop = default_inspector(name, value, parent, fullname)\n"
" if mt and prop then\n"
" local mtprop = M.inspect(\"metatable\", mt, prop, \"metatable[\"..prop.attr.fullname..\"]\")\n"
" if mtprop then mtprop.attr.type = \"special\" end\n"
" end\n"
" return prop, false\n"
"end\n"
"\n"
"local function fancy_func_repr(f, info)\n"
" local args = {}\n"
" for i=1, info.nparams do\n"
" args[i] = debug.getlocal(f, i)\n"
" end\n"
"\n"
" if info.isvararg then\n"
" args[#args+1] = \"...\"\n"
" end\n"
"\n"
" return \"function(\" .. tconcat(args, \", \") .. \")\"\n"
"end\n"
"\n"
"--- Generate a name siutable for table index syntax\n"
"-- @param name Key name\n"
"-- @return #string A table index style index\n"
"-- @usage generate_printable_key('foo') => '[\"foo\"]'\n"
"-- @usage generate_printable_key(12) => '[12]'\n"
"-- @usage generate_printable_key({}) => '[table: 0x12345678]\n"
"-- @function [parent=#debugger.introspection] generate_printable_key\n"
"local function generate_printable_key(name)\n"
" return \"[\" .. (type(name) == \"string\" and sformat(\"%q\", name) or tostring(name)) .. \"]\"\n"
"end\n"
"M.generate_printable_key = generate_printable_key\n"
"\n"
"-- Used to store complex keys (other than string and number) as they cannot be passed in text\n"
"-- For these keys, the resulting expression will not be the key itself but \"key_cache[...]\"\n"
"-- where key_cache must be mapped to this table to resolve key correctly.\n"
"M.key_cache = setmetatable({ n=0 }, { __mode = \"v\" })\n"
"\n"
"local function generate_key(name)\n"
" local tname = type(name)\n"
" if tname == \"string\" then return sformat(\"%q\", name)\n"
" elseif tname == \"number\" or tname == \"boolean\" then return tostring(name)\n"
" else -- complex key, use key_cache for lookup\n"
" local i = M.key_cache.n\n"
" M.key_cache[i] = name\n"
" M.key_cache.n = i+1\n"
" return \"key_cache[\"..tostring(i)..\"]\"\n"
" end\n"
"end\n"
"\n"
"--- Generate a usable fullname for a value.\n"
"-- Based on parent fullname and key value, return a valid Lua expression.\n"
"-- Key can be any value (as anything can act as table key). If it cannot\n"
"-- be serialized (only string, number and boolean can), it will be temporarly\n"
"-- stored in an internal cache to be retrieved later.\n"
"-- @param #string parent Parent fullname\n"
"-- @param key The child key to generate fullname for\n"
"-- @return #string A valid fullname expression\n"
"-- @function [parent=#debugger.introspection] make_fullname\n"
"local function make_fullname(parent, key)\n"
" return parent .. \"[\" .. generate_key(key) .. \"]\"\n"
"end\n"
"M.make_fullname = make_fullname\n"
"\n"
"-- ---------- --\n"
"-- Inspectors --\n"
"-- ---------- --\n"
"\n"
"M.inspectors.number = default_inspector\n"
"M.inspectors.boolean = default_inspector\n"
"M.inspectors[\"nil\"] = default_inspector\n"
"M.inspectors.userdata = default_inspector\n"
"M.inspectors.thread = default_inspector\n"
"M.inspectors.default = default_inspector -- allows 3rd party inspectors to use the default inspector if needed\n"
"\n"
"M.inspectors.userdata = function(name, value, parent, fullname)\n"
" return (metatable_inspector(name, value, parent, fullname)) -- drop second return value\n"
"end\n"
"\n"
"M.inspectors.string = function(name, value, parent, fullname)\n"
" -- escape linebreaks as \\n and not as \\<0x0A> like %q does\n"
" return M.property(name, \"string\", sformat(\"%q\", value):gsub(\"\\\\\\n\", \"\\\\n\"), parent, fullname)\n"
"end\n"
"\n"
"M.inspectors[\"function\"] = function(name, value, parent, fullname)\n"
" local info = debug.getinfo(value, \"nSflu\")\n"
" local prop\n"
" if info.what ~= \"C\" then\n"
" -- try to create a fancy representation if possible\n"
" local repr = info.nparams and fancy_func_repr(value, info) or tostring(value)\n"
" if info.source:sub(1,1) == \"@\" then\n"
" repr = repr .. \"\\n\" .. platform.get_uri(\"@\" .. info.source) .. \"\\n\" .. tostring(info.linedefined)\n"
" end\n"
" prop = M.property(name, \"function (Lua)\", repr, parent, fullname)\n"
" else\n"
" prop = M.property(name, \"function\", tostring(value), parent, fullname)\n"
" end\n"
" if not prop then return nil end\n"
"\n"
" -- (5.1 only) environment is dumped only if it is different from global environment\n"
" -- TODO: this is not a correct behavior: environment should be dumped if is different from current stack level one\n"
" local fenv = getfenv and getfenv(value)\n"
" if fenv and fenv ~= getfenv(0) then\n"
" local fenvprop = M.inspect(\"environment\", fenv, prop, \"environment[\"..prop.attr.fullname..\"]\")\n"
" if fenvprop then fenvprop.attr.type = \"special\" end\n"
" end\n"
"\n"
" return prop\n"
"end\n"
"\n"
"\n"
"M.inspectors.table = function(name, value, parent, fullname)\n"
" local prop, iscustom = metatable_inspector(name, value, parent, fullname)\n"
" if not prop or iscustom then return prop end\n"
"\n"
" -- iterate over table values and detect arrays at the same time\n"
" -- next is used to circumvent __pairs metamethod in 5.2\n"
" local isarray, i = true, 1\n"
" for k,v in next, value, nil do\n"
" M.inspect(generate_printable_key(k), v, prop, make_fullname(fullname, k))\n"
" -- array detection: keys should be accessible by 1..n keys\n"
" isarray = isarray and rawget(value, i) ~= nil\n"
" i = i + 1\n"
" end\n"
" -- empty tables are considered as tables\n"
" if isarray and i > 1 then prop.attr.type = \"sequence\" end\n"
"\n"
" return prop\n"
"end\n"
"\n"
"M.inspectors[MULTIVAL_MT] = function(name, value, parent, fullname)\n"
" if value.n == 1 then\n"
" -- return directly the value as result\n"
" return M.inspect(name, value[1], parent, fullname)\n"
" else\n"
" -- wrap values inside a multival container\n"
" local prop = M.property(name, \"multival\", \"\", parent, fullname)\n"
" if not prop then return nil end\n"
" for i=1, value.n do\n"
" M.inspect(generate_printable_key(i), value[i], prop, fullname .. \"[\" .. i .. \"]\")\n"
" end\n"
" return prop\n"
" end\n"
"end\n"
"\n"
"-- ------------ --\n"
"-- Internal API --\n"
"-- ------------ --\n"
"\n"
"-- Used to inspect \"multival\" or \"vararg\" values. The typical use is to pack function result(s) in a single\n"
"-- value to inspect. The Multival instances can be passed to make_property as a single value, they will be\n"
"-- correctly reported to debugger\n"
"function M.Multival(...)\n"
" return setmetatable({ n=select(\"#\", ...), ... }, MULTIVAL_MT)\n"
"end\n"
"\n"
"--- Makes a property form a name/value pair (and fullname). This is an **internal** function, and should not be used by 3rd party inspectors.\n"
"-- @param #number cxt_id Context ID in which this value resides (workaround bug 352316)\n"
"-- @param value The value to debug\n"
"-- @param name The name associated with value, passed through tostring, so it can be anything\n"
"-- @param #string fullname A Lua expression to eval to get that property again (if nil, computed automatically)\n"
"-- @param #number depth The maximum property depth (recursive calls)\n"
"-- @param #number pagesize maximum children to include\n"
"-- @param #number page The page to generate (0 based)\n"
"-- @param #number size_limit Optional, if set, the maximum size of the string representation (in bytes)\n"
"-- @param #boolean safe_name If true, does not encode the name as table key\n"
"-- @return #DBGPProperty root property\n"
"-- @function [parent=#debugger.introspection] make_property\n"
"--TODO BUG ECLIPSE TOOLSLINUX-99 352316 : as a workaround, context is encoded into the fullname property\n"
"M.make_property = function(cxt_id, value, name, fullname, depth, pagesize, page, size_limit, safe_name)\n"
" fullname = fullname or \"(...)[\" .. generate_key(name) .. \"]\"\n"
" if not safe_name then name = generate_printable_key(name) end\n"
"\n"
" local generator = cocreate(function() return M.inspect(name, value, nil, fullname) end)\n"
" local propstack = { }\n"
" local rootnode\n"
" local catchthis = true\n"
" local nodestoskip = page * pagesize -- nodes to skip at root level to respect pagination\n"
" local fullname_prefix = tostring(cxt_id)..\"|\"\n"
"\n"
" while true do\n"
" local succes, name, datatype, repr, parent, fullname = assert(coresume(generator, catchthis and propstack[#propstack] or nil))\n"
" -- finalize and pop all finished properties\n"
" while propstack[#propstack] ~= parent do\n"
" local topop = propstack[#propstack]\n"
" topop.attr.fullname = util.rawb64(fullname_prefix .. topop.attr.fullname)\n"
" propstack[#propstack] = nil\n"
" end\n"
" if costatus(generator) == \"dead\" then break end\n"
"\n"
" local prop = {\n"
" tag = \"property\",\n"
" attr = {\n"
" children = 0,\n"
" pagesize = pagesize,\n"
" page = parent and 0 or page,\n"
" type = datatype,\n"
" name = name,\n"
" fullname = fullname,\n"
" encoding = \"base64\",\n"
" size = #repr,\n"
" },\n"
" util.b64(size_limit and repr:sub(1, size_limit) or repr)\n"
" }\n"
"\n"
" if parent then\n"
" parent.attr.children = 1\n"
" parent.attr.numchildren = (parent.attr.numchildren or 0) + 1\n"
" -- take pagination into accont to know if node needs to be catched\n"
" catchthis = #parent <= pagesize and #propstack <= depth\n"
" if parent == rootnode then\n"
" catchthis = catchthis and nodestoskip <= 0\n"
" nodestoskip = nodestoskip - 1\n"
" end\n"
" -- add node to tree\n"
" if catchthis then\n"
" parent[#parent + 1] = prop\n"
" propstack[#propstack + 1] = prop\n"
" end\n"
" else\n"
" rootnode = prop\n"
" catchthis = true\n"
" propstack[#propstack + 1] = prop\n"
" end\n"
" end\n"
"\n"
" return rootnode\n"
"end\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.introspection\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.plugins.ffi\n"
"package.preload[\"debugger.plugins.ffi\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2012-2013 Julien Desgats\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Julien Desgats - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- LuaJIT cdata introspection library.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"-- known issues:\n"
"-- * references are de-referenced event if inspect_references is unset\n"
"-- * is automatic pointer and reference de-referencing is possible ?\n"
"-- (only for first item in case of arrays). Possible leads:\n"
"-- http://stackoverflow.com/questions/7134590/how-to-test-if-an-address-is-readable-in-linux-userspace-app\n"
"-- http://www.softwareverify.com/blog/?p=319\n"
"-- * when setting a value from Eclipse, the type is sometimes changed (e.g. int => number)\n"
"\n"
"local introspection = require \"debugger.introspection\"\n"
"local reflect = require \"debugger.plugins.ffi.reflect\"\n"
"local ffi = require \"ffi\"\n"
"\n"
"local tostring, tonumber, type, assert, sformat, tconcat = tostring, tonumber, type, assert, string.format, table.concat\n"
"\n"
"local M = { }\n"
"\n"
"--- Whether the reference types are inspected. Usually references should be safe (at least a bit\n"
"-- safer than pointers) so they are inspected. If a reference points to unsafe memory, the whole\n"
"-- program could crash !\n"
"-- If this feature is disabled, deeply nested C types will not be displayed correctly as evaluation\n"
"-- has a recursion limit, any further evaluation is done through references.\n"
"M.inspect_references = true\n"
"\n"
"local function make_typename(refct)\n"
" local t = refct.what\n"
" if t == \"int\" then\n"
" if refct.bool then t = \"bool\"\n"
" else\n"
" -- use C99 type notation to give more details about acutal type\n"
" t = (refct.unsigned and \"uint\" or \"int\") .. tostring(refct.size * 8) .. \"_t\"\n"
" end\n"
" elseif t == \"float\" then\n"
" -- assume IEEE754\n"
" if refct.size == 8 then t = \"double\"\n"
" elseif refct.size == 16 then t = \"long double\" -- not really sure this one is always true\n"
" end\n"
" elseif t == \"struct\" or t == \"enum\" or t == \"union\" then\n"
" t = refct.name and (t .. \" \" .. refct.name) or (\"anonymous \"..t)\n"
" elseif t == \"func\" then\n"
" t = \"function (FFI)\"\n"
" elseif t == \"ptr\" then\n"
" t = make_typename(refct.element_type) .. \"*\"\n"
" elseif t == \"ref\" then\n"
" t = make_typename(refct.element_type) .. \"&\"\n"
" elseif t == \"field\" then\n"
" return make_typename(refct.type)\n"
" elseif t == \"bitfield\" then\n"
" t = (refct.type.unsigned and \"unsigned\" or \"signed\") .. \":\" .. tostring(refct.size * 8)\n"
" refct = refct.type\n"
" end\n"
"\n"
" if refct.const then t = \"const \" .. t end\n"
" if refct.volatile then t = \"volatile \" .. t end\n"
" return t\n"
"end\n"
"\n"
"-- if cdatakind is unknown, this one will be called\n"
"local default_inspector = introspection.inspectors.number\n"
"local inspect\n"
"\n"
"-- recursion must be handled with some care: if we call regular introspection.inspect\n"
"-- we may create boxed references or Lua native objects which will be inspected as such\n"
"-- (leading to wrong type names).\n"
"local function recurse(name, value, parent, fullname, refct)\n"
" if type(value) == \"cdata\" then\n"
" return inspect(name, value, parent, fullname, refct)\n"
" else\n"
" local prop = introspection.inspect(name, value, parent, fullname)\n"
" if prop then\n"
" prop.attr.type = make_typename(refct)\n"
" end\n"
" return prop\n"
" end\n"
"end\n"
"\n"
"-- cdata specific inspectors\n"
"local inspectors = {\n"
" struct = function(name, value, parent, fullname, refct)\n"
" local prop = introspection.property(name, make_typename(refct), tostring(value), parent, fullname)\n"
"\n"
" -- inspect children, if needed\n"
" if prop then\n"
" for member in refct:members() do\n"
" local mname = member.name\n"
" recurse(mname, value[mname], prop, fullname .. sformat('[%q]', mname), member)\n"
" end\n"
" end\n"
" return prop\n"
" end,\n"
"\n"
" array = function(name, value, parent, fullname, refct)\n"
" local etype = refct.element_type\n"
" -- for VLAs, reflect does not give size\n"
" local size = refct.size ~= \"none\" and refct.size or ffi.sizeof(value)\n"
" size = size and (size / etype.size) -- we've got the byte size, not element count\n"
"\n"
" local typename = make_typename(etype)\n"
" local prop = introspection.property(name, typename .. \"[\" .. (tostring(size) or \"\") .. \"]\", tostring(value), parent, fullname)\n"
"\n"
" if prop and size then\n"
" for i=0, size-1 do\n"
" local idx = \"[\"..tostring(i)..\"]\"\n"
" recurse(idx, value[i], prop, fullname .. idx, etype)\n"
" end\n"
" end\n"
" return prop\n"
" end,\n"
"\n"
" func = function(name, value, parent, fullname, refct)\n"
" local args = { }\n"
" for arg in refct:arguments() do\n"
" args[#args + 1] = make_typename(arg.type) .. \" \" .. arg.name\n"
" end\n"
"\n"
" if refct.vararg then\n"
" args[#args + 1] = \"...\"\n"
" end\n"
"\n"
" local repr = make_typename(refct.return_type) .. \" \" .. refct.name .. \"(\" .. tconcat(args, \", \") .. \")\"\n"
" return introspection.property(name, make_typename(refct), repr, parent, fullname)\n"
" end,\n"
"\n"
" enum = function(name, value, parent, fullname, refct)\n"
" local repr = tonumber(value)\n"
" -- try to convert numeric value into enum name\n"
" --TODO: is there a faster method to make it ?\n"
" for val in refct:values() do\n"
" if val.value == repr then\n"
" repr = val.name\n"
" break\n"
" end\n"
" end\n"
"\n"
" return introspection.property(name, make_typename(refct), tostring(repr), parent, fullname)\n"
" end,\n"
"\n"
" ref = function(name, value, parent, fullname, refct)\n"
" -- this may be unsafe, see inspect_references setting\n"
" local typename = make_typename(refct)\n"
" if not M.inspect_references then\n"
" return introspection.property(name, typename, tostring(value), parent, fullname)\n"
" end\n"
"\n"
" local prop = recurse(name, value, parent, fullname, refct.element_type)\n"
" if prop then\n"
" prop.attr.type = typename\n"
" end\n"
" return prop\n"
" end,\n"
"\n"
" int = function(name, value, parent, fullname, refct)\n"
" return introspection.property(name, make_typename(refct), tostring(tonumber(value)), parent, fullname)\n"
" end,\n"
"\n"
" -- pointers are too unsafe, do not inspect them\n"
" ptr = function(name, value, parent, fullname, refct)\n"
" return introspection.property(name, make_typename(refct), tostring(value), parent, fullname)\n"
" end,\n"
"}\n"
"\n"
"inspectors.union = inspectors.struct\n"
"inspectors.float = inspectors.int\n"
"\n"
"-- for struct/union fields, the actual type is nested into the refct\n"
"inspectors.field = function(name, value, parent, fullname, refct)\n"
" return inspect(name, value, parent, fullname, refct.type)\n"
"end\n"
"inspectors.bitfield = inspectors.field\n"
"\n"
"inspect = function(name, value, parent, fullname, refct)\n"
" -- inspect only values, not ctypes\n"
" --FIXME: this cause references to be dereferenced and crash the process if they are wrong !\n"
" if ffi.typeof(value) ~= value then\n"
" refct = refct or reflect.typeof(value)\n"
" return (inspectors[refct.what] or default_inspector)(name, value, parent, fullname, refct)\n"
" end\n"
"\n"
" -- return a simple property for ctypes\n"
" return introspection.property(name, \"ctype\", tostring(value), parent, fullname)\n"
"end\n"
"\n"
"introspection.inspectors.cdata = inspect\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.plugins.ffi\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.plugins.ffi.reflect\n"
"package.preload[\"debugger.plugins.ffi.reflect\"] = function(...)\n"
"--[[ LuaJIT FFI reflection Library ]]--\n"
"--[[ Copyright (C) 2013 Peter Cawley <lua@corsix.org>. All rights reserved.\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"--]]\n"
"local ffi = require \"ffi\"\n"
"local bit = require \"bit\"\n"
"local reflect = {}\n"
"\n"
"-- Relevant minimal definitions from lj_ctype.h\n"
"ffi.cdef [[\n"
" typedef struct CType {\n"
" uint32_t info;\n"
" uint32_t size;\n"
" uint16_t sib;\n"
" uint16_t next;\n"
" uint32_t name;\n"
" } CType;\n"
"\n"
" typedef struct CTState {\n"
" CType *tab;\n"
" uint32_t top;\n"
" uint32_t sizetab;\n"
" void *L;\n"
" void *g;\n"
" void *finalizer;\n"
" void *miscmap;\n"
" } CTState;\n"
"]]\n"
"\n"
"local function gc_str(gcref) -- Convert a GCref (to a GCstr) into a string\n"
" if gcref ~= 0 then\n"
" local ts = ffi.cast(\"uint32_t*\", gcref)\n"
" return ffi.string(ts + 4, ts[3])\n"
" end\n"
"end\n"
"\n"
"local function memptr(gcobj)\n"
" return tonumber(tostring(gcobj):match\"%x*$\", 16)\n"
"end\n"
"\n"
"-- Acquire a pointer to this Lua universe's CTState\n"
"local CTState do\n"
" local co = coroutine.create(function()end) -- Any live coroutine will do.\n"
" local uint32_ptr = ffi.typeof(\"uint32_t*\")\n"
" local G = ffi.cast(uint32_ptr, ffi.cast(uint32_ptr, memptr(co))[2])\n"
" -- In global_State, `MRef ctype_state` is immediately before `GCRef gcroot[GCROOT_MAX]`.\n"
" -- We first find (an entry in) gcroot by looking for a metamethod name string.\n"
" local anchor = ffi.cast(\"uint32_t\", ffi.cast(\"const char*\", \"__index\"))\n"
" local i = 0\n"
" while math.abs(tonumber(G[i] - anchor)) > 64 do\n"
" i = i + 1\n"
" end\n"
" -- We then work backwards looking for something resembling ctype_state.\n"
" repeat\n"
" i = i - 1\n"
" CTState = ffi.cast(\"CTState*\", G[i])\n"
" until ffi.cast(uint32_ptr, CTState.g) == G\n"
"end\n"
"\n"
"-- Acquire the CTState's miscmap table as a Lua variable\n"
"local miscmap do\n"
" local t = {}; t[0] = t\n"
" local tvalue = ffi.cast(\"uint32_t*\", memptr(t))[2]\n"
" ffi.cast(\"uint32_t*\", tvalue)[ffi.abi\"le\" and 0 or 1] = ffi.cast(\"uint32_t\", ffi.cast(\"uintptr_t\", CTState.miscmap))\n"
" miscmap = t[0]\n"
"end\n"
"\n"
"-- Information for unpacking a `struct CType`.\n"
"-- One table per CT_* constant, containing:\n"
"-- * A name for that CT_\n"
"-- * Roles of the cid and size fields.\n"
"-- * Whether the sib field is meaningful.\n"
"-- * Zero or more applicable boolean flags.\n"
"local CTs = {[0] =\n"
" {\"int\",\n"
" \"\", \"size\", false,\n"
" {0x08000000, \"bool\"},\n"
" {0x04000000, \"float\", \"subwhat\"},\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" {0x00800000, \"unsigned\"},\n"
" {0x00400000, \"long\"},\n"
" },\n"
" {\"struct\",\n"
" \"\", \"size\", true,\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" {0x00800000, \"union\", \"subwhat\"},\n"
" {0x00100000, \"vla\"},\n"
" },\n"
" {\"ptr\",\n"
" \"element_type\", \"size\", false,\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" {0x00800000, \"ref\", \"subwhat\"},\n"
" },\n"
" {\"array\",\n"
" \"element_type\", \"size\", false,\n"
" {0x08000000, \"vector\"},\n"
" {0x04000000, \"complex\"},\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" {0x00100000, \"vla\"},\n"
" },\n"
" {\"void\",\n"
" \"\", \"size\", false,\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" },\n"
" {\"enum\",\n"
" \"type\", \"size\", true,\n"
" },\n"
" {\"func\",\n"
" \"return_type\", \"nargs\", true,\n"
" {0x00800000, \"vararg\"},\n"
" {0x00400000, \"sse_reg_params\"},\n"
" },\n"
" {\"typedef\", -- Not seen\n"
" \"element_type\", \"\", false,\n"
" },\n"
" {\"attrib\", -- Only seen internally\n"
" \"type\", \"value\", true,\n"
" },\n"
" {\"field\",\n"
" \"type\", \"offset\", true,\n"
" },\n"
" {\"bitfield\",\n"
" \"\", \"offset\", true,\n"
" {0x08000000, \"bool\"},\n"
" {0x02000000, \"const\"},\n"
" {0x01000000, \"volatile\"},\n"
" {0x00800000, \"unsigned\"},\n"
" },\n"
" {\"constant\",\n"
" \"type\", \"value\", true,\n"
" {0x02000000, \"const\"},\n"
" },\n"
" {\"extern\", -- Not seen\n"
" \"CID\", \"\", true,\n"
" },\n"
" {\"kw\", -- Not seen\n"
" \"TOK\", \"size\",\n"
" },\n"
"}\n"
"\n"
"-- Set of CType::cid roles which are a CTypeID.\n"
"local type_keys = {\n"
" element_type = true,\n"
" return_type = true,\n"
" value_type = true,\n"
" type = true,\n"
"}\n"
"\n"
"-- Create a metatable for each CT.\n"
"local metatables = {\n"
"}\n"
"for _, CT in ipairs(CTs) do\n"
" local what = CT[1]\n"
" local mt = {__index = {}}\n"
" metatables[what] = mt\n"
"end\n"
"\n"
"-- Logic for merging an attribute CType onto the annotated CType.\n"
"local CTAs = {[0] =\n"
" function(a, refct) error(\"TODO: CTA_NONE\") end,\n"
" function(a, refct) error(\"TODO: CTA_QUAL\") end,\n"
" function(a, refct)\n"
" a = 2^a.value\n"
" refct.alignment = a\n"
" refct.attributes.align = a\n"
" end,\n"
" function(a, refct)\n"
" refct.transparent = true\n"
" refct.attributes.subtype = refct.typeid\n"
" end,\n"
" function(a, refct) refct.sym_name = a.name end,\n"
" function(a, refct) error(\"TODO: CTA_BAD\") end,\n"
"}\n"
"\n"
"-- C function calling conventions (CTCC_* constants in lj_refct.h)\n"
"local CTCCs = {[0] =\n"
" \"cdecl\",\n"
" \"thiscall\",\n"
" \"fastcall\",\n"
" \"stdcall\",\n"
"}\n"
"\n"
"local function refct_from_id(id) -- refct = refct_from_id(CTypeID)\n"
" local ctype = CTState.tab[id]\n"
" local CT_code = bit.rshift(ctype.info, 28)\n"
" local CT = CTs[CT_code]\n"
" local what = CT[1]\n"
" local refct = setmetatable({\n"
" what = what,\n"
" typeid = id,\n"
" name = gc_str(ctype.name),\n"
" }, metatables[what])\n"
"\n"
" -- Interpret (most of) the CType::info field\n"
" for i = 5, #CT do\n"
" if bit.band(ctype.info, CT[i][1]) ~= 0 then\n"
" if CT[i][3] == \"subwhat\" then\n"
" refct.what = CT[i][2]\n"
" else\n"
" refct[CT[i][2]] = true\n"
" end\n"
" end\n"
" end\n"
" if CT_code <= 5 then\n"
" refct.alignment = bit.lshift(1, bit.band(bit.rshift(ctype.info, 16), 15))\n"
" elseif what == \"func\" then\n"
" refct.convention = CTCCs[bit.band(bit.rshift(ctype.info, 16), 3)]\n"
" end\n"
"\n"
" if CT[2] ~= \"\" then -- Interpret the CType::cid field\n"
" local k = CT[2]\n"
" local cid = bit.band(ctype.info, 0xffff)\n"
" if type_keys[k] then\n"
" if cid == 0 then\n"
" cid = nil\n"
" else\n"
" cid = refct_from_id(cid)\n"
" end\n"
" end\n"
" refct[k] = cid\n"
" end\n"
"\n"
" if CT[3] ~= \"\" then -- Interpret the CType::size field\n"
" local k = CT[3]\n"
" refct[k] = ctype.size\n"
" if k == \"size\" and bit.bnot(refct[k]) == 0 then\n"
" refct[k] = \"none\"\n"
" end\n"
" end\n"
"\n"
" if what == \"attrib\" then\n"
" -- Merge leading attributes onto the type being decorated.\n"
" local CTA = CTAs[bit.band(bit.rshift(ctype.info, 16), 0xff)]\n"
" if refct.type then\n"
" local ct = refct.type\n"
" ct.attributes = {}\n"
" CTA(refct, ct)\n"
" ct.typeid = refct.typeid\n"
" refct = ct\n"
" else\n"
" refct.CTA = CTA\n"
" end\n"
" elseif what == \"bitfield\" then\n"
" -- Decode extra bitfield fields, and make it look like a normal field.\n"
" refct.offset = refct.offset + bit.band(ctype.info, 127) / 8\n"
" refct.size = bit.band(bit.rshift(ctype.info, 8), 127) / 8\n"
" refct.type = {\n"
" what = \"int\",\n"
" bool = refct.bool,\n"
" const = refct.const,\n"
" volatile = refct.volatile,\n"
" unsigned = refct.unsigned,\n"
" size = bit.band(bit.rshift(ctype.info, 16), 127),\n"
" }\n"
" refct.bool, refct.const, refct.volatile, refct.unsigned = nil\n"
" end\n"
"\n"
" if CT[4] then -- Merge sibling attributes onto this type.\n"
" while ctype.sib ~= 0 do\n"
" local entry = CTState.tab[ctype.sib]\n"
" if CTs[bit.rshift(entry.info, 28)][1] ~= \"attrib\" then break end\n"
" if bit.band(entry.info, 0xffff) ~= 0 then break end\n"
" local sib = refct_from_id(ctype.sib)\n"
" sib:CTA(refct)\n"
" ctype = entry\n"
" end\n"
" end\n"
"\n"
" return refct\n"
"end\n"
"\n"
"local function sib_iter(s, refct)\n"
" repeat\n"
" local ctype = CTState.tab[refct.typeid]\n"
" if ctype.sib == 0 then return end\n"
" refct = refct_from_id(ctype.sib)\n"
" until refct.what ~= \"attrib\" -- Pure attribs are skipped.\n"
" return refct\n"
"end\n"
"\n"
"local function siblings(refct)\n"
" -- Follow to the end of the attrib chain, if any.\n"
" while refct.attributes do\n"
" refct = refct_from_id(refct.attributes.subtype or CTState.tab[refct.typeid].sib)\n"
" end\n"
"\n"
" return sib_iter, nil, refct\n"
"end\n"
"\n"
"metatables.struct.__index.members = siblings\n"
"metatables.func.__index.arguments = siblings\n"
"metatables.enum.__index.values = siblings\n"
"\n"
"local function find_sibling(refct, name)\n"
" local num = tonumber(name)\n"
" if num then\n"
" for sib in siblings(refct) do\n"
" if num == 1 then\n"
" return sib\n"
" end\n"
" num = num - 1\n"
" end\n"
" else\n"
" for sib in siblings(refct) do\n"
" if sib.name == name then\n"
" return sib\n"
" end\n"
" end\n"
" end\n"
"end\n"
"\n"
"metatables.struct.__index.member = find_sibling\n"
"metatables.func.__index.argument = find_sibling\n"
"metatables.enum.__index.value = find_sibling\n"
"\n"
"function reflect.typeof(x) -- refct = reflect.typeof(ct)\n"
" return refct_from_id(tonumber(ffi.typeof(x)))\n"
"end\n"
"\n"
"function reflect.getmetatable(x) -- mt = reflect.getmetatable(ct)\n"
" return miscmap[-tonumber(ffi.typeof(x))]\n"
"end\n"
"\n"
"return reflect\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.plugins.ffi.reflect\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.platform\n"
"package.preload[\"debugger.platform\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Platform/OS specific features and path handling.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local url = require \"debugger.url\"\n"
"local util = require \"debugger.util\"\n"
"\n"
"local M = { }\n"
"\n"
"-- Execution plaform (could be win or unix)\n"
"-- Used to manage file path difference between the 2 platform\n"
"local platform = nil\n"
"\n"
"-- keep all computed URIs in cache (as they are quite long to compute)\n"
"local uri_cache = { }\n"
"\n"
"-- parse a normalized path and return a table of each segment\n"
"-- you could precise the path seperator.\n"
"local function split(path,sep)\n"
" local t = {}\n"
" for w in path:gmatch(\"[^\"..(sep or \"/\")..\"]+\")do\n"
" table.insert(t, w)\n"
" end\n"
" return t\n"
"end\n"
"\n"
"-- return a table: key=searchpathname,value=true (add by guanyu)\n"
"local function convert_to_search_paths(searchpaths)\n"
" local t = {}\n"
" for w in searchpaths:gmatch(\"[^;]+\") do\n"
" t[w] = true\n"
" end\n"
" return t;\n"
"end\n"
"\n"
"-- return a relative uri to M.base_dir for a given source, for android device (added by guanyu)\n"
"local function convert_to_android_file_uri(source)\n"
" if source:sub(1,6) == \"assets\" then -- replace assets\n"
" source = source:sub(8)\n"
" elseif M.search_paths then -- replace /mnt/sdcard/packagename/ with empty string\n"
" for k, v in pairs(M.search_paths) do\n"
" if #source > #k and M.search_paths[source:sub(1,#k)] then\n"
" source = source:sub(#k+1)\n"
" break\n"
" end\n"
" end\n"
" end\n"
" return source\n"
"end\n"
"\n"
"-- return a relative uri to M.base_dir for a given source, , for ios simulator/device? (added by guanyu\n"
"local function convert_to_ios_file_uri(source)\n"
" if M.search_paths then\n"
" for k, v in pairs(M.search_paths) do\n"
" if #source > #k and M.search_paths[source:sub(1,#k)] then\n"
" source = source:sub(#k+1)\n"
" break\n"
" end\n"
" end\n"
" end\n"
" return source\n"
"end\n"
"\n"
"local function convert_to_relative_file_uri(source)\n"
" if M.search_paths then\n"
" for k, v in pairs(M.search_paths) do\n"
" if #source > #k and M.search_paths[source:sub(1,#k)] then\n"
" source = source:sub(#k+1)\n"
" break\n"
" end\n"
" end\n"
" end\n"
" return source\n"
"end\n"
"\n"
"--- Returns a RFC2396 compliant URI for given source, or false if the mapping failed\n"
"local function get_abs_file_uri (source)\n"
" local uri\n"
" if source:sub(1,1) == \"@\" then -- real source file\n"
" local sourcepath = source:sub(2)\n"
" if M.base_dir then\n"
" sourcepath = convert_to_relative_file_uri(sourcepath)\n"
" end\n"
" local normalizedpath = M.normalize(sourcepath)\n"
"-- if not M.is_path_absolute(normalizedpath) then\n"
"-- normalizedpath = M.normalize(M.base_dir .. \"/\" .. normalizedpath)--M.base_dir .. \"/src/\" .. normalizedpath\n"
"-- end\n"
" return M.to_file_uri(normalizedpath)\n"
" elseif source:sub(#source-3,-1):lower() == \".lua\" then -- loadstring relative path source file (added by guanyu)\n"
" -- android\n"
" if platform == \"android\" then\n"
" source = convert_to_android_file_uri(source)\n"
" -- ios ???\n"
" elseif platform == \"ios\" then\n"
" source = convert_to_ios_file_uri(source)\n"
" end\n"
" local normalizedpath = M.normalize(source)\n"
"-- if not M.is_path_absolute(normalizedpath) then\n"
"-- normalizedpath = M.normalize(M.base_dir .. \"/\" .. normalizedpath)--M.base_dir .. \"/src/\" .. normalizedpath\n"
"-- end\n"
" return M.to_file_uri(normalizedpath)\n"
" else -- dynamic code, stripped bytecode, tail return, ...\n"
" return false\n"
" end\n"
"end\n"
"\n"
"--FIXME: as result is cached, changes in package.path that modify the module name are missed\n"
"-- (mostly affect main module when Lua interpreter is launched with an absolute path)\n"
"local function get_module_uri (source)\n"
" if source:sub(1,1) == \"@\" then -- real source file\n"
" local uri\n"
" local sourcepath = source:sub(2)\n"
" local normalizedpath = M.normalize(sourcepath)\n"
" local luapathtable = split (package.path, \";\")\n"
" local is_source_absolute = M.is_path_absolute(sourcepath)\n"
" -- workarround : Add always the ?.lua entry to support\n"
" -- the case where file was loaded by : \"lua myfile.lua\"\n"
" table.insert(luapathtable,\"?.lua\")\n"
" for i,var in ipairs(luapathtable) do\n"
" -- avoid relative patterns matching absolute ones (e.g. ?.lua matches anything)\n"
" if M.is_path_absolute(var) == is_source_absolute then\n"
" local escaped = string.gsub(M.normalize(var),\"[%^%$%(%)%%%.%[%]%*%+%-%?]\",function(c) return \"%\"..c end)\n"
" local pattern = string.gsub(escaped,\"%%%?\",\"(.+)\")\n"
" local modulename = string.match(normalizedpath,pattern)\n"
" if modulename then\n"
" modulename = string.gsub(modulename,\"/\",\".\");\n"
" -- if we find more than 1 possible modulename return the shorter\n"
" if not uri or string.len(uri)>string.len(modulename) then\n"
" uri = modulename\n"
" end\n"
" end\n"
" end\n"
" end\n"
" if uri then return \"module:///\"..uri end\n"
" end\n"
" return false\n"
"end\n"
"\n"
"function M.get_uri (source)\n"
" -- search in cache\n"
" local uri = uri_cache[source]\n"
" if uri ~= nil then return uri end\n"
"\n"
" -- not found, create uri\n"
" if util.features.uri == \"module\" then\n"
" uri = get_module_uri(source)\n"
" if not uri then uri = get_abs_file_uri (source) end\n"
" else\n"
" uri = get_abs_file_uri (source)\n"
" end\n"
"\n"
" uri_cache[source] = uri\n"
" return uri\n"
"end\n"
"\n"
"-- get path file from uri\n"
"function M.get_path (uri)\n"
" local parsed_path = assert(url.parse(uri))\n"
" if parsed_path.scheme == \"file\" then\n"
" return M.to_path(parsed_path)\n"
" else\n"
" -- search in cache\n"
" -- we should surely calculate it instead of find in cache\n"
" for k,v in pairs(uri_cache)do\n"
" if v == uri then\n"
" assert(k:sub(1,1) == \"@\")\n"
" return k:sub(2)\n"
" end\n"
" end\n"
" end\n"
"end\n"
"\n"
"function M.normalize(path)\n"
" local parts = { }\n"
" for w in path:gmatch(\"[^/]+\") do\n"
" if w == \"..\" and #parts ~=0 then table.remove(parts)\n"
" elseif w ~= \".\" then table.insert(parts, w)\n"
" end\n"
" end\n"
" return (path:sub(1,1) == \"/\" and \"/\" or \"\") .. table.concat(parts, \"/\")\n"
"end\n"
"\n"
"function M.init(executionplatform,workingdirectory,searchpaths)\n"
" --------------------------\n"
" -- define current platform\n"
" --------------------------\n"
" -- check parameter\n"
" if executionplatform and executionplatform ~= \"unix\" and executionplatform ~=\"win\" and executionplatform ~=\"android\" and executionplatform ~=\"ios\"then\n"
" error(\"Unable to initialize platform module : execution platform should be 'unix' or 'win'.\")\n"
" end\n"
"\n"
" -- use parameter as current platform\n"
" if executionplatform then\n"
" platform = executionplatform\n"
" else\n"
" --if not define try to guess it.\n"
" local function iswindows()\n"
" local p = io.popen(\"echo %os%\")\n"
" if p then\n"
" local result =p:read(\"*l\")\n"
" p:close()\n"
" return result == \"Windows_NT\"\n"
" end\n"
" return false\n"
" end\n"
"\n"
" local status, iswin = pcall(iswindows)\n"
" if status and iswin then\n"
" platform = \"win\"\n"
" else\n"
" platform = \"unix\"\n"
" end\n"
" end\n"
"\n"
" --------------------------\n"
" -- platform dependent function\n"
" --------------------------\n"
" if platform == \"unix\" or platform == \"android\" or platform == \"ios\" then\n"
" -- The Path separator character\n"
" M.path_sep = \"/\"\n"
"\n"
" -- TODO the way to get the absolute path can be wrong if the program loads new source files by relative path after a cd.\n"
" -- currently, the directory is registered on start, this allows program to load any source file and then change working dir,\n"
" -- which is the most common use case.\n"
" M.base_dir = workingdirectory or os.getenv(\"PWD\")\n"
"\n"
" -- convert parsed URL table to file path for the current OS (see url.parse from luasocket)\n"
" M.to_file_uri = function (path)\n"
" if string.find(path,\"/\") ~= 1 then\n"
" path=\"/\"..path\n"
" end\n"
" return url.build{scheme=\"file\",authority=\"\", path=path}\n"
" end\n"
"\n"
" -- return true is the path is absolute\n"
" -- the path must be normalized\n"
" M.is_path_absolute = function (path) return path:sub(1,1) == \"/\" end\n"
"\n"
" -- convert absolute normalized path file to uri\n"
" M.to_path = function (parsed_url) return url.unescape(parsed_url.path) end\n"
" else\n"
" -- Implementations for Windows, see UNIX versions for documentation.\n"
" M.path_sep = \"\\\\\"\n"
" M.is_path_absolute = function (path) return path:match(\"^%a:/\") end\n"
" M.to_file_uri = function (path) return url.build{scheme=\"file\",authority=\"\", path=\"/\"..path} end\n"
" M.to_path = function (parsed_url) return url.unescape(parsed_url.path):gsub(\"^/\", \"\") end\n"
"\n"
" local unixnormalize = M.normalize\n"
" M.normalize = function(path) return unixnormalize(path:gsub(\"\\\\\",\"/\"):lower()) end\n"
"\n"
" -- determine base dir\n"
" local function getworkingdirectory()\n"
" local p = io.popen(\"echo %cd%\")\n"
" if p then\n"
" local res = p:read(\"*l\")\n"
" p:close()\n"
" return M.normalize(res)\n"
" end\n"
" end\n"
" M.base_dir = workingdirectory or getworkingdirectory()\n"
"\n"
" end\n"
" --fix bug: can not break when project path contains blank\n"
" --replace blank with %20 (add by guanyu)\n"
" M.base_dir = M.base_dir:gsub(\" \",\"%%20\")\n"
" -- the js/lua script searchpath if has more then one, split them with a semicolon \";\" (added by guanyu)\n"
" if searchpaths then M.search_paths = convert_to_search_paths(searchpaths) end\n"
" if not M.base_dir then error(\"Unable to determine the working directory.\") end\n"
"end\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.platform\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.util\n"
"package.preload[\"debugger.util\"] = function(...)\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"-- Utility functions.\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local M = { }\n"
"\n"
"-- log system\n"
"local LEVELS = { ERROR = 0, WARNING = 1, INFO = 2, DETAIL = 3, DEBUG = 4 }\n"
"local LOG_LEVEL = LEVELS.WARNING\n"
"\n"
"-- Debugger features handling. Any feature can be get like any regular table, setting features result in\n"
"-- error for unknown or read-only features.\n"
"M.features = setmetatable({ }, {\n"
" -- functions that format/validate data. If function is not provided, the feature cannot be modified.\n"
" validators = {\n"
" multiple_sessions = tonumber,\n"
" encoding = tostring,\n"
" max_children = tonumber,\n"
" max_data = tonumber,\n"
" max_depth = tonumber,\n"
" show_hidden = tonumber,\n"
" uri = tostring,\n"
" log_level = function(level_name)\n"
" -- set numerical index in internal var\n"
" LOG_LEVEL = assert(LEVELS[level_name], \"No such level\")\n"
" return level_name -- the displayed level is still the name\n"
" end,\n"
" },\n"
" __index = {\n"
" multiple_sessions = 0,\n"
" encoding =\"UTF-8\",\n"
" max_children = 32,\n"
" max_data = 0xFFFF,\n"
" max_depth = 1,\n"
" show_hidden = 1,\n"
" uri = \"file\",\n"
" log_level = \"WARNING\",\n"
" -- read only features\n"
" language_supports_threads = 0,\n"
" language_name = \"Lua\",\n"
" language_version = _VERSION,\n"
" protocol_version = 1,\n"
" supports_async = 1,\n"
" data_encoding = \"base64\",\n"
" breakpoint_languages = \"Lua\",\n"
" breakpoint_types = \"line conditional\",\n"
" },\n"
" __newindex = function(self, k, v)\n"
" local mt = getmetatable(self)\n"
" local values, validator = mt.__index, mt.validators[k]\n"
" if values[k] == nil then error(\"No such feature \" .. tostring(k)) end\n"
" if not validator then error(\"The feature \" .. tostring(k) .. \" is read-only\") end\n"
" v = assert(validator(v))\n"
" values[k] = v\n"
" end,\n"
"})\n"
"\n"
"-- Wraps debug function and an attached thread\n"
"-- also handle stack & coroutine management differencies between Lua versions\n"
"local getinfo, getlocal, setlocal = debug.getinfo, debug.getlocal, debug.setlocal\n"
"\n"
"-- Foreign thread is used to debug paused thread\n"
"local ForeignThreadMT = {\n"
" getinfo = function(self, level, what) return getinfo(self[1], level, what) end,\n"
" getlocal = function(self, level, idx) return getlocal(self[1], level, idx) end,\n"
" setlocal = function(self, level, idx, val) return setlocal(self[1], level, idx, val) end,\n"
"}\n"
"ForeignThreadMT.__index = ForeignThreadMT\n"
"function M.ForeignThread(coro) return setmetatable({ coro }, ForeignThreadMT) end\n"
"\n"
"-- Current thread is used to debug the thread that caused the hook\n"
"-- intended to be used *ONLY* in debug loop (executed in a new thread)\n"
"local CurrentThreadMT = {\n"
" getinfo = function(self, level, what) return getinfo(self[1], level + 2, what) end,\n"
" getlocal = function(self, level, idx) return getlocal(self[1], level + 2, idx) end,\n"
" setlocal = function(self, level, idx, val) return setlocal(self[1], level + 2, idx, val) end,\n"
"}\n"
"CurrentThreadMT.__index = CurrentThreadMT\n"
"function M.CurrentThread(coro) return setmetatable({ coro }, CurrentThreadMT) end\n"
"\n"
"\n"
"-- Some version dependant functions\n"
"if _VERSION == \"Lua 5.1\" then\n"
" local loadstring, getfenv, setfenv, debug_getinfo, MainThread =\n"
" loadstring, getfenv, setfenv, debug.getinfo, nil\n"
"\n"
" -- in 5.1 \"t\" flag does not exist and trigger an error so remove it from what\n"
" CurrentThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level + 2, what:gsub(\"t\", \"\", 1)) end\n"
" ForeignThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level, what:gsub(\"t\", \"\", 1)) end\n"
"\n"
" -- when we're forced to start debug loop on top of program stack (when on main coroutine)\n"
" -- this requires some hackery to get right stack level\n"
"\n"
" -- Fallback method to inspect running thread (only for main thread in 5.1 or for conditional breakpoints)\n"
" --- Gets a script stack level with additional debugger logic added\n"
" -- @param l (number) stack level to get for debugged script (0 based)\n"
" -- @return real Lua stack level suitable to be passed through deubg functions\n"
" local function get_script_level(l)\n"
" local hook = debug.gethook()\n"
" for i=2, math.huge do\n"
" if assert(debug.getinfo(i, \"f\")).func == hook then\n"
" return i + l -- the script to level is just below, but because of the extra call to this function, the level is ok for callee\n"
" end\n"
" end\n"
" end\n"
"\n"
" if rawget(_G, \"jit\") then\n"
" MainThread = {\n"
" [1] = \"main\", -- as the raw thread object is used as table keys, provide a replacement.\n"
" -- LuaJIT completely eliminates tail calls from stack, so get_script_level retunrs wrong result in this case\n"
" getinfo = function(self, level, what) return getinfo(get_script_level(level) - 1, what:gsub(\"t\", \"\", 1)) end,\n"
" getlocal = function(self, level, idx) return getlocal(get_script_level(level) - 1, idx) end,\n"
" setlocal = function(self, level, idx, val) return setlocal(get_script_level(level) - 1, idx, val) end,\n"
" }\n"
" else\n"
" MainThread = {\n"
" [1] = \"main\",\n"
" getinfo = function(self, level, what) return getinfo(get_script_level(level) , what:gsub(\"t\", \"\", 1)) end,\n"
" getlocal = function(self, level, idx) return getlocal(get_script_level(level), idx) end,\n"
" setlocal = function(self, level, idx, val) return setlocal(get_script_level(level), idx, val) end,\n"
" }\n"
" end\n"
"\n"
"\n"
"\n"
" -- If the VM is vanilla Lua 5.1 or LuaJIT 2 without 5.2 compatibility, there is no way to get a reference to\n"
" -- the main coroutine, so fall back to direct mode: the debugger loop is started on the top of main thread\n"
" -- and the actual level is recomputed each time\n"
" local oldCurrentThread = M.CurrentThread\n"
" M.CurrentThread = function(coro) return coro and oldCurrentThread(coro) or MainThread end\n"
"\n"
" -- load a piece of code alog with its environment\n"
" function M.loadin(code, env)\n"
" local f,err = loadstring(code)\n"
" if not f then\n"
" return nil, err\n"
" else\n"
" return f and setfenv(f, env)\n"
" end\n"
" end\n"
"\n"
" -- table that maps [gs]et environment to index\n"
" M.eval_env = setmetatable({ }, {\n"
" __index = function(self, func) return getfenv(func) end,\n"
" __newindex = function(self, func, env) return setfenv(func, env) end,\n"
" })\n"
"elseif _VERSION == \"Lua 5.2\" then\n"
" local load, debug_getinfo = load, debug.getinfo\n"
" function M.getinfo(coro, level, what)\n"
" if coro then return debug_getinfo(coro, level, what)\n"
" else return debug_getinfo(level + 1, what) end\n"
" end\n"
"\n"
" function M.loadin(code, env) return load(code, nil, nil, env) end\n"
"\n"
" -- no eval_env for 5.2 as functions does not have environments anymore\n"
"end\n"
"\n"
"-- ----------------------------------------------------------------------------\n"
"-- Bare minimal log system.\n"
"-- ----------------------------------------------------------------------------\n"
"function M.log(level, msg, ...)\n"
" if (LEVELS[level] or -1) > LOG_LEVEL then return end\n"
" if select(\"#\", ...) > 0 then msg = msg:format(...) end\n"
" io.base.stderr:write(string.format(\"DEBUGGER\\t%s\\t%s\\n\", level, msg))\n"
"end\n"
"\n"
"return M\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.util\n"
"--------------------------------------------------------------------------------\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Module debugger.url\n"
"package.preload[\"debugger.url\"] = function(...)\n"
"-----------------------------------------------------------------------------\n"
"-- URI parsing, composition and relative URL resolution\n"
"-- LuaSocket toolkit.\n"
"-- Author: Diego Nehab\n"
"-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $\n"
"-----------------------------------------------------------------------------\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Declare module\n"
"-----------------------------------------------------------------------------\n"
"local string = require(\"string\")\n"
"local base = _G\n"
"local table = require(\"table\")\n"
"\n"
"local _ENV = { }\n"
"if setfenv then setfenv(1, _ENV) end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Module version\n"
"-----------------------------------------------------------------------------\n"
"_VERSION = \"URL 1.0.1\"\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Encodes a string into its escaped hexadecimal representation\n"
"-- Input\n"
"-- s: binary string to be encoded\n"
"-- Returns\n"
"-- escaped representation of string binary\n"
"-----------------------------------------------------------------------------\n"
"function escape(s)\n"
" return string.gsub(s, \"([^A-Za-z0-9_])\", function(c)\n"
" return string.format(\"%%%02x\", string.byte(c))\n"
" end)\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Protects a path segment, to prevent it from interfering with the\n"
"-- url parsing.\n"
"-- Input\n"
"-- s: binary string to be encoded\n"
"-- Returns\n"
"-- escaped representation of string binary\n"
"-----------------------------------------------------------------------------\n"
"local function make_set(t)\n"
" local s = {}\n"
" for i,v in base.ipairs(t) do\n"
" s[t[i]] = 1\n"
" end\n"
" return s\n"
"end\n"
"\n"
"-- these are allowed withing a path segment, along with alphanum\n"
"-- other characters must be escaped\n"
"local segment_set = make_set {\n"
" \"-\", \"_\", \".\", \"!\", \"~\", \"*\", \"'\", \"(\",\n"
" \")\", \":\", \"@\", \"&\", \"=\", \"+\", \"$\", \",\",\n"
"}\n"
"\n"
"local function protect_segment(s)\n"
" return string.gsub(s, \"([^A-Za-z0-9_])\", function (c)\n"
" if segment_set[c] then return c\n"
" else return string.format(\"%%%02x\", string.byte(c)) end\n"
" end)\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Encodes a string into its escaped hexadecimal representation\n"
"-- Input\n"
"-- s: binary string to be encoded\n"
"-- Returns\n"
"-- escaped representation of string binary\n"
"-----------------------------------------------------------------------------\n"
"function unescape(s)\n"
" return string.gsub(s, \"%%(%x%x)\", function(hex)\n"
" return string.char(base.tonumber(hex, 16))\n"
" end)\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Builds a path from a base path and a relative path\n"
"-- Input\n"
"-- base_path\n"
"-- relative_path\n"
"-- Returns\n"
"-- corresponding absolute path\n"
"-----------------------------------------------------------------------------\n"
"local function absolute_path(base_path, relative_path)\n"
" if string.sub(relative_path, 1, 1) == \"/\" then return relative_path end\n"
" local path = string.gsub(base_path, \"[^/]*$\", \"\")\n"
" path = path .. relative_path\n"
" path = string.gsub(path, \"([^/]*%./)\", function (s)\n"
" if s ~= \"./\" then return s else return \"\" end\n"
" end)\n"
" path = string.gsub(path, \"/%.$\", \"/\")\n"
" local reduced\n"
" while reduced ~= path do\n"
" reduced = path\n"
" path = string.gsub(reduced, \"([^/]*/%.%./)\", function (s)\n"
" if s ~= \"../../\" then return \"\" else return s end\n"
" end)\n"
" end\n"
" path = string.gsub(reduced, \"([^/]*/%.%.)$\", function (s)\n"
" if s ~= \"../..\" then return \"\" else return s end\n"
" end)\n"
" return path\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Parses a url and returns a table with all its parts according to RFC 2396\n"
"-- The following grammar describes the names given to the URL parts\n"
"-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>\n"
"-- <authority> ::= <userinfo>@<host>:<port>\n"
"-- <userinfo> ::= <user>[:<password>]\n"
"-- <path> :: = {<segment>/}<segment>\n"
"-- Input\n"
"-- url: uniform resource locator of request\n"
"-- default: table with default values for each field\n"
"-- Returns\n"
"-- table with the following fields, where RFC naming conventions have\n"
"-- been preserved:\n"
"-- scheme, authority, userinfo, user, password, host, port,\n"
"-- path, params, query, fragment\n"
"-- Obs:\n"
"-- the leading '/' in {/<path>} is considered part of <path>\n"
"-----------------------------------------------------------------------------\n"
"function parse(url, default)\n"
" -- initialize default parameters\n"
" local parsed = {}\n"
" for i,v in base.pairs(default or parsed) do parsed[i] = v end\n"
" -- empty url is parsed to nil\n"
" if not url or url == \"\" then return nil, \"invalid url\" end\n"
" -- remove whitespace\n"
" -- url = string.gsub(url, \"%s\", \"\")\n"
" -- get fragment\n"
" url = string.gsub(url, \"#(.*)$\", function(f)\n"
" parsed.fragment = f\n"
" return \"\"\n"
" end)\n"
" -- get scheme\n"
" url = string.gsub(url, \"^([%w][%w%+%-%.]*)%:\",\n"
" function(s) parsed.scheme = s; return \"\" end)\n"
" -- get authority\n"
" url = string.gsub(url, \"^//([^/]*)\", function(n)\n"
" parsed.authority = n\n"
" return \"\"\n"
" end)\n"
" -- get query stringing\n"
" url = string.gsub(url, \"%?(.*)\", function(q)\n"
" parsed.query = q\n"
" return \"\"\n"
" end)\n"
" -- get params\n"
" url = string.gsub(url, \"%;(.*)\", function(p)\n"
" parsed.params = p\n"
" return \"\"\n"
" end)\n"
" -- path is whatever was left\n"
" if url ~= \"\" then parsed.path = url end\n"
" local authority = parsed.authority\n"
" if not authority then return parsed end\n"
" authority = string.gsub(authority,\"^([^@]*)@\",\n"
" function(u) parsed.userinfo = u; return \"\" end)\n"
" authority = string.gsub(authority, \":([^:]*)$\",\n"
" function(p) parsed.port = p; return \"\" end)\n"
" if authority ~= \"\" then parsed.host = authority end\n"
" local userinfo = parsed.userinfo\n"
" if not userinfo then return parsed end\n"
" userinfo = string.gsub(userinfo, \":([^:]*)$\",\n"
" function(p) parsed.password = p; return \"\" end)\n"
" parsed.user = userinfo\n"
" return parsed\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Rebuilds a parsed URL from its components.\n"
"-- Components are protected if any reserved or unallowed characters are found\n"
"-- Input\n"
"-- parsed: parsed URL, as returned by parse\n"
"-- Returns\n"
"-- a stringing with the corresponding URL\n"
"-----------------------------------------------------------------------------\n"
"function build(parsed)\n"
" local ppath = parse_path(parsed.path or \"\")\n"
" local url = build_path(ppath)\n"
" if parsed.params then url = url .. \";\" .. parsed.params end\n"
" if parsed.query then url = url .. \"?\" .. parsed.query end\n"
" local authority = parsed.authority\n"
" if parsed.host then\n"
" authority = parsed.host\n"
" if parsed.port then authority = authority .. \":\" .. parsed.port end\n"
" local userinfo = parsed.userinfo\n"
" if parsed.user then\n"
" userinfo = parsed.user\n"
" if parsed.password then\n"
" userinfo = userinfo .. \":\" .. parsed.password\n"
" end\n"
" end\n"
" if userinfo then authority = userinfo .. \"@\" .. authority end\n"
" end\n"
" if authority then url = \"//\" .. authority .. url end\n"
" if parsed.scheme then url = parsed.scheme .. \":\" .. url end\n"
" if parsed.fragment then url = url .. \"#\" .. parsed.fragment end\n"
" -- url = string.gsub(url, \"%s\", \"\")\n"
" return url\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Builds a absolute URL from a base and a relative URL according to RFC 2396\n"
"-- Input\n"
"-- base_url\n"
"-- relative_url\n"
"-- Returns\n"
"-- corresponding absolute url\n"
"-----------------------------------------------------------------------------\n"
"function absolute(base_url, relative_url)\n"
" if base.type(base_url) == \"table\" then\n"
" base_parsed = base_url\n"
" base_url = build(base_parsed)\n"
" else\n"
" base_parsed = parse(base_url)\n"
" end\n"
" local relative_parsed = parse(relative_url)\n"
" if not base_parsed then return relative_url\n"
" elseif not relative_parsed then return base_url\n"
" elseif relative_parsed.scheme then return relative_url\n"
" else\n"
" relative_parsed.scheme = base_parsed.scheme\n"
" if not relative_parsed.authority then\n"
" relative_parsed.authority = base_parsed.authority\n"
" if not relative_parsed.path then\n"
" relative_parsed.path = base_parsed.path\n"
" if not relative_parsed.params then\n"
" relative_parsed.params = base_parsed.params\n"
" if not relative_parsed.query then\n"
" relative_parsed.query = base_parsed.query\n"
" end\n"
" end\n"
" else\n"
" relative_parsed.path = absolute_path(base_parsed.path or \"\",\n"
" relative_parsed.path)\n"
" end\n"
" end\n"
" return build(relative_parsed)\n"
" end\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Breaks a path into its segments, unescaping the segments\n"
"-- Input\n"
"-- path\n"
"-- Returns\n"
"-- segment: a table with one entry per segment\n"
"-----------------------------------------------------------------------------\n"
"function parse_path(path)\n"
" local parsed = {}\n"
" path = path or \"\"\n"
" --path = string.gsub(path, \"%s\", \"\")\n"
" string.gsub(path, \"([^/]+)\", function (s) table.insert(parsed, s) end)\n"
" for i = 1, #parsed do\n"
" parsed[i] = unescape(parsed[i])\n"
" end\n"
" if string.sub(path, 1, 1) == \"/\" then parsed.is_absolute = 1 end\n"
" if string.sub(path, -1, -1) == \"/\" then parsed.is_directory = 1 end\n"
" return parsed\n"
"end\n"
"\n"
"-----------------------------------------------------------------------------\n"
"-- Builds a path component from its segments, escaping protected characters.\n"
"-- Input\n"
"-- parsed: path segments\n"
"-- unsafe: if true, segments are not protected before path is built\n"
"-- Returns\n"
"-- path: corresponding path stringing\n"
"-----------------------------------------------------------------------------\n"
"function build_path(parsed, unsafe)\n"
" local path = \"\"\n"
" local n = #parsed\n"
" if unsafe then\n"
" for i = 1, n-1 do\n"
" path = path .. parsed[i]\n"
" path = path .. \"/\"\n"
" end\n"
" if n > 0 then\n"
" path = path .. parsed[n]\n"
" if parsed.is_directory then path = path .. \"/\" end\n"
" end\n"
" else\n"
" for i = 1, n-1 do\n"
" path = path .. protect_segment(parsed[i])\n"
" path = path .. \"/\"\n"
" end\n"
" if n > 0 then\n"
" path = path .. protect_segment(parsed[n])\n"
" if parsed.is_directory then path = path .. \"/\" end\n"
" end\n"
" end\n"
" if parsed.is_absolute then path = \"/\" .. path end\n"
" return path\n"
"end\n"
"\n"
"return _ENV\n"
"\n"
"end\n"
"--------------------------------------------------------------------------------\n"
"-- End of moduledebugger.url\n"
"--------------------------------------------------------------------------------\n"
"\n"
"\n"
"--------------------------------------------------------------------------------\n"
"-- Main content\n"
"--------------------------------------------------------------------------------\n"
"\n"
"-------------------------------------------------------------------------------\n"
"-- Copyright (c) 2011-2012 Sierra Wireless and others.\n"
"-- All rights reserved. This program and the accompanying materials\n"
"-- are made available under the terms of the Eclipse Public License v1.0\n"
"-- which accompanies this distribution, and is available at\n"
"-- http://www.eclipse.org/legal/epl-v10.html\n"
"--\n"
"-- Contributors:\n"
"-- Sierra Wireless - initial API and implementation\n"
"-------------------------------------------------------------------------------\n"
"\n"
"local DBGP_CLIENT_VERSION = \"1.1.0\"\n"
"\n"
"local debug = require \"debug\"\n"
"\n"
"-- To avoid cyclic dependency, internal state of the debugger that must be accessed\n"
"-- elsewhere (in commands most likely) will be stored in a fake module \"debugger.core\"\n"
"local core = { }\n"
"package.loaded[\"debugger.core\"] = core\n"
"\n"
"local util = require \"debugger.util\"\n"
"local platform = require \"debugger.platform\"\n"
"local dbgp = require \"debugger.dbgp\"\n"
"local commands = require \"debugger.commands\"\n"
"local context = require \"debugger.context\"\n"
"local url = require \"debugger.url\"\n"
"\n"
"local log = util.log\n"
"\n"
"\n"
"-- TODO complete the stdlib access\n"
"local corunning, cocreate, cowrap, coyield, coresume, costatus = coroutine.running, coroutine.create, coroutine.wrap, coroutine.yield, coroutine.resume, coroutine.status\n"
"\n"
"\n"
"-- register the URI of the debugger, to not jump into with redefined function or coroutine bootstrap stuff\n"
"local debugger_uri = nil -- set in init function\n"
"local transportmodule_uri = nil -- set in init function\n"
"\n"
"-- will contain the session object, and possibly a list of all sessions if a multi-threaded model is adopted\n"
"-- this is only used for async commands.\n"
"local active_session = nil\n"
"\n"
"-- tracks all active coroutines and associate an id to them, the table from_id is the id=>coro mapping, the table from_coro is the reverse\n"
"core.active_coroutines = { n = 0, from_id = setmetatable({ }, { __mode = \"v\" }), from_coro = setmetatable({ }, { __mode = \"k\" }) }\n"
"\n"
"core.prev_break_line = nil -- set in line_hook function\n"
"\n"
"-- \"BEGIN VERSION DEPENDENT CODE\"\n"
"local setbpenv -- set environment of a breakpoint (compiled function)\n"
"if _VERSION == \"Lua 5.1\" then\n"
" local setfenv = setfenv\n"
" setbpenv = setfenv\n"
"elseif _VERSION == \"Lua 5.2\" then\n"
" local setupvalue = debug.setupvalue\n"
" -- _ENV is the first upvalue\n"
" setbpenv = function(f, t) return setupvalue(f, 1, t) end\n"
"else error(_VERSION .. \"is not supported.\") end\n"
"-- \"END VERSION DEPENDENT CODE\"\n"
"\n"
"-------------------------------------------------------------------------------\n"
"-- Output redirection handling\n"
"-------------------------------------------------------------------------------\n"
"-- Override standard output functions & constants to redirect data written to these files to IDE too.\n"
"-- This works only for output done in Lua, output written by C extensions is still go to system output file.\n"
"\n"
"-- references to native values\n"
"io.base = { output = io.output, stdin = io.stdin, stdout = io.stdout, stderr = io.stderr }\n"
"\n"
"-- comment out by guanyu_yan, not redirect io.\n"
"-- function print(...)\n"
"-- local buf = {...}\n"
"-- for i=1, select(\"#\", ...) do\n"
"-- buf[i] = tostring(buf[i])\n"
"-- end\n"
"-- io.stdout:write(table.concat(buf, \"\\t\") .. \"\\n\")\n"
"-- end\n"
"\n"
"-- Actually change standard output file but still return the \"fake\" stdout\n"
"function io.output(output)\n"
" io.base.output(output)\n"
" return io.stdout\n"
"end\n"
"\n"
"local dummy = function() end\n"
"\n"
"-- metatable for redirecting output (not printed at all in actual output)\n"
"core.redirect_output = {\n"
" write = function(self, ...)\n"
" local buf = {...}\n"
" for i=1, select(\"#\", ...) do buf[i] = tostring(buf[i]) end\n"
" buf = table.concat(buf):gsub(\"\\n\", \"\\r\\n\")\n"
" dbgp.send_xml(self.skt, { tag = \"stream\", attr = { type=self.mode }, util.b64(buf) } )\n"
" end,\n"
" flush = dummy,\n"
" close = dummy,\n"
" setvbuf = dummy,\n"
" seek = dummy\n"
"}\n"
"core.redirect_output.__index = core.redirect_output\n"
"\n"
"-- metatable for cloning output (outputs to actual system and send to IDE)\n"
"core.copy_output = {\n"
" write = function(self, ...)\n"
" core.redirect_output.write(self, ...)\n"
" io.base[self.mode]:write(...)\n"
" end,\n"
" flush = function(self, ...) return self.out:flush(...) end,\n"
" close = function(self, ...) return self.out:close(...) end,\n"
" setvbuf = function(self, ...) return self.out:setvbuf(...) end,\n"
" seek = function(self, ...) return self.out:seek(...) end,\n"
"}\n"
"core.copy_output.__index = core.copy_output\n"
"\n"
"-------------------------------------------------------------------------------\n"
"-- Breakpoint registry\n"
"-------------------------------------------------------------------------------\n"
"-- Registry of current stack levels of all running threads\n"
"local stack_levels = setmetatable( { }, { __mode = \"k\" } )\n"
"\n"
"-- File/line mapping for breakpoints (BP). For a given file/line, a list of BP is associated (DBGp specification section 7.6.1\n"
"-- require that multiple BP at same place must be handled)\n"
"-- A BP is a table with all additional properties (type, condition, ...) the id is the string representation of the table.\n"
"core.breakpoints = {\n"
" -- functions to call to match hit conditions\n"
" hit_conditions = {\n"
" [\">=\"] = function(value, target) return value >= target end,\n"
" [\"==\"] = function(value, target) return value == target end,\n"
" [\"%\"] = function(value, target) return (value % target) == 0 end,\n"
" }\n"
"}\n"
"\n"
"-- tracks events such as step_into or step_over\n"
"core.events = { }\n"
"\n"
"do\n"
" local file_mapping = { }\n"
" local id_mapping = { }\n"
" local waiting_sessions = { } -- sessions that wait for an event (over, into, out)\n"
" local step_into = nil -- session that registered a step_into event, if any\n"
" local sequence = 0 -- used to generate breakpoint IDs\n"
" local line_mapping = {} -- use to record the line of bp\n"
"\n"
" local function insert_line_mapping(line)\n"
" if line_mapping[line] == nil then\n"
" line_mapping[line] = 1\n"
" else\n"
" line_mapping[line] = line_mapping[line] + 1\n"
" end\n"
" end\n"
"\n"
" local function remove_line_mapping(line)\n"
" if not line_mapping[line] and line_mapping[line] > 1 then\n"
" line_mapping[line] = line_mapping[line] - 1\n"
" else\n"
" line_mapping[line] = nil\n"
" end\n"
" end\n"
"\n"
" function core.breakpoints.guess(line)\n"
" return line_mapping[line]\n"
" end\n"
"\n"
" function core.breakpoints.update(oldline,newbp)\n"
" local file = newbp.filename\n"
" local line = oldline\n"
" local filereg = file_mapping[file]\n"
" if not filereg then return nil end\n"
" local linereg = filereg[line]\n"
" if not linereg then return nil end\n"
" remove_line_mapping(oldline)\n"
" for i=1, #linereg do\n"
" if linereg[i].id == newbp.id then\n"
" table.remove(linereg, i)\n"
" break\n"
" end\n"
" end\n"
" linereg = {}\n"
" insert_line_mapping(newbp.lineno)\n"
" table.insert(linereg, newbp)\n"
" filereg[newbp.lineno] = linereg\n"
" end\n"
"\n"
" --- Inserts a new breakpoint into registry\n"
" -- @param bp (table) breakpoint data\n"
" -- @param uri (string, optional) Absolute file URI, for line breakpoints\n"
" -- @param line (number, optional) Line where breakpoint stops, for line breakpoints\n"
" -- @return breakpoint identifier\n"
" function core.breakpoints.insert(bp)\n"
" local bpid = sequence\n"
" sequence = bpid + 1\n"
" bp.id = bpid\n"
" -- re-encode the URI to avoid any mismatch (with authority for example)\n"
" local uri = url.parse(bp.filename)\n"
" bp.filename = url.build{ scheme=uri.scheme, authority=\"\", path=platform.normalize(uri.path)}\n"
"\n"
" local filereg = file_mapping[bp.filename]\n"
" if not filereg then\n"
" filereg = { }\n"
" file_mapping[bp.filename] = filereg\n"
" end\n"
"\n"
" local linereg = filereg[bp.lineno]\n"
" if not linereg then\n"
" linereg = {}\n"
" filereg[bp.lineno] = linereg\n"
" end\n"
" insert_line_mapping(bp.lineno)\n"
"\n"
" table.insert(linereg, bp)\n"
"\n"
" id_mapping[bpid] = bp\n"
" return bpid\n"
" end\n"
"\n"
" --- If breakpoint(s) exists for given file/line, uptates breakpoint counters\n"
" -- and returns whether a breakpoint has matched (boolean)\n"
" function core.breakpoints.at(file, line)\n"
" local temp = file:sub(#\"file://\"+1)\n"
" for k, _ in pairs(file_mapping) do\n"
" i,j = k:find(temp)\n"
" if(j == #k) then\n"
" file = k\n"
" break;\n"
" end\n"
" end\n"
" local bps = file_mapping[file] and file_mapping[file][line]\n"
" if not bps then return nil end\n"
"\n"
" local do_break = false\n"
" for _, bp in pairs(bps) do\n"
" if bp.state == \"enabled\" then\n"
" local match = true\n"
" if bp.condition then\n"
" -- TODO: this is not the optimal solution because Context can be instantiated twice if the breakpoint matches\n"
" local cxt = context.Context:new(active_session.coro, 0)\n"
" setbpenv(bp.condition, cxt)\n"
" local success, result = pcall(bp.condition)\n"
" if not success then log(\"ERROR\", \"Condition evaluation failed for breakpoint at %s:%d: %s\", file, line, result) end\n"
" -- debugger always stops if an error occurs\n"
" match = (not success) or result\n"
" end\n"
" if match then\n"
" bp.hit_count = bp.hit_count + 1\n"
" if core.breakpoints.hit_conditions[bp.hit_condition](bp.hit_count, bp.hit_value) then\n"
" if bp.temporary then\n"
" core.breakpoints.remove(bp.id)\n"
" end\n"
" do_break = true\n"
" -- there is no break to handle multiple breakpoints: all hit counts must be updated\n"
" end\n"
" end\n"
" end\n"
" end\n"
" return do_break\n"
" end\n"
"\n"
" function core.breakpoints.get(id)\n"
" if id then return id_mapping[id]\n"
" else return id_mapping end\n"
" end\n"
"\n"
" function core.breakpoints.remove(id)\n"
" local bp = id_mapping[id]\n"
" if bp then\n"
" id_mapping[id] = nil\n"
" local linereg = file_mapping[bp.filename][bp.lineno]\n"
" for i=1, #linereg do\n"
" if linereg[i] == bp then\n"
" table.remove(linereg, i)\n"
" break\n"
" end\n"
" end\n"
"\n"
" remove_line_mapping(bp.lineno)\n"
"\n"
" -- cleanup file_mapping\n"
" if not next(linereg) then file_mapping[bp.filename][bp.lineno] = nil end\n"
" if not next(file_mapping[bp.filename]) then file_mapping[bp.filename] = nil end\n"
" return true\n"
" end\n"
" return false\n"
" end\n"
"\n"
" --- Returns an XML data structure that describes given breakpoint\n"
" -- @param id (number) breakpoint ID\n"
" -- @return Table describing a <breakpooint> tag or nil followed by an error message\n"
" function core.breakpoints.get_xml(id)\n"
" local bp = id_mapping[id]\n"
" if not bp then return nil, \"No such breakpoint: \"..tostring(id) end\n"
"\n"
" local response = { tag = \"breakpoint\", attr = { } }\n"
" for k,v in pairs(bp) do response.attr[k] = v end\n"
" if bp.expression then\n"
" response[1] = { tag = \"expression\", bp.expression }\n"
" end\n"
"\n"
" -- internal use only\n"
" response.attr.expression = nil\n"
" response.attr.condition = nil\n"
" response.attr.temporary = nil -- TODO: the specification is not clear whether this should be provided, see other implementations\n"
" return response\n"
" end\n"
"\n"
" --- Register an event to be triggered.\n"
" -- @param event event name to register (must be \"over\", \"out\" or \"into\")\n"
" function core.events.register(event)\n"
" local thread = active_session.coro[1]\n"
" log(\"DEBUG\", \"Registered %s event for %s (%d)\", event, tostring(thread), stack_levels[thread])\n"
" if event == \"into\" then\n"
" step_into = true\n"
" else\n"
" waiting_sessions[thread] = { event, stack_levels[thread] }\n"
" end\n"
" end\n"
"\n"
" --- Returns if an event (step into, over, out) is triggered.\n"
" -- Does *not* discard events (even if they match) as event must be discarded manually if a breakpoint match before anyway.\n"
" -- @return true if an event has matched, false otherwise\n"
" function core.events.does_match(line)\n"
" if step_into and core.prev_break_line ~= line then return true end\n"
"\n"
" local thread = active_session.coro[1]\n"
" local event = waiting_sessions[thread]\n"
" if event then\n"
" local event_type, target_level = unpack(event)\n"
" local current_level = stack_levels[thread]\n"
"\n"
" if (event_type == \"over\" and current_level <= target_level and core.prev_break_line ~= line) or -- step over\n"
" (event_type == \"out\" and current_level < target_level) then -- step out\n"
" log(\"DEBUG\", \"Event %s matched!\", event_type)\n"
" return true\n"
" end\n"
" end\n"
" return false\n"
" end\n"
"\n"
" --- Discards event for current thread (if any)\n"
" function core.events.discard()\n"
" waiting_sessions[active_session.coro[1]] = nil\n"
" step_into = nil\n"
" end\n"
"end\n"
"\n"
"-------------------------------------------------------------------------------\n"
"-- Debugger main loop\n"
"-------------------------------------------------------------------------------\n"
"\n"
"--- Send the XML response to the previous continuation command and clear the previous context\n"
"function core.previous_context_response(self, reason)\n"
" self.previous_context.status = self.state\n"
" self.previous_context.reason = reason or \"ok\"\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = self.previous_context } )\n"
" self.previous_context = nil\n"
"end\n"
"\n"
"local function cleanup()\n"
" coroutine.resume, coroutine.wrap = coresume, cowrap\n"
" for _, coro in pairs(core.active_coroutines.from_id) do\n"
" debug.sethook(coro)\n"
" end\n"
" -- to remove hook on the main coroutine, it must be the current one (otherwise, this is a no-op) and this function\n"
" -- have to be called adain later on the main thread to finish cleaup\n"
" debug.sethook()\n"
" core.active_coroutines.from_id, core.active_coroutines.from_coro = { }, { }\n"
"end\n"
"\n"
"--- This function handles the debugger commands while the execution is paused. This does not use coroutines because there is no\n"
"-- way to get main coro in Lua 5.1 (only in 5.2)\n"
"local function debugger_loop(self, async_packet)\n"
" self.skt:settimeout(nil) -- set socket blocking\n"
"\n"
" -- in async mode, the debugger does not wait for another command before continuing and does not modify previous_context\n"
" local async_mode = async_packet ~= nil\n"
"\n"
" if self.previous_context and not async_mode then\n"
" self.state = \"break\"\n"
" core.previous_context_response(self)\n"
" end\n"
" self.stack = context.ContextManager(self.coro) -- will be used to mutualize context allocation for each loop\n"
"\n"
" while true do\n"
" -- reads packet\n"
" local packet = async_packet or dbgp.read_packet(self.skt)\n"
" if not packet then\n"
" log(\"WARNING\", \"lost debugger connection\")\n"
" cleanup()\n"
" break\n"
" end\n"
"\n"
" async_packet = nil\n"
" log(\"DEBUG\", packet)\n"
" local cmd, args, data = dbgp.cmd_parse(packet)\n"
"\n"
" -- FIXME: command such as continuations sent in async mode could lead both engine and IDE in inconsistent state :\n"
" -- make a blacklist/whitelist of forbidden or allowed commands in async ?\n"
" -- invoke function\n"
" local func = commands[cmd]\n"
" if func then\n"
" local ok, cont = xpcall(function() return func(self, args, data) end, debug.traceback)\n"
" if not ok then -- internal exception\n"
" local code, msg, attr\n"
" if type(cont) == \"table\" and getmetatable(cont) == dbgp.DBGP_ERR_METATABLE then\n"
" code, msg, attr = cont.code, cont.message, cont.attr\n"
" else\n"
" code, msg, attr = 998, tostring(cont), { }\n"
" end\n"
" log(\"ERROR\", \"Command %s caused: (%d) %s\", cmd, code, tostring(msg))\n"
" attr.command, attr.transaction_id = cmd, args.i\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = attr, dbgp.make_error(code, msg) } )\n"
" elseif cont then\n"
" self.previous_context = { command = cmd, transaction_id = args.i }\n"
" break\n"
" elseif cont == nil and async_mode then\n"
" break\n"
" elseif cont == false then -- In case of commands that fully resumes debugger loop, the mode is sync\n"
" async_mode = false\n"
" end\n"
" else\n"
" log(\"Got unknown command: \"..cmd)\n"
" dbgp.send_xml(self.skt, { tag = \"response\", attr = { command = cmd, transaction_id = args.i, }, dbgp.make_error(4) } )\n"
" end\n"
" end\n"
"\n"
" self.stack = nil -- free allocated contexts\n"
" self.state = \"running\"\n"
" self.skt:settimeout(0) -- reset socket to async\n"
"end\n"
"\n"
"-- Stack handling can be pretty complex sometimes, especially with LuaJIT (as tail-call optimization are\n"
"-- more aggressive as stock Lua). So all debugger stuff is done in another coroutine, which leave the program\n"
"-- stack in a clean state and allow faster and clearer stack operations (no need to remove all debugger calls\n"
"-- from stack for each operation).\n"
"-- However, this does not always work with stock Lua 5.1 as the main coroutine cannot be referenced\n"
"-- (coroutine.running() return nil). For this particular case, the debugger loop is started on the top of\n"
"-- program stack and every stack operation is relative the the hook level (see MainThread in util.lua).\n"
"local function line_hook(line)\n"
" local do_break, packet = nil, nil\n"
"\n"
" if (core.prev_break_line ~= line) then\n"
" if core.breakpoints.guess(line) ~= nil then\n"
" local info = active_session.coro:getinfo(0, \"S\")\n"
" local uri = platform.get_uri(info.source)\n"
" if uri and uri ~= debugger_uri and uri ~= transportmodule_uri then -- the debugger does not break if the source is not known\n"
" do_break = core.breakpoints.at(uri, line) --or core.events.does_match()\n"
" end\n"
" end\n"
" end\n"
"\n"
" if not do_break then\n"
" do_break = core.events.does_match(line)\n"
" end\n"
" if do_break then\n"
" core.prev_break_line = line\n"
" core.events.discard()\n"
" end\n"
" -- check for async commands\n"
" if not do_break then\n"
" packet = dbgp.read_packet(active_session.skt)\n"
" if packet then do_break = true end\n"
" end\n"
" if do_break then\n"
" core.prev_break_line = line\n"
" local success, err = pcall(debugger_loop, active_session, packet)\n"
" if not success then log(\"ERROR\", \"Error while debug loop: \"..err) end\n"
" end\n"
"end\n"
"\n"
"local line_hook_coro = cocreate(function(line)\n"
" while true do\n"
" line_hook(line)\n"
" line = coyield()\n"
" end\n"
"end)\n"
"\n"
"local function debugger_hook(event, line)\n"
" local thread = corunning() or \"main\"\n"
" if event == \"call\" then\n"
" stack_levels[thread] = stack_levels[thread] + 1\n"
" elseif event == \"tail call\" then\n"
" -- tail calls has no effects on stack handling: it is only used only for step commands but a such even does not\n"
" -- interfere with any of them\n"
" elseif event == \"return\" or event == \"tail return\" then\n"
" stack_levels[thread] = stack_levels[thread] - 1\n"
" else -- line event: check for breakpoint\n"
" active_session.coro = util.CurrentThread(corunning())\n"
" if active_session.coro[1] == \"main\" then\n"
" line_hook(line)\n"
" else\n"
" -- run the debugger loop in another thread on the other cases (simplifies stack handling)\n"
" assert(coresume(line_hook_coro, line))\n"
" end\n"
" active_session.coro = nil\n"
" end\n"
"end\n"
"\n"
"if rawget(_G, \"jit\") then\n"
" debugger_hook = function(event, line)\n"
" local thread = corunning() or \"main\"\n"
" if event == \"call\" then\n"
" if debug.getinfo(2, \"S\").what == \"C\" then return end\n"
" stack_levels[thread] = stack_levels[thread] + 1\n"
" elseif event == \"return\" or event == \"tail return\" then\n"
" -- Return hooks are not called for tail calls in JIT (but unlike 5.2 there is no way to know whether a call is tail or not).\n"
" -- So the only reliable way to know stack depth is to walk it.\n"
" local depth = 2\n"
" -- TODO: find the fastest way to call getinfo ('what' parameter)\n"
" while debug.getinfo(depth, \"f\") do depth = depth + 1 end\n"
" stack_levels[thread] = depth - 2\n"
" elseif event == \"line\" then\n"
" active_session.coro = util.CurrentThread(corunning())\n"
" if active_session.coro[1] == \"main\" then\n"
" line_hook(line)\n"
" else\n"
" -- run the debugger loop in another thread on the other cases (simplifies stack handling)\n"
" assert(coresume(line_hook_coro, line))\n"
" end\n"
" active_session.coro = nil\n"
" end\n"
" end\n"
"end\n"
"\n"
"local function init(host, port, idekey, transport, executionplatform, workingdirectory, searchpaths)\n"
" -- get connection data\n"
" local host = host or os.getenv \"DBGP_IDEHOST\" or \"127.0.0.1\"\n"
" local port = port or os.getenv \"DBGP_IDEPORT\" or \"10000\"\n"
" local idekey = idekey or os.getenv(\"DBGP_IDEKEY\") or \"luaidekey\"\n"
"\n"
" -- init plaform module\n"
" local executionplatform = executionplatform or os.getenv(\"DBGP_PLATFORM\") or nil\n"
" local workingdirectory = workingdirectory or os.getenv(\"DBGP_WORKINGDIR\") or nil\n"
" platform.init(executionplatform,workingdirectory,searchpaths)\n"
"\n"
" -- get transport layer\n"
" local transportpath = transport or os.getenv(\"DBGP_TRANSPORT\") or \"debugger.transport.luasocket\"\n"
" local transport = require(transportpath)\n"
"\n"
" -- install base64 functions into util\n"
" util.b64, util.rawb64, util.unb64 = transport.b64, transport.rawb64, transport.unb64\n"
"\n"
" local skt = assert(transport.create())\n"
" skt:settimeout(nil)\n"
"\n"
" -- try to connect several times: if IDE launches both process and server at same time, first connect attempts may fail\n"
" local ok, err\n"
" print(string.format(\"Debugger v%s\", DBGP_CLIENT_VERSION))\n"
" print(string.format(\"Debugger: Trying to connect to %s:%s ... \", host, port))\n"
" ok, err = skt:connect(host, port)\n"
" for i=1, 4 do\n"
" if ok then\n"
" print(\"Debugger: Connection succeed.\")\n"
" break\n"
" else\n"
" -- wait\n"
" transport.sleep(0.5)\n"
" -- then retry.\n"
" print(string.format(\"Debugger: Retrying to connect to %s:%s ... \", host, port))\n"
" ok, err = skt:connect(host, port)\n"
" end\n"
" end\n"
" if err then error(string.format(\"Cannot connect to %s:%d : %s\", host, port, err)) end\n"
"\n"
" -- get the debugger and transport layer URI\n"
" debugger_uri = platform.get_uri(debug.getinfo(1).source)\n"
" transportmodule_uri = platform.get_uri(debug.getinfo(transport.create).source)\n"
"\n"
" -- get the root script path (the highest possible stack index)\n"
" local source\n"
" for i=2, math.huge do\n"
" local info = debug.getinfo(i)\n"
" if not info then break end\n"
" source = platform.get_uri(info.source) or source\n"
" end\n"
" if not source then source = \"unknown:/\" end -- when loaded before actual script (with a command line switch)\n"
"\n"
" -- generate some kind of thread identifier\n"
" local thread = corunning() or \"main\"\n"
" stack_levels[thread] = 1 -- the return event will set the counter to 0\n"
" local sessionid = tostring(os.time()) .. \"_\" .. tostring(thread)\n"
"\n"
" dbgp.send_xml(skt, { tag = \"init\", attr = {\n"
" appid = \"Lua DBGp\",\n"
" idekey = idekey,\n"
" session = sessionid,\n"
" thread = tostring(thread),\n"
" parent = \"\",\n"
" language = \"Lua\",\n"
" protocol_version = \"1.0\",\n"
" fileuri = source\n"
" } })\n"
"\n"
" --FIXME util.CurrentThread(corunning) => util.CurrentThread(corunning()) WHAT DOES IT FIXES ??\n"
" local sess = { skt = skt, state = \"starting\", id = sessionid, coro = util.CurrentThread(corunning) }\n"
" active_session = sess\n"
" debugger_loop(sess)\n"
"\n"
" -- set debug hooks\n"
" debug.sethook(debugger_hook, \"rlc\")\n"
"\n"
" -- install coroutine collecting functions.\n"
" -- TODO: maintain a list of *all* coroutines can be overkill (for example, the ones created by copcall), make a extension point to\n"
" -- customize debugged coroutines\n"
" -- coroutines are referenced during their first resume (so we are sure that they always have a stack frame)\n"
" local function resume_handler(coro, ...)\n"
" if costatus(coro) == \"dead\" then\n"
" local coro_id = core.active_coroutines.from_coro[coro]\n"
" core.active_coroutines.from_id[coro_id] = nil\n"
" core.active_coroutines.from_coro[coro] = nil\n"
" stack_levels[coro] = nil\n"
" end\n"
" return ...\n"
" end\n"
"\n"
" function coroutine.resume(coro, ...)\n"
" if not stack_levels[coro] then\n"
" -- first time referenced\n"
" stack_levels[coro] = 0\n"
" core.active_coroutines.n = core.active_coroutines.n + 1\n"
" core.active_coroutines.from_id[core.active_coroutines.n] = coro\n"
" core.active_coroutines.from_coro[coro] = core.active_coroutines.n\n"
" debug.sethook(coro, debugger_hook, \"rlc\")\n"
" end\n"
" return resume_handler(coro, coresume(coro, ...))\n"
" end\n"
"\n"
" -- coroutine.wrap uses directly C API for coroutines and does not trigger our overridden coroutine.resume\n"
" -- so this is an implementation of wrap in pure Lua\n"
" local function wrap_handler(status, ...)\n"
" if not status then error((...)) end\n"
" return ...\n"
" end\n"
"\n"
" function coroutine.wrap(f)\n"
" local coro = coroutine.create(f)\n"
" return function(...)\n"
" return wrap_handler(coroutine.resume(coro, ...))\n"
" end\n"
" end\n"
"\n"
" return sess\n"
"end\n"
"\n"
"return init\n"
"\n";
int luaopen_lua_m_debugger(lua_State *L) {
luaL_loadstring(L, lua_m_debugger);
return 1;
}
static luaL_Reg lua_debugger_modules[] = {
{"debugger", luaopen_lua_m_debugger},
{NULL, NULL}
};
void luaopen_lua_debugger(lua_State* L)
{
luaL_Reg* lib = lua_debugger_modules;
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
for (; lib->func; lib++)
{
lib->func(L);
lua_setfield(L, -2, lib->name);
}
lua_pop(L, 2);
}