-------------------------------------------------------------------------------
-- MOOSE VERSION for DCS WORLD
-- Modified by FlightControl to understand "DCS".
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Debugger using DBGp protocol.
-------------------------------------------------------------------------------
-- The module returns a single init function which takes 7 parameters (IDEHOST, IDEPORT, IDEKEY, TRANSPORT, PLATFORM, WORKINGDIR, NBRETRY).
--
-- IDEHOST: the host name or the ip address of the DBGP server (so your ide)
-- if HOST is nil, the DBGP_IDEHOST env var is used.
-- if the env var is nil, the default value '127.0.0.1' is used.
--
-- IDEPORT: the port of the DBGP server (must be configure in the IDE) 
-- if PORT is nil, the DBGP_IDEPORT env var is used.
-- if the env var is nil, the default value '10000' is used.
--
-- IDEIDEKEY: a string which is used as session key
-- if IDEKEY is nil, the DBGP_IDEKEY env var is used.
-- if the env var is nil, the default value 'luaidekey' is used.
--
-- TRANSPORT: (advanced optional parameter) the module name of which implement the transport interface used to do the connection with the server.
-- 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.
-- if TRANSPORT is nil, the DBGP_TRANSPORT env var is used.
-- if the env var is nil, the default value 'debugger.transport.luasocket' is used : this is the default implementation based on luasocket.
--
-- PLATFORM: (advanced optional parameter) 'unix' or 'win32' string which define the kind of platform on which the program to debug is executed.
-- 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.
-- if PLATFORM is nil, the DBGP_PLATFORM env var is used.
-- if the env var is nil, the debugger will try to guess it.
--
-- WORKINGDIR: (advanced optional parameter) the working directory in which the program to debug is executed.
-- 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. 
-- if WORKINGDIR is nil, the DBGP_WORKINGDIR env var is used.
-- if the env var is nil, the debugger will try to guess it.
--
-- NBRETRY: (advanced optional parameter) the number of connection retry at start up.
-- if NBRETRY is nil, the DBGP_NBRETRY env var is used.
-- if the env var is nil, the default value is 10.
--
-------------------------------------------------------------------------------
-- Known Issues:
--   * Functions cannot be created using the debugger and then called in program because their environment is mapped directly to
--     a debugger internal structure which cannot be persisted (i.e. used outside of the debug_hook).
--   * The DLTK client implementation does not handle context for properties. As a workaround, the context is encoded into the
--     fullname attribute of each property and is used likewise in property_get commands. The syntax is "<context ID>|<full name>"
--   * Dynamic code (compiled with load or loadstring) is not handled (the debugger will step over it, like C code)
-- Design notes:
--   * The whole debugger state is kept in a (currently) unique session table in order to ease eventual adaptation to a multi-threaded 
--     model, as DBGp needs one connection per thread.
--   * Full names of properties are base64 encoded because they can contain arbitrary data (spaces, escape characters, ...), this makes
--     command parsing munch easier and faster
--   * This debugger supports asynchronous commands: any command can be done at any time, but some of them (continuations) can lead to
--     inconsistent states. In addition, this have a quite big overhead (~66%), if performance is an issue, a custom command to disable
--     async mode could be done.
--   * All commands are implemented in table commands, see this comments on this table to additional details about commands implementation
--   * The environments in which are evaluated user code (property_* and eval commands, conditional breakpoints, ...) is a read/write 
--     mapping of the local environment of a given stack level (can be accessed with variable names). See Context for additional details.
--     Context instantiation is pooled inside a debugging loop with ContextManager (each stack level is instantiated only once).
--   * Output redirection is done by redefining print and some values inside the io table. See "Output redirection handling" for details.
-- Todo list:
--   * Override I/O in init function instead of on module loading.
--   * Allow to break programatically (debugger.break()).
--   * Break-on-error feature (break if an error is thrown and there is no pcall in stack to handle it).
--   * Use new 5.2 facilities to provide informations about function (arguments names, vararg, ...)
--   * Allow to see ... content for vararg functions (5.2 only)
--   * Inspect LuaJIT C data (http://lua-users.org/lists/lua-l/2011-02/msg01012.html)-- /!\ This file is auto-generated. Do not alter manually /!\

--------------------------------------------------------------------------------
--  Submodules body
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--  Module debugger.transport.apr
package.preload["debugger.transport.apr"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Apache Portable Runtime backend for DBGP debugger.
-------------------------------------------------------------------------------

local apr = require "apr"

-- base 64 wrapping
function b64_wrap(src)
  local t = {}
  local b64_src = mime.b64(src)
  for i=1, #b64_src, 76 do t[#t+1] = b64_src:sub(i, i+75).."\r\n" end
  return table.concat(t)
end

-- implements a subset of LuaSocket API using APR
local SOCKET_MT = {
  connect = function(self, address, port) return self.skt:connect(address, port) end,
  receive = function(self, n)             return self.skt:read(n) end, -- only numeric read is used
  send    = function(self, data)          return self.skt:write(data) end,
  close   = function(self)                return self.skt:close() end,
  settimeout = function(self, sec)
    if     sec == nil then self.skt:timeout_set(true)
    elseif sec == 0   then self.skt:timeout_set(false)
    else                   self.skt:timeout_set(math.floor(sec * 1000000)) end
  end
}
SOCKET_MT.__index = SOCKET_MT

return {
  create = function()
    local skt, err = apr.socket_create('tcp')
    if not skt then return nil, err end
    return setmetatable({skt = skt}, SOCKET_MT)
  end,
  sleep      = apr.sleep, -- exact same API as LuaSocket

  -- Base64 related functions
  --- Encodes a string into Base64 with line wrapping
  -- @param data (string) data to encode
  -- @return base64 encoded string
  b64 = function(data)
    t = {}
    local b64_data = apr.base64_encode(data)
    for i=1, #b64_data, 76 do t[#t+1] = b64_data:sub(i, i+75).."\r\n" end
    return table.concat(t)
  end,

  --- Encodes a string into Base64, without any extra parsing (wrapping, ...)
  -- @param data (string) data to encode
  -- @return decoded string
  rawb64 = apr.base64_encode,

  --- Decodes base64 data
  -- @param data (string) base64 encoded data
  -- @return decoded string
  unb64 = apr.base64_decode,
}

end
--------------------------------------------------------------------------------
-- End of moduledebugger.transport.apr
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.transport.luasocket
package.preload["debugger.transport.luasocket"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- LuaSocket backend for DBGP debugger.
-------------------------------------------------------------------------------

-- in order to be as lightweight as possible with Luasocket, core API is used
-- directly (to no add yet another layer)

--FIXME: remove this hack as soon as luasocket officially support 5.2
if DBGP_CLIENT_LUA_VERSION == "Lua 5.2" then
  table.getn = function(t) return t and #t end
end

local socket = require "socket"
local mime   = require "mime"
local ltn12  = require "ltn12"
local reg = debug.getregistry()


return {
  create = socket.tcp,
  sleep  = socket.sleep,

  -- Base64 related functions
  --- Encodes a string into Base64 with line wrapping
  -- @param data (string) data to encode
  -- @return base64 encoded string
  b64 = function(data)
    local filter = ltn12.filter.chain(mime.encode("base64"), mime.wrap("base64"))
    local sink, output = ltn12.sink.table()
    ltn12.pump.all(ltn12.source.string(data), ltn12.sink.chain(filter, sink))
    return table.concat(output)
  end,

  --- Encodes a string into Base64, without any extra parsing (wrapping, ...)
  -- @param data (string) data to encode
  -- @return decoded string
  rawb64 = function(data)
    return (mime.b64(data)) -- first result of the low-level function is fine here
  end,

  --- Decodes base64 data
  -- @param data (string) base64 encoded data
  -- @return decoded string
  unb64 = function(data)
    return (mime.unb64(data)) -- first result of the low-level function is fine here
  end,
}

end
--------------------------------------------------------------------------------
-- End of moduledebugger.transport.luasocket
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.transport.luasocket_sched
package.preload["debugger.transport.luasocket_sched"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- LuaSocket with LuaSched backend for DBGP debugger.
-------------------------------------------------------------------------------

-- As LuaShed totally hides blocking functions, this module MUST be loaded on the very start of the program
-- (before loading sched) to catch references to blocking functions.

local socketcore = require"socket.core"
local debug      = require "debug"
local reg = debug.getregistry()

local blockingcreate  = socketcore.tcp
local blockingsleep   = socketcore.sleep

local blockingconnect    = reg["tcp{master}"].__index.connect
local blockingreceive    = reg["tcp{client}"].__index.receive
local blockingsend       = reg["tcp{client}"].__index.send
local blockingsettimeout = reg["tcp{master}"].__index.settimeout
local blockingclose      = reg["tcp{master}"].__index.close

-- we cannot set a new metatable directly on socket object, so wrap it into a new table
-- and forward all calls.
local blockingtcp = {
  connect    = function(self, address, port) return blockingconnect(self.skt, address, port) end,
  receive    = function(self, n)             return blockingreceive(self.skt, n) end,
  send       = function(self, data)          return blockingsend(self.skt, data) end,
  settimeout = function(self, sec)           return blockingsettimeout(self.skt, sec) end,
  close      = function(self)                return blockingclose(self.skt) end,
}

blockingtcp.__index = blockingtcp

local mime  = require "mime"
local ltn12 = require "ltn12"

-- verify that the socket function are the real ones and not sched not blocking versions
assert(debug.getinfo(blockingcreate, "S").what == "C", "The debugger needs the real socket functions !")
-- cleanup the package.loaded table (socket.core adds socket field into it)
package.loaded.socket = nil

return {
  create = function() return setmetatable({ skt = blockingcreate() }, blockingtcp) end,
  sleep  = blockingsleep,

  -- Base64 related functions
  --- Encodes a string into Base64 with line wrapping
  -- @param data (string) data to encode
  -- @return base64 encoded string
  b64 = function(data)
    local filter = ltn12.filter.chain(mime.encode("base64"), mime.wrap("base64"))
    local sink, output = ltn12.sink.table()
    ltn12.pump.all(ltn12.source.string(data), ltn12.sink.chain(filter, sink))
    return table.concat(output)
  end,

  --- Encodes a string into Base64, without any extra parsing (wrapping, ...)
  -- @param data (string) data to encode
  -- @return decoded string
  rawb64 = function(data)
    return (mime.b64(data)) -- first result of the low-level function is fine here
  end,

  --- Decodes base64 data
  -- @param data (string) base64 encoded data
  -- @return decoded string
  unb64 = function(data)
    return (mime.unb64(data)) -- first result of the low-level function is fine here
  end,
}

end
--------------------------------------------------------------------------------
-- End of moduledebugger.transport.luasocket_sched
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.commands
package.preload["debugger.commands"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Commands handlers for DBGp protocol.
-------------------------------------------------------------------------------
-- Debugger command functions. Each function handle a different command.
-- A command function is called with 3 arguments
--   1. the debug session instance
--   2. the command arguments as table
--   3. the command data, if any
-- The result is either :
--   * true (or any value evaluated to true) : the debugger will resume the execution of the application (continuation command)
--   * false : only in async mode, the debugger WILL wait for further commands instead of continuing (typically, break command)
--   * nil/no return : in sync mode, the debugger will wait for another command. In async mode the debugger will continue the execution

local cowrap, coyield = coroutine.wrap, coroutine.yield
local debug = require "debug"

local core          = require "debugger.core"
local dbgp          = require "debugger.dbgp"
local util          = require "debugger.util"
local platform      = require "debugger.platform"
local introspection = require "debugger.introspection"
local context       = require "debugger.context"
local log = util.log

local M = { } -- command handlers table

--- Gets the coroutine behind an id
-- Throws errors on unknown identifiers
-- @param  coro_id  (string or nil) Coroutine identifier or nil (current coroutine)
-- @return Coroutine instance or nil (if coro_id was nil or if coroutine is the current coroutine)
local function get_coroutine(self, coro_id)
  if coro_id then
    local coro = dbgp.assert(399, core.active_coroutines.from_id[tonumber(coro_id)], "No such coroutine")
    dbgp.assert(399, coroutine.status(coro) ~= "dead", "Coroutine is dead")
    if coro ~= self.coro[1] then return util.ForeignThread(coro) end
  end
  return self.coro
end

M["break"] = function(self, args)
  self.state = "break"
  -- send response to previous command
  core.previous_context_response(self)
  -- and then response to break command itself
  dbgp.send_xml(self.skt, { tag = "response", attr = { command = "break", transaction_id = args.i, success = 1 } } )
  return false
end

function M.status(self, args)
  dbgp.send_xml(self.skt, { tag = "response", attr = {
    command = "status",
    reason = "ok",
    status = self.state,
    transaction_id = args.i } } )
end

function M.stop(self, args)
  dbgp.send_xml(self.skt, { tag = "response", attr = {
    command = "stop",
    reason = "ok",
    status = "stopped",
    transaction_id = args.i } } )
  self.skt:close()
  os.exit(1)
end

function M.feature_get(self, args)
  local name = args.n
  local response = util.features[name] or (not not M[name])
  dbgp.send_xml(self.skt, { tag = "response", attr = {
    command = "feature_get",
    feature_name = name,
    supported = response and "1" or "0",
    transaction_id = args.i },
  tostring(response) } )
end

function M.feature_set(self, args)
  local name, value = args.n, args.v
  local success = pcall(function() util.features[name] = value end)
  dbgp.send_xml(self.skt, { tag = "response", attr = {
    command = "feature_set",
    feature = name,
    success = success and 1 or 0,
    transaction_id = args.i
  } } )
end

function M.typemap_get(self, args)
  local function gentype(name, type, xsdtype)
    return { tag = "map", atts = { name = name, type = type, ["xsi:type"] = xsdtype } }
  end

  dbgp.send_xml(self.skt, { tag = "response", attr = {
    command = "typemap_get",
    transaction_id = args.i,
    ["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance",
    ["xmlns:xsd"] = "http://www.w3.org/2001/XMLSchema",
  },
  gentype("nil", "null"),
  gentype("boolean", "bool", "xsd:boolean"),
  gentype("number", "float", "xsd:float"),
  gentype("string", "string", "xsd:string"),
  gentype("function", "resource"),
  gentype("userdata", "resource"),
  gentype("thread", "resource"),
  gentype("table", "hash"),
  gentype("sequence", "array"), -- artificial type to represent sequences (1-n continuous indexes)
  gentype("multival", "array"), -- used to represent return values
  } )
end

function M.run(self) return true end

function M.step_over(self)
  core.events.register("over")
  return true
end

function M.step_out(self)
  core.events.register("out")
  return true
end

function M.step_into(self)
  core.events.register("into")
  return true
end

function M.eval(self, args, data)
  log("DEBUG", "Going to eval "..data)
  local result, err, success
  local env = self.stack(self.coro, 0)
  -- first, try to load as expression
  -- DBGp does not support stack level here, see http://bugs.activestate.com/show_bug.cgi?id=81178
  local func, err = util.loadin("return "..data, env)

  -- if it is not an expression, try as statement (assignment, ...)
  if not func then
    func, err = util.loadin(data, env)
  end

  if func then
    success, result = pcall(function() return introspection.Multival(func()) end)
    if not success then err = result end
  end

  local response = { tag = "response", attr = { command = "eval", transaction_id = args.i } }
  if not err then
    local nresults = result.n
    if nresults == 1 then result = result[1] end

    -- store result for further use (property_*)
    -- TODO: this could be optimized: this is only used for Expressions view and totally useless for interactive console,
    --       so storing result or not could be set by an argument
    local idx
    if nresults > 0 then
      local cache = env[context.Context[-1]]
      idx = #cache + 1
      cache[idx] = result
    end

    -- 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
    response[1] = introspection.make_property(-1, result, idx or "", nil, 1, 8000, 0, nil)
    response.attr.success = 1
  else
    response.attr.success = 0
    response[1] = dbgp.make_error(206, err)
  end
  dbgp.send_xml(self.skt, response)
end

function M.breakpoint_set(self, args, data)
  if args.o and not core.breakpoints.hit_conditions[args.o] then dbgp.error(200, "Invalid hit_condition operator: "..args.o) end

  local filename, lineno = args.f, tonumber(args.n)
  local bp = {
    type = args.t,
    state = args.s or "enabled",
    temporary = args.r == "1", -- "0" or nil makes this property false
    hit_count = 0,
    filename = filename,
    lineno = lineno,
    hit_value = tonumber(args.h or 0),
    hit_condition = args.o or ">=",
  }

  if args.t == "conditional" then
    bp.expression = data
    -- the expression is compiled only once
    bp.condition = dbgp.assert(207, loadstring("return (" .. data .. ")"))
  elseif args.t ~= "line" then dbgp.error(201, "BP type " .. args.t .. " not yet supported") end

  local bpid = core.breakpoints.insert(bp)
  dbgp.send_xml(self.skt, { tag = "response", attr = { command = "breakpoint_set", transaction_id = args.i, state = bp.state, id = bpid } } )
end

function M.breakpoint_get(self, args)
  dbgp.send_xml(self.skt, { tag = "response",
    attr = { command = "breakpoint_get", transaction_id = args.i },
    dbgp.assert(205, core.breakpoints.get_xml(tonumber(args.d))) })
end

function M.breakpoint_list(self, args)
  local bps = { tag = "response", attr = { command = "breakpoint_list", transaction_id = args.i } }
  for id, bp in pairs(core.breakpoints.get()) do bps[#bps + 1] = core.breakpoints.get_xml(id) end
  dbgp.send_xml(self.skt, bps)
end

function M.breakpoint_update(self, args)
  local bp = core.breakpoints.get(tonumber(args.d))
  if not bp then dbgp.error(205, "No such breakpint "..args.d) end
  if args.o and not core.breakpoints.hit_conditions[args.o] then dbgp.error(200, "Invalid hit_condition operator: "..args.o) end

  local response = { tag = "response", attr = { command = "breakpoint_update", transaction_id = args.i } }
  bp.state = args.s or bp.state
  bp.lineno = tonumber(args.n or bp.lineno)
  bp.hit_value = tonumber(args.h or bp.hit_value)
  bp.hit_condition = args.o or bp.hit_condition
  dbgp.send_xml(self.skt, response)
end

function M.breakpoint_remove(self, args)
  local response = { tag = "response", attr = { command = "breakpoint_remove", transaction_id = args.i } }
  if not core.breakpoints.remove(tonumber(args.d)) then dbgp.error(205, "No such breakpint "..args.d) end
  dbgp.send_xml(self.skt, response)
end

function M.stack_depth(self, args)
  local depth = 0
  local coro = get_coroutine(self, args.o)
  for level = 0, math.huge do
    local info = coro:getinfo(level, "St")
    if not info then break end -- end of stack
    depth = depth + 1
    if info.istailcall then depth = depth + 1 end -- a 'fake' level is added in that case
    if info.what == "main" then break end -- levels below main chunk are not interesting
  end
  dbgp.send_xml(self.skt, { tag = "response", attr = { command = "stack_depth", transaction_id = args.i, depth = depth} } )
end

function M.stack_get(self, args) -- TODO: dynamic code
  -- special URIs to identify unreachable stack levels
  local what2uri = {
    tail = "tailreturn:/",
    C    = "ccode:/",
  }

local function make_level(info, level)
  local attr = { level = level, where = info.name, type="file" }
  local uri = platform.get_uri(info.source)
  if uri and info.currentline then -- reachable level
    attr.filename = uri
    attr.lineno = info.currentline
  else
    attr.filename = what2uri[info.what] or "unknown:/"
    attr.lineno = -1
  end
  return { tag = "stack", attr = attr }
end

local node = { tag = "response", attr = { command = "stack_get", transaction_id = args.i} }
local coro = get_coroutine(self, args.o)

if args.d then
  local stack_level = tonumber(args.d)
  node[#node+1] = make_level(coro:getinfo(stack_level, "nSl"), stack_level)
else
  for i=0, math.huge do
    local info = coro:getinfo(i, "nSlt")
    if not info then break end
    node[#node+1] = make_level(info, i)
    -- add a fake level of stack for tail calls (tells user that the function has not been called directly)
    if info.istailcall then
      node[#node+1] = { tag = "stack", attr = { level=i, type="file", filename="tailreturn:/", lineno=-1 } }
    end
    if info.what == "main" then break end -- levels below main chunk are not interesting
  end
end

dbgp.send_xml(self.skt, node)
end

--- Lists all active coroutines.
-- Returns a list of active coroutines with their id (an arbitrary string) to query stack and properties. The id is
-- guaranteed to be unique and stable for all coroutine life (they can be reused as long as coroutine exists).
-- Others commands such as stack_get or property_* commands takes an additional -o switch to query a particular cOroutine.
-- If the switch is not given, running coroutine will be used.
-- In case of error on coroutines (most likely coroutine not found or dead), an error 399 is thrown.
-- Note there is an important limitation due to Lua 5.1 coroutine implementation: you cannot query main "coroutine" from
-- another one, so main coroutine is not in returned list (this will change with Lua 5.2).
--
-- This is a non-standard command. The returned XML has the following strucuture:
--     <response command="coroutine_list" transaction_id="0">
--       <coroutine name="<some printtable name>" id="<coroutine id>" running="0|1" />
--       ...
--     </response>
function M.coroutine_list(self, args)
  local running = self.coro[1]
  local coroutines = { tag = "response", attr = { command = "coroutine_list", transaction_id = args.i } }
  -- as any operation on main coroutine will fail, it is not yet listed
  -- coroutines[1] = { name = "coroutine", attr = { id = 0, name = "main", running = (running == nil) and "1" or "0" } }
  for id, coro in pairs(core.active_coroutines.from_id) do
    if id ~= "n" then
      coroutines[#coroutines + 1] = { tag = "coroutine", attr = { id = id, name = tostring(coro), running = (coro == running) and "1" or "0" } }
    end
  end
  dbgp.send_xml(self.skt, coroutines)
end

function M.context_names(self, args)
  local coro = get_coroutine(self, args.o)
  local level = tonumber(args.d or 0)
  local info = coro:getinfo(level, "f") or dbgp.error(301, "No such stack level "..tostring(level))

  -- All contexts are always passed, even if empty. This is how DLTK expect context, what about others ?
  local contexts = {
    tag = "response", attr = { command = "context_names", transaction_id = args.i },
    { tag = "context", attr = { name = "Local",   id = 0 } },
    { tag = "context", attr = { name = "Upvalue", id = 2 } },
    { tag = "context", attr = { name = "Global",  id = 1 } },
  }

  dbgp.send_xml(self.skt, contexts)
end

function M.context_get(self, args)
  local cxt_num = tonumber(args.c or 0)
  local cxt_id = context.Context[cxt_num] or dbgp.error(302, "No such context: "..tostring(cxt_num))
  local level = tonumber(args.d or 0)
  local coro = get_coroutine(self, args.o)
  local cxt = self.stack(coro, level)

  local properties = { tag = "response", attr = { command = "context_get", transaction_id = args.i, context = context} }
  -- iteration over global is different (this could be unified in Lua 5.2 thanks to __pairs metamethod)
  for name, val in (cxt_num == 1 and next or getmetatable(cxt[cxt_id]).iterator), cxt[cxt_id], nil do
    -- the DBGp specification is not clear about the depth of a context_get, but a recursive get could be *really* slow in Lua
    properties[#properties + 1] = introspection.make_property(cxt_num, val, name, nil, 0, util.features.max_children, 0,
      util.features.max_data, cxt_num ~= 1)
  end

  dbgp.send_xml(self.skt, properties)
end

-------------------------------------------------------------------------------
--  Property_* commands
-------------------------------------------------------------------------------
-- This in the environment in which properties are get or set.
-- It notably contain a collection of proxy table which handle transparentely get/set operations on special fields
-- and the cache of complex keys.
local property_evaluation_environment = {
  key_cache = introspection.key_cache,
  metatable = setmetatable({ }, {
    __index = function(self, tbl) return getmetatable(tbl) end,
    __newindex = function(self, tbl, mt) return setmetatable(tbl, mt) end,
  }),
  environment = util.eval_env,
}
-- to allows to be set as metatable
property_evaluation_environment.__index = property_evaluation_environment

function M.property_get(self, args)
  --TODO BUG ECLIPSE TOOLSLINUX-99 352316
  local cxt_num, name = assert(util.unb64(args.n):match("^(%-?%d+)|(.*)$"))
  cxt_num = tonumber(args.c or cxt_num)
  local cxt_id = context.Context[cxt_num] or dbgp.error(302, "No such context: "..tostring(cxt_num))
  local level = tonumber(args.d or 0)
  local coro = get_coroutine(self, args.o)
  local size = tonumber(args.m or util.features.max_data)
  if size < 0 then size = nil end -- call from property_value
  local page = tonumber(args.p or 0)
  local cxt = self.stack(coro, level)
  local chunk = dbgp.assert(206, util.loadin("return "..name, property_evaluation_environment))
  local prop = select(2, dbgp.assert(300, pcall(chunk, cxt[cxt_id])))
  local response = introspection.make_property(cxt_num, prop, name, name, util.features.max_depth, util.features.max_children, page, size)
  -- make_property is not able to flag special variables as such when they are at root of property
  -- special variables queries are in the form "<proxy name>[(...)[a][b]<...>]"
  -- TODO: such parsing is far from perfect
  if name:match("^[%w_]+%[.-%b[]%]$") == name then response.attr.type = "special" end
  dbgp.send_xml(self.skt, { tag = "response",
    attr = { command = "property_get", transaction_id = args.i, context = context},
    response } )
end

function M.property_value(self, args)
  args.m = -1
  M.property_get(self, args)
end

function M.property_set(self, args, data)
  local cxt_num, name = assert(util.unb64(args.n):match("^(%-?%d+)|(.*)$"))
  cxt_num = tonumber(args.c or cxt_num)
  local cxt_id = context.Context[cxt_num] or dbgp.error(302, "No such context: "..tostring(cxt_num))
  local level = tonumber(args.d or 0)
  local coro = get_coroutine(self, args.o)
  local cxt = self.stack(coro, level)

  -- evaluate the new value in the local context
  local value = select(2, dbgp.assert(206, pcall(dbgp.assert(206, util.loadin("return "..data, cxt)))))

  local chunk = dbgp.assert(206, util.loadin(name .. " = value", setmetatable({ value = value }, property_evaluation_environment)))
  dbgp.assert(206, pcall(chunk, cxt[cxt_id]))
  dbgp.send_xml(self.skt, { tag = "response", attr = { success = 1, transaction_id = args.i } } )
end

--TODO dynamic code handling
-- The DBGp specification is not clear about the line number meaning, this implementation is 1-based and numbers are inclusive
function M.source(self, args)
  local path
  if args.f then
    path = platform.get_path(args.f)
  else
    path = self.coro:getinfo(0, "S").source
    assert(path:sub(1,1) == "@")
    path = path:sub(2)
  end
  local file, err = io.open(path)
  if not file then 
    dbgp.error(100, err, { success = 0 }) 
  end
  -- Commented out by FlightControl. Not working in DCS.
  -- Try to identify compiled files
  --if file:read(1) == "\033" then dbgp.error(100, args.f.." is bytecode", { success = 0 }) end
  --file:seek("set", 0)


  local srclines = { }
  local beginline, endline, currentline = tonumber(args.b or 0), tonumber(args.e or math.huge), 0
  for line in file:lines() do
    currentline = currentline + 1
    if currentline >= beginline and currentline <= endline then
      srclines[#srclines + 1] = line
    elseif currentline >= endline then break end
  end
  file:close()
  srclines[#srclines + 1] = "" -- to add a trailing \n

  dbgp.send_xml(self.skt, { tag = "response",
    attr = { command = "source", transaction_id = args.i, success = 1},
    util.b64(table.concat(srclines, "\n")) })
end

-- Factory for both stdout and stderr commands, change file descriptor in io
local function output_command_handler_factory(mode)
  return function(self, args)
    if args.c == "0" then -- disable
      io[mode] = io.base[mode]
    else
      io[mode] = setmetatable({ skt = self.skt, mode = mode }, args.c == "1" and core.copy_output or core.redirect_output)
    end
    dbgp.send_xml(self.skt, { tag = "response", attr = { command = mode, transaction_id = args.i, success = "1" } } )
  end
end

M.stdout = output_command_handler_factory("stdout")
M.stderr = output_command_handler_factory("stderr")


return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.commands
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.context
package.preload["debugger.context"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
--  Context handling: allows to evaluate code snippets in the context of a function
-------------------------------------------------------------------------------

local M = { }

local dbgp = require "debugger.dbgp"
local util = require "debugger.util"

-- make unique object to access contexts
local LOCAL, UPVAL, GLOBAL, EVAL, STORE, HANDLE = {}, {}, {}, {}, {}, {}

local getglobals
if DBGP_CLIENT_LUA_VERSION == "Lua 5.1" then
  getglobals = function(f) return getfenv(f) end
elseif DBGP_CLIENT_LUA_VERSION == "Lua 5.2" then
  getglobals = function(f, cxt)
    -- 'global' environment: this is either the local _ENV or upvalue _ENV. A special case happen when a
    -- function does not reference any global variable: the upvalue _ENV may not exist at all. In this case,
    -- global environment is not relevant so it is fixed to an empty table. Another solution would be to set it
    -- to the environment from above stack level but it would require some overhead (especially if multiple
    -- levels must be instantiated)
    if     cxt[LOCAL][STORE]["_ENV"] then return cxt[LOCAL]["_ENV"]
    elseif cxt[UPVAL][STORE]["_ENV"] then return cxt[UPVAL]["_ENV"]
    else return { } end
  end
end

--- Captures variables for given stack level. The capture contains local, upvalues and global variables.
-- The capture can be seen as a proxy table to the stack level: any value can be queried or set no matter
-- it is a local or an upvalue.
-- The individual local and upvalues context are also available and can be queried and modified with indexed notation too.
-- These objects are NOT persistant and must not be used outside the debugger loop which instanciated them !
M.Context = {
  -- Context identifiers can be accessed by their DBGp context ID
  [0] = LOCAL,
  [1] = GLOBAL, -- DLTK internal ID for globals is 1
  [2] = UPVAL,
  -- EVAL is used to keep results from eval in cache in order to browse or modify them, results are stored as sequence
  [-1] = EVAL,
  STORE = STORE,

  -- gets a variable by name with correct handling of Lua scope chain
  -- the or chain does not work here beacause __index metamethod would raise an error instead of returning nil
  __index = function(self, k)
    if     self[LOCAL][STORE][k] then return self[LOCAL][k]
    elseif self[UPVAL][STORE][k] then return self[UPVAL][k]
    else return self[GLOBAL][k] end
  end,
  __newindex = function(self, k, v)
    if     self[LOCAL][STORE][k] then self[LOCAL][k] = v
    elseif self[UPVAL][STORE][k] then self[UPVAL][k] = v
    else self[GLOBAL][k] = v end
  end,

  -- debug only !!
  __tostring = function(self)
    local buf = { "Locals: \n" }
    for k,v in pairs(self[LOCAL][STORE]) do
      buf[#buf+1] = "\t"..tostring(k).."("..tostring(v)..")="..tostring(self[LOCAL][k]).."\n"
    end
    buf[#buf+1] = "Upvalues: \n"
    for k,v in pairs(self[UPVAL][STORE]) do
      buf[#buf+1] = "\t"..tostring(k).."("..tostring(v)..")="..tostring(self[UPVAL][k]).."\n"
    end
    return table.concat(buf)
  end,

  LocalContext = {
    __index = function(self, k)
      local index = self[STORE][k]
      if not index then error("The local "..tostring(k).." does not exists.") end
      local handle = self[HANDLE]
      return select(2, handle.coro:getlocal(handle.level, index))
    end,
    __newindex = function(self, k, v)
      local index = self[STORE][k]
      if index then
        local handle = self[HANDLE]
        handle.coro:setlocal(handle.level, index, v)
      else error("Cannot set local " .. k) end
    end,
    -- Lua 5.2 ready :)
    --__pairs = function(self) return getmetatable(self).iterator, self, nil end,
    iterator = function(self, prev)
      local key, index = next(self[STORE], prev)
      if key then return key, self[key] else return nil end
    end,
  },

  UpvalContext = {
    __index = function(self, k)
      local index = self[STORE][k]
      if not index then error("The local "..tostring(k).." does not exitsts.") end
      return select(2, debug.getupvalue(self[HANDLE], index))
    end,
    __newindex = function(self, k, v)
      local index = self[STORE][k]
      if index then debug.setupvalue(self[HANDLE], index, v)
      else error("Cannot set upvalue " .. k) end
    end,
    -- Lua 5.2 ready :)
    -- __pairs = function(self) return getmetatable(self).iterator, self, nil end,
    iterator = function(self, prev)
      local key, index = next(self[STORE], prev)
      if key then return key, self[key] else return nil end
    end,
  },

  --- Context constructor
  -- @param coro  (util.*Thread instance) coroutine to map to
  -- @param level (number) stack level do dump (script stack level)
  new = function(cls, coro, level)
    local locals, upvalues = {}, {}
    if level < 0 then dbgp.error(301, "No such stack level: "..tostring(level)) end
    local func = (coro:getinfo(level, "f") or dbgp.error(301, "No such stack level: "..tostring(level))).func

    -- local variables
    for i=1, math.huge do
      local name, val = coro:getlocal(level, i)
      if not name then break
      elseif name:sub(1,1) ~= "(" then -- skip internal values
        locals[name] = i
      end
    end

    -- upvalues
    for i=1, math.huge do
      local name, val = debug.getupvalue(func, i)
      if not name then break end
      upvalues[name] = i
    end

    locals = setmetatable({ [STORE] = locals, [HANDLE] = { level = level, coro = coro } }, cls.LocalContext)
    upvalues = setmetatable({ [STORE] = upvalues, [HANDLE] = func }, cls.UpvalContext)

    local result = setmetatable({ [LOCAL] = locals, [UPVAL] = upvalues, [EVAL] = {} }, cls)
    rawset(result, GLOBAL, getglobals(func, result))
    return result
  end,
}

--- Handle caching of all instantiated context.
-- Returns a function which takes 2 parameters: thread and stack level and returns the corresponding context. If this
-- context has been already queried there is no new instantiation. A ContextManager is valid only during the debug loop
-- on which it has been instantiated. References to a ContextManager must be lost after the end of debug loop (so
-- threads can be collected).
-- If a context cannot be instantiated, an 301 DBGP error is thrown.
function M.ContextManager()
  local cache = { }
  return function(thread, level)
    -- the real coroutine is used as key (not the wrapped instance as its unicity is not guaranteed)
    -- otherwise, true is used to identify current thread (as nil is not a valid table key)
    local key = thread[1] or true
    local thread_contexts = cache[key]
    if not thread_contexts then
      thread_contexts = { }
      cache[key] = thread_contexts
    end

    local context = thread_contexts[level]
    if not context then
      context = M.Context:new(thread, level)
      thread_contexts[level] = context
    end

    return context
  end
end

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.context
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.dbgp
package.preload["debugger.dbgp"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- DBGp protocol utility function (parsing, error handling, XML generation).
-------------------------------------------------------------------------------

local util = require "debugger.util"

local error, setmetatable, type, pairs, ipairs, tostring, tconcat =
  error, setmetatable, type, pairs, ipairs, tostring, table.concat

local M = { }

--- Parses the DBGp command arguments and returns it as a Lua table with key/value pairs.
-- For example, the sequence <code>-i 5 -j foo</code> will result in <code>{i=5, j=foo}</code>
-- @param cmd_args (string) sequence of arguments
-- @return table described above
function M.arg_parse(cmd_args)
  local args = {}
  for arg, val in cmd_args:gmatch("%-(%w) (%S+)") do
    args[arg] = val
  end
  return args
end

--- Parses a command line
-- @return commande name (string)
-- @retrun arguments (table)
-- @return data (string, optional)
function M.cmd_parse(cmd)
  local cmd_name, args, data
  if cmd:find("--", 1, true) then -- there is a data part
    cmd_name, args, data = cmd:match("^(%S+)%s+(.*)%s+%-%-%s*(.*)$")
    data = util.unb64(data)
  else
    cmd_name, args = cmd:match("^(%S+)%s+(.*)$")
  end
  return cmd_name, M.arg_parse(args), data
end

--- Returns the packet read from socket, or nil followed by an error message on errors.
function M.read_packet(skt)
  local size = {}
  while true do
    local byte, err = skt:receive(1)
    if not byte then return nil, err end
    if byte == "\000" then break end
    size[#size+1] = byte
  end
  return tconcat(size)
end

M.DBGP_ERR_METATABLE = {} -- unique object used to identify DBGp errors

--- Throws a correct DBGp error which result in a fine tuned error message to the server.
-- It is intended to be called into a command to make a useful error message, a standard Lua error
-- result in a code 998 error (internal debugger error).
-- @param code numerical error code
-- @param message message string (optional)
-- @param attr extra attributes to add to the response tag (optional)
function M.error(code, message, attr)
  error(setmetatable({ code = code, message = message, attr = attr or {} }, M.DBGP_ERR_METATABLE), 2)
end

--- Like core assert but throws a DBGp error if condition is not met.
-- @param code numerical error code thrown if condition is not met.
-- @param message condition to test
-- @param ... will be used as error message if test fails.
function M.assert(code, success, ...)
  if not success then M.error(code, (...)) end
  return success, ...
end

-- -----------------
-- Outgoing data
-- -----------------
local xmlattr_specialchars = { ['"'] = "&quot;", ["<"] = "&lt;", ["&"] = "&amp;" }
--- Very basic XML generator
-- Generates a XML string from a Lua Object Model (LOM) table.
-- See http://matthewwild.co.uk/projects/luaexpat/lom.html
function M.lom2str(xml)
  local pieces = { } -- string buffer

  local function generate(node)
    pieces[#pieces + 1] = "<"..node.tag
    pieces[#pieces + 1] = " "
    -- attribute ordering is not honored here
    for attr, val in pairs(node.attr or {}) do
      if type(attr) == "string" then
        pieces[#pieces + 1] = attr .. '="' .. tostring(val):gsub('["&<]', xmlattr_specialchars) .. '"'
        pieces[#pieces + 1] = " "
      end
    end
    pieces[#pieces] = nil -- remove the last separator (useless)

    if node[1] then
      pieces[#pieces + 1] = ">"
      for _, child in ipairs(node) do
        if type(child) == "table" then generate(child)
        else pieces[#pieces + 1] = "<![CDATA[" .. tostring(child) .. "]]>" end
      end
      pieces[#pieces + 1] = "</" .. node.tag .. ">"
    else
      pieces[#pieces + 1] = "/>"
    end
  end

  generate(xml)
  return tconcat(pieces)
end

function M.send_xml(skt, resp)
  if not resp.attr then resp.attr = {} end
  resp.attr.xmlns = "urn:debugger_protocol_v1"

  local data = '<?xml version="1.0" encoding="UTF-8" ?>\n'..M.lom2str(resp)
  util.log("DEBUG", "Send " .. data)
  skt:send(tostring(#data).."\000"..data.."\000")
end

--- Return an XML tag describing a debugger error, with an optional message
-- @param code (number) error code (see DBGp specification)
-- @param msg  (string, optional) textual description of error
-- @return table, suitable to be converted into XML
function M.make_error(code, msg)
  local elem = { tag = "error", attr = { code = code } }
  if msg then
    elem[1] = { tostring(msg), tag = "message" }
  end
  return elem
end

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.dbgp
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.introspection
package.preload["debugger.introspection"] = function(...)
-- ----------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Julien Desgats - initial API and implementation
-- ----------------------------------------------------------------------------
-- Properties generation. Generate a LOM table with data from introspection.
-- ----------------------------------------------------------------------------

local debug = require "debug"
local platform = require "debugger.platform"
local util = require "debugger.util"

local tostring, type, assert, next, rawget, getmetatable, setmetatable, getfenv, select,         coyield,         cocreate,         costatus,         coresume,       sformat,      tconcat =
  tostring, type, assert, next, rawget, getmetatable, setmetatable, getfenv, select, coroutine.yield, coroutine.create, coroutine.status, coroutine.resume, string.format, table.concat

local MULTIVAL_MT = { __tostring = function() return "" end }
local probes = { }

-- ---------- --
-- Public API --
-- ---------- --

---
-- Introspection logic. This module implements Lua objects introspection and
-- generates a [DBGP](http://xdebug.org/docs-dbgp.php) compatible
-- [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html) data scructure.
-- @module debugger.introspection
local M = { }

---
-- Represent the actual data to send to the debugger.
-- Full XML specification can be found in [DBGP specification](http://xdebug.org/docs-dbgp.php#properties-variables-and-values).
-- Modifying properties after their generation is possible (as actual data serialization/sending is delayed)
-- but should be used with care. The XML structure uses the [LOM](http://matthewwild.co.uk/projects/luaexpat/lom.html)
-- format, refer to these documents to get more informations about fields.
--
-- In addition to table fields, it has an array part, `[1]` being the string representation (base64 encoded),
-- possibly followed by chlid properties (@{#DBGPProperty} themselves)
--
-- @field #string tag Always "property"
-- @field #table attr XML attributes, see DBGP specification
-- @type DBGPProperty

---
-- Inpectors table, contain all inspector functions.
-- Keys are either type names (`string`, `number`, ...) or metatables
-- that have a custom inspector attached.
-- @field [parent=#debugger.introspection] #table inspectors
M.inspectors = { }

---
-- Generate a DBGP property if needed. If data is in data pagination and recursion depth ranges,
-- and send a property to the debugger, otherwise drop current property.
-- @param #string name Property name (displayed in IDE)
-- @param #string typename Type name (displayed in IDE)
-- @param #string repr Value string representation
-- @param #DBGPProperty parent Parent property
-- @param #string fullname Lua expression used to get value back in further calls
-- @return #table description
-- @function [parent=#debugger.introspection] property
M.property = coyield

---
-- Adds a probe that will be called for every unknown table/userdata.
-- @param #function probe Inspector function to call.
-- @function [parent=#debugger.introspection] add_probe
M.add_probe = function(probe) probes[#probes + 1] = probe end

---
-- Inspects a Lua value by dispatching it to correct inspector. Inspector functions have the same API.
-- @param #string name Property name (will be displayed by IDE)
-- @param value Value to inspect
-- @param #table parent Parent property (LOM table of the )
-- @param #string fullname Expression used to retrieve `value` for further debugger calls
-- @return #DBGPProperty The inspected value as returned by @{debugger.introspection#debugger.introspection.property}.
-- @return #nil If the value has not been inspected
-- @function [parent=#debugger.introspection] inspect
M.inspect = function(name, value, parent, fullname)
  return (M.inspectors[type(value)] or M.inspectors.default)(name, value, parent, fullname)
end

-- ----------------- --
-- Utility functions --
-- ----------------- --

local function default_inspector(name, value, parent, fullname)
  return M.property(name, type(value), tostring(value), parent, fullname)
end

-- Inspects types that can have a metatable (table and userdata). Returns
--   1) generated property
--   2) boolean indicating whether a custom inspector has been called (in that case, do not process value any further)
local function metatable_inspector(name, value, parent, fullname)
  local mt = getmetatable(value)
  do
    -- find  by metatable
    local custom = M.inspectors[mt]
    if custom then return custom(name, value, parent, fullname), true end
    -- or else call probes
    for i=1, #probes do
      local prop = probes[i](name, value, parent, fullname)
      if prop then return prop, true end
    end
  end

  local prop = default_inspector(name, value, parent, fullname)
  if mt and prop then
    local mtprop = M.inspect("metatable", mt, prop, "metatable["..prop.attr.fullname.."]")
    if mtprop then mtprop.attr.type = "special" end
  end
  return prop, false
end

local function fancy_func_repr(f, info)
  local args = {}
  for i=1, info.nparams do
    args[i] = debug.getlocal(f, i)
  end

  if info.isvararg then
    args[#args+1] = "..."
  end

  return "function(" .. tconcat(args, ", ") .. ")"
end

--- Generate a name siutable for table index syntax
-- @param name Key name
-- @return #string A table index style index
-- @usage generate_printable_key('foo') => '["foo"]'
-- @usage generate_printable_key(12)    => '[12]'
-- @usage generate_printable_key({})    => '[table: 0x12345678]
-- @function [parent=#debugger.introspection] generate_printable_key
local function generate_printable_key(name)
  return "[" .. (type(name) == "string" and sformat("%q", name) or tostring(name)) .. "]"
end
M.generate_printable_key = generate_printable_key

-- Used to store complex keys (other than string and number) as they cannot be passed in text
-- For these keys, the resulting expression will not be the key itself but "key_cache[...]"
-- where key_cache must be mapped to this table to resolve key correctly.
M.key_cache = setmetatable({ n=0 }, { __mode = "v" })

local function generate_key(name)
  local tname = type(name)
  if tname == "string" then return sformat("%q", name)
  elseif tname == "number" or tname == "boolean" then return tostring(name)
  else -- complex key, use key_cache for lookup
    local i = M.key_cache.n
    M.key_cache[i] = name
    M.key_cache.n = i+1
    return "key_cache["..tostring(i).."]"
  end
end

--- Generate a usable fullname for a value.
-- Based on parent fullname and key value, return a valid Lua expression.
-- Key can be any value (as anything can act as table key). If it cannot
-- be serialized (only string, number and boolean can), it will be temporarly
-- stored in an internal cache to be retrieved later.
-- @param #string parent Parent fullname
-- @param key The child key to generate fullname for
-- @return #string A valid fullname expression
-- @function [parent=#debugger.introspection] make_fullname
local function make_fullname(parent, key)
  return parent .. "[" .. generate_key(key) .. "]"
end
M.make_fullname = make_fullname

-- ---------- --
-- Inspectors --
-- ---------- --

M.inspectors.number   = default_inspector
M.inspectors.boolean  = default_inspector
M.inspectors["nil"]   = default_inspector
M.inspectors.userdata = default_inspector
M.inspectors.thread   = default_inspector
M.inspectors.default  = default_inspector -- allows 3rd party inspectors to use the default inspector if needed

M.inspectors.userdata = function(name, value, parent, fullname)
  return (metatable_inspector(name, value, parent, fullname)) -- drop second return value
end

M.inspectors.string = function(name, value, parent, fullname)
  -- escape linebreaks as \n and not as \<0x0A> like %q does
  return M.property(name, "string", sformat("%q", value):gsub("\\\n", "\\n"), parent, fullname)
end

M.inspectors["function"] = function(name, value, parent, fullname)
  local info = debug.getinfo(value, "nSflu")
  local prop
  if info.what ~= "C" then
    -- try to create a fancy representation if possible
    local repr = info.nparams and fancy_func_repr(value, info) or tostring(value)
    if info.source:sub(1,1) == "@" then
      repr = repr .. "\n" .. platform.get_uri("@" .. info.source) .. "\n" .. tostring(info.linedefined)
    end
    prop = M.property(name, "function (Lua)", repr, parent, fullname)
  else
    prop = M.property(name, "function", tostring(value), parent, fullname)
  end
  if not prop then return nil end

  -- (5.1 only) environment is dumped only if it is different from global environment
  -- TODO: this is not a correct behavior: environment should be dumped if is different from current stack level one
  local fenv = getfenv and getfenv(value)
  if fenv and fenv ~= getfenv(0) then
    local fenvprop = M.inspect("environment", fenv, prop, "environment["..prop.attr.fullname.."]")
    if fenvprop then fenvprop.attr.type = "special" end
  end

  return prop
end


M.inspectors.table = function(name, value, parent, fullname)
  local prop, iscustom = metatable_inspector(name, value, parent, fullname)
  if not prop or iscustom then return prop end

  -- iterate over table values and detect arrays at the same time
  -- next is used to circumvent __pairs metamethod in 5.2
  local isarray, i = true, 1
  for k,v in next, value, nil do
    M.inspect(generate_printable_key(k), v, prop, make_fullname(fullname, k))
    -- array detection: keys should be accessible by 1..n keys
    isarray = isarray and rawget(value, i) ~= nil
    i = i + 1
  end
  -- empty tables are considered as tables
  if isarray and i > 1 then prop.attr.type = "sequence" end

  return prop
end

M.inspectors[MULTIVAL_MT] = function(name, value, parent, fullname)
  if value.n == 1 then
    -- return directly the value as result
    return M.inspect(name, value[1], parent, fullname)
  else
    -- wrap values inside a multival container
    local prop = M.property(name, "multival", "", parent, fullname)
    if not prop then return nil end
    for i=1, value.n do
      M.inspect(generate_printable_key(i), value[i], prop, fullname .. "[" .. i .. "]")
    end
    return prop
  end
end

-- ------------ --
-- Internal API --
-- ------------ --

-- Used to inspect "multival" or "vararg" values. The typical use is to pack function result(s) in a single
-- value to inspect. The Multival instances can be passed to make_property as a single value, they will be
-- correctly reported to debugger
function M.Multival(...)
  return setmetatable({ n=select("#", ...), ... }, MULTIVAL_MT)
end

--- Makes a property form a name/value pair (and fullname). This is an **internal** function, and should not be used by 3rd party inspectors.
-- @param #number cxt_id Context ID in which this value resides (workaround bug 352316)
-- @param value The value to debug
-- @param name The name associated with value, passed through tostring, so it can be anything
-- @param #string fullname A Lua expression to eval to get that property again (if nil, computed automatically)
-- @param #number depth The maximum property depth (recursive calls)
-- @param #number pagesize maximum children to include
-- @param #number page The page to generate (0 based)
-- @param #number size_limit Optional, if set, the maximum size of the string representation (in bytes)
-- @param #boolean safe_name If true, does not encode the name as table key
-- @return #DBGPProperty root property
-- @function [parent=#debugger.introspection] make_property
--TODO BUG ECLIPSE TOOLSLINUX-99 352316 : as a workaround, context is encoded into the fullname property
M.make_property = function(cxt_id, value, name, fullname, depth, pagesize, page, size_limit, safe_name)
  fullname = fullname or "(...)[" .. generate_key(name) .. "]"
  if not safe_name then name = generate_printable_key(name) end

  local generator = cocreate(function() return M.inspect(name, value, nil, fullname) end)
  local propstack = { }
  local rootnode
  local catchthis = true
  local nodestoskip = page * pagesize -- nodes to skip at root level to respect pagination
  local fullname_prefix = tostring(cxt_id).."|"

  while true do
    local succes, name, datatype, repr, parent, fullname = assert(coresume(generator, catchthis and propstack[#propstack] or nil))
    -- finalize and pop all finished properties
    while propstack[#propstack] ~= parent do
      local topop = propstack[#propstack]
      topop.attr.fullname = util.rawb64(fullname_prefix .. topop.attr.fullname)
      propstack[#propstack] = nil
    end
    if costatus(generator) == "dead" then break end

    local prop = {
      tag = "property",
      attr = {
        children = 0,
        pagesize = pagesize,
        page = parent and 0 or page,
        type = datatype,
        name = name,
        fullname = fullname,
        encoding = "base64",
        size = #repr,
      },
      util.b64(size_limit and repr:sub(1, size_limit) or repr)
    }

    if parent then
      parent.attr.children = 1
      parent.attr.numchildren = (parent.attr.numchildren or 0) + 1
      -- take pagination into accont to know if node needs to be catched
      catchthis = #parent <= pagesize and #propstack <= depth
      if parent == rootnode then
        catchthis = catchthis and nodestoskip <= 0
        nodestoskip = nodestoskip - 1
      end
      -- add node to tree
      if catchthis then
        parent[#parent + 1] = prop
        propstack[#propstack + 1] = prop
      end
    else
      rootnode = prop
      catchthis = true
      propstack[#propstack + 1] = prop
    end
  end

  return rootnode
end

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.introspection
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.plugins.ffi
package.preload["debugger.plugins.ffi"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2012-2013 Julien Desgats
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Julien Desgats - initial API and implementation
-------------------------------------------------------------------------------
-- LuaJIT cdata introspection library.
-------------------------------------------------------------------------------

-- known issues:
--  * references are de-referenced event if inspect_references is unset
--  * is automatic pointer and reference de-referencing is possible ?
--    (only for first item in case of arrays). Possible leads:
--    http://stackoverflow.com/questions/7134590/how-to-test-if-an-address-is-readable-in-linux-userspace-app
--    http://www.softwareverify.com/blog/?p=319
--  * when setting a value from Eclipse, the type is sometimes changed (e.g. int => number)

local introspection = require "debugger.introspection"
local reflect = require "debugger.plugins.ffi.reflect"
local ffi = require "ffi"

local tostring, tonumber, type, assert, sformat, tconcat = tostring, tonumber, type, assert, string.format, table.concat

local M = { }

--- Whether the reference types are inspected. Usually references should be safe (at least a bit
-- safer than pointers) so they are inspected. If a reference points to unsafe memory, the whole
-- program could crash !
-- If this feature is disabled, deeply nested C types will not be displayed correctly as evaluation
-- has a recursion limit, any further evaluation is done through references.
M.inspect_references = true

local function make_typename(refct)
  local t = refct.what
  if t == "int" then
    if refct.bool then t = "bool"
    else
      -- use C99 type notation to give more details about acutal type
      t = (refct.unsigned and "uint" or "int") .. tostring(refct.size * 8) .. "_t"
    end
  elseif t == "float" then
    -- assume IEEE754
    if     refct.size ==  8 then t = "double"
    elseif refct.size == 16 then t = "long double" -- not really sure this one is always true
    end
  elseif t == "struct" or t == "enum" or t == "union" then
    t = refct.name and (t .. " " .. refct.name) or ("anonymous "..t)
  elseif t == "func" then
    t = "function (FFI)"
  elseif t == "ptr" then
    t = make_typename(refct.element_type) .. "*"
  elseif t == "ref" then
    t = make_typename(refct.element_type) .. "&"
  elseif t == "field" then
    return make_typename(refct.type)
  elseif t == "bitfield" then
    t = (refct.type.unsigned and "unsigned" or "signed") .. ":" .. tostring(refct.size * 8)
    refct = refct.type
  end

  if refct.const then t = "const " .. t end
  if refct.volatile then t = "volatile " .. t end
  return t
end

-- if cdatakind is unknown, this one will be called
local default_inspector = introspection.inspectors.number
local inspect

-- recursion must be handled with some care: if we call regular introspection.inspect
-- we may create boxed references or Lua native objects which will be inspected as such
-- (leading to wrong type names).
local function recurse(name, value, parent, fullname, refct)
  if type(value) == "cdata" then
    return inspect(name, value, parent, fullname, refct)
  else
    local prop = introspection.inspect(name, value, parent, fullname)
    if prop then
      prop.attr.type = make_typename(refct)
    end
    return prop
  end
end

-- cdata specific inspectors
local inspectors = {
  struct = function(name, value, parent, fullname, refct)
    local prop = introspection.property(name, make_typename(refct), tostring(value), parent, fullname)

    -- inspect children, if needed
    if prop then
      for member in refct:members() do
        local mname = member.name
        recurse(mname, value[mname], prop, fullname .. sformat('[%q]', mname), member)
      end
    end
    return prop
  end,

  array = function(name, value, parent, fullname, refct)
    local etype = refct.element_type
    -- for VLAs, reflect does not give size
    local size = refct.size ~= "none" and refct.size or ffi.sizeof(value)
    size = size and (size / etype.size) -- we've got the byte size, not element count

    local typename = make_typename(etype)
    local prop = introspection.property(name, typename .. "[" .. (tostring(size) or "") .. "]", tostring(value), parent, fullname)

    if prop and size then
      for i=0, size-1 do
        local idx = "["..tostring(i).."]"
        recurse(idx, value[i], prop, fullname .. idx, etype)
      end
    end
    return prop
  end,

  func = function(name, value, parent, fullname, refct)
    local args = { }
    for arg in refct:arguments() do
      args[#args + 1] = make_typename(arg.type) .. " " .. arg.name
    end

    if refct.vararg then
      args[#args + 1] = "..."
    end

    local repr = make_typename(refct.return_type) .. " " .. refct.name .. "(" .. tconcat(args, ", ") .. ")"
    return introspection.property(name, make_typename(refct), repr, parent, fullname)
  end,

  enum = function(name, value, parent, fullname, refct)
    local repr = tonumber(value)
    -- try to convert numeric value into enum name
    --TODO: is there a faster method to make it ?
    for val in refct:values() do
      if val.value == repr then
        repr = val.name
        break
      end
    end

    return introspection.property(name, make_typename(refct), tostring(repr), parent, fullname)
  end,

  ref = function(name, value, parent, fullname, refct)
    -- this may be unsafe, see inspect_references setting
    local typename = make_typename(refct)
    if not M.inspect_references then
      return introspection.property(name, typename, tostring(value), parent, fullname)
    end

    local prop = recurse(name, value, parent, fullname, refct.element_type)
    if prop then
      prop.attr.type = typename
    end
    return prop
  end,

  int = function(name, value, parent, fullname, refct)
    return introspection.property(name, make_typename(refct), tostring(tonumber(value)), parent, fullname)
  end,

  -- pointers are too unsafe, do not inspect them
  ptr = function(name, value, parent, fullname, refct)
    return introspection.property(name, make_typename(refct), tostring(value), parent, fullname)
  end,
}

inspectors.union = inspectors.struct
inspectors.float = inspectors.int

-- for struct/union fields, the actual type is nested into the refct
inspectors.field = function(name, value, parent, fullname, refct)
  return inspect(name, value, parent, fullname, refct.type)
end
inspectors.bitfield = inspectors.field

inspect = function(name, value, parent, fullname, refct)
  -- inspect only values, not ctypes
  --FIXME: this cause references to be dereferenced and crash the process if they are wrong !
  if ffi.typeof(value) ~= value then
    refct = refct or reflect.typeof(value)
    return (inspectors[refct.what] or default_inspector)(name, value, parent, fullname, refct)
  end

  -- return a simple property for ctypes
  return introspection.property(name, "ctype", tostring(value), parent, fullname)
end

introspection.inspectors.cdata = inspect

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.plugins.ffi
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.plugins.ffi.reflect
package.preload["debugger.plugins.ffi.reflect"] = function(...)
--[[ LuaJIT FFI reflection Library ]]--
--[[ Copyright (C) 2013 Peter Cawley <lua@corsix.org>. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--]]
local ffi = require "ffi"
local bit = require "bit"
local reflect = {}

-- Relevant minimal definitions from lj_ctype.h
ffi.cdef [[
  typedef struct CType {
    uint32_t info;
    uint32_t size;
    uint16_t sib;
    uint16_t next;
    uint32_t name;
  } CType;
  
  typedef struct CTState {
    CType *tab;
    uint32_t top;
    uint32_t sizetab;
    void *L;
    void *g;
    void *finalizer;
    void *miscmap;
  } CTState;
]]

local function gc_str(gcref) -- Convert a GCref (to a GCstr) into a string
  if gcref ~= 0 then
    local ts = ffi.cast("uint32_t*", gcref)
    return ffi.string(ts + 4, ts[3])
end
end

local function memptr(gcobj)
  return tonumber(tostring(gcobj):match"%x*$", 16)
end

-- Acquire a pointer to this Lua universe's CTState
local CTState do
  local co = coroutine.create(function()end) -- Any live coroutine will do.
  local uint32_ptr = ffi.typeof("uint32_t*")
  local G = ffi.cast(uint32_ptr, ffi.cast(uint32_ptr, memptr(co))[2])
  -- In global_State, `MRef ctype_state` is immediately before `GCRef gcroot[GCROOT_MAX]`.
  -- We first find (an entry in) gcroot by looking for a metamethod name string.
  local anchor = ffi.cast("uint32_t", ffi.cast("const char*", "__index"))
  local i = 0
  while math.abs(tonumber(G[i] - anchor)) > 64 do
    i = i + 1
  end
  -- We then work backwards looking for something resembling ctype_state.
  repeat
    i = i - 1
    CTState = ffi.cast("CTState*", G[i])
  until ffi.cast(uint32_ptr, CTState.g) == G
end

-- Acquire the CTState's miscmap table as a Lua variable
local miscmap do
  local t = {}; t[0] = t
  local tvalue = ffi.cast("uint32_t*", memptr(t))[2]
  ffi.cast("uint32_t*", tvalue)[ffi.abi"le" and 0 or 1] = ffi.cast("uint32_t", ffi.cast("uintptr_t", CTState.miscmap))
  miscmap = t[0]
end

-- Information for unpacking a `struct CType`.
-- One table per CT_* constant, containing:
-- * A name for that CT_
-- * Roles of the cid and size fields.
-- * Whether the sib field is meaningful.
-- * Zero or more applicable boolean flags.
local CTs = {[0] =
  {"int",
    "", "size", false,
    {0x08000000, "bool"},
    {0x04000000, "float", "subwhat"},
    {0x02000000, "const"},
    {0x01000000, "volatile"},
    {0x00800000, "unsigned"},
    {0x00400000, "long"},
  },
  {"struct",
    "", "size", true,
    {0x02000000, "const"},
    {0x01000000, "volatile"},
    {0x00800000, "union", "subwhat"},
    {0x00100000, "vla"},
  },
  {"ptr",
    "element_type", "size", false,
    {0x02000000, "const"},
    {0x01000000, "volatile"},
    {0x00800000, "ref", "subwhat"},
  },
  {"array",
    "element_type", "size", false,
    {0x08000000, "vector"},
    {0x04000000, "complex"},
    {0x02000000, "const"},
    {0x01000000, "volatile"},
    {0x00100000, "vla"},
  },
  {"void",
    "", "size", false,
    {0x02000000, "const"},
    {0x01000000, "volatile"},
  },
  {"enum",
    "type", "size", true,
  },
  {"func",
    "return_type", "nargs", true,
    {0x00800000, "vararg"},
    {0x00400000, "sse_reg_params"},
  },
  {"typedef", -- Not seen
    "element_type", "", false,
  },
  {"attrib", -- Only seen internally
    "type", "value", true,
  },
  {"field",
    "type", "offset", true,
  },
  {"bitfield",
    "", "offset", true,
    {0x08000000, "bool"},
    {0x02000000, "const"},
    {0x01000000, "volatile"},
    {0x00800000, "unsigned"},
  },
  {"constant",
    "type", "value", true,
    {0x02000000, "const"},
  },
  {"extern", -- Not seen
    "CID", "", true,
  },
  {"kw", -- Not seen
    "TOK", "size",
  },
}

-- Set of CType::cid roles which are a CTypeID.
local type_keys = {
  element_type = true,
  return_type = true,
  value_type = true,
  type = true,
}

-- Create a metatable for each CT.
local metatables = {
  }
for _, CT in ipairs(CTs) do
  local what = CT[1]
  local mt = {__index = {}}
  metatables[what] = mt
end

-- Logic for merging an attribute CType onto the annotated CType.
local CTAs = {[0] =
  function(a, refct) error("TODO: CTA_NONE") end,
  function(a, refct) error("TODO: CTA_QUAL") end,
  function(a, refct)
    a = 2^a.value
    refct.alignment = a
    refct.attributes.align = a
  end,
  function(a, refct)
    refct.transparent = true
    refct.attributes.subtype = refct.typeid
  end,
  function(a, refct) refct.sym_name = a.name end,
  function(a, refct) error("TODO: CTA_BAD") end,
}

-- C function calling conventions (CTCC_* constants in lj_refct.h)
local CTCCs = {[0] =
  "cdecl",
  "thiscall",
  "fastcall",
  "stdcall",
}

local function refct_from_id(id) -- refct = refct_from_id(CTypeID)
  local ctype = CTState.tab[id]
  local CT_code = bit.rshift(ctype.info, 28)
  local CT = CTs[CT_code]
  local what = CT[1]
  local refct = setmetatable({
    what = what,
    typeid = id,
    name = gc_str(ctype.name),
  }, metatables[what])

  -- Interpret (most of) the CType::info field
  for i = 5, #CT do
    if bit.band(ctype.info, CT[i][1]) ~= 0 then
      if CT[i][3] == "subwhat" then
        refct.what = CT[i][2]
      else
        refct[CT[i][2]] = true
      end
    end
  end
  if CT_code <= 5 then
    refct.alignment = bit.lshift(1, bit.band(bit.rshift(ctype.info, 16), 15))
  elseif what == "func" then
    refct.convention = CTCCs[bit.band(bit.rshift(ctype.info, 16), 3)]
  end

  if CT[2] ~= "" then -- Interpret the CType::cid field
    local k = CT[2]
    local cid = bit.band(ctype.info, 0xffff)
    if type_keys[k] then
      if cid == 0 then
        cid = nil
      else
        cid = refct_from_id(cid)
      end
    end
    refct[k] = cid
  end

  if CT[3] ~= "" then -- Interpret the CType::size field
    local k = CT[3]
    refct[k] = ctype.size
    if k == "size" and bit.bnot(refct[k]) == 0 then
      refct[k] = "none"
    end
  end

  if what == "attrib" then
    -- Merge leading attributes onto the type being decorated.
    local CTA = CTAs[bit.band(bit.rshift(ctype.info, 16), 0xff)]
    if refct.type then
      local ct = refct.type
      ct.attributes = {}
      CTA(refct, ct)
      ct.typeid = refct.typeid
      refct = ct
    else
      refct.CTA = CTA
    end
  elseif what == "bitfield" then
    -- Decode extra bitfield fields, and make it look like a normal field.
    refct.offset = refct.offset + bit.band(ctype.info, 127) / 8
    refct.size = bit.band(bit.rshift(ctype.info, 8), 127) / 8
    refct.type = {
      what = "int",
      bool = refct.bool,
      const = refct.const,
      volatile = refct.volatile,
      unsigned = refct.unsigned,
      size = bit.band(bit.rshift(ctype.info, 16), 127),
    }
    refct.bool, refct.const, refct.volatile, refct.unsigned = nil
  end

  if CT[4] then -- Merge sibling attributes onto this type.
    while ctype.sib ~= 0 do
      local entry = CTState.tab[ctype.sib]
      if CTs[bit.rshift(entry.info, 28)][1] ~= "attrib" then break end
      if bit.band(entry.info, 0xffff) ~= 0 then break end
      local sib = refct_from_id(ctype.sib)
      sib:CTA(refct)
      ctype = entry
  end
  end

  return refct
end

local function sib_iter(s, refct)
  repeat
    local ctype = CTState.tab[refct.typeid]
    if ctype.sib == 0 then return end
    refct = refct_from_id(ctype.sib)
  until refct.what ~= "attrib" -- Pure attribs are skipped.
  return refct
end

local function siblings(refct)
  -- Follow to the end of the attrib chain, if any.
  while refct.attributes do
    refct = refct_from_id(refct.attributes.subtype or CTState.tab[refct.typeid].sib)
  end

  return sib_iter, nil, refct
end

metatables.struct.__index.members = siblings
metatables.func.__index.arguments = siblings
metatables.enum.__index.values = siblings

local function find_sibling(refct, name)
  local num = tonumber(name)
  if num then
    for sib in siblings(refct) do
      if num == 1 then
        return sib
      end
      num = num - 1
    end
  else
    for sib in siblings(refct) do
      if sib.name == name then
        return sib
      end
    end
  end
end

metatables.struct.__index.member = find_sibling
metatables.func.__index.argument = find_sibling
metatables.enum.__index.value = find_sibling

function reflect.typeof(x) -- refct = reflect.typeof(ct)
  return refct_from_id(tonumber(ffi.typeof(x)))
end

function reflect.getmetatable(x) -- mt = reflect.getmetatable(ct)
  return miscmap[-tonumber(ffi.typeof(x))]
end

return reflect

end
--------------------------------------------------------------------------------
-- End of moduledebugger.plugins.ffi.reflect
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.platform
package.preload["debugger.platform"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Platform/OS specific features and path handling.
-------------------------------------------------------------------------------

local url = require "debugger.url"
local util = require "debugger.util"

local M = { }

-- Execution plaform (could be win or unix)
-- Used to manage file path difference between the 2 platform
local platform = nil

-- keep all computed URIs in cache (as they are quite long to compute)
local uri_cache = { }

-- parse a normalized path and return a table of each segment
-- you could precise the path seperator.
local function split(path,sep)
  local t = {}
  for w in path:gmatch("[^"..(sep or "/").."]+")do
    table.insert(t, w)
  end
  return t
end

--- Returns a RFC2396 compliant URI for given source, or false if the mapping failed
local function get_abs_file_uri (source)
  local uri
  if source:sub(1,1) == "@" then -- real source file
    local sourcepath = source:sub(2)
    local normalizedpath = M.normalize(sourcepath)
    if not M.is_path_absolute(normalizedpath) then
      normalizedpath = M.normalize(M.base_dir .. "/" .. normalizedpath)
    end
    return M.to_file_uri(normalizedpath)
  else -- dynamic code, stripped bytecode, tail return, ...
    return false
  end
end

--FIXME: as result is cached, changes in package.path that modify the module name are missed
-- (mostly affect main module when Lua interpreter is launched with an absolute path)
local function get_module_uri (source)
  if source:sub(1,1) == "@" then -- real source file
    local uri
    local sourcepath = source:sub(2)
    local normalizedpath = M.normalize(sourcepath)
    local luapathtable = split (package.path, ";")
    local is_source_absolute = M.is_path_absolute(sourcepath)
    -- workarround : Add always the ?.lua entry to support
    -- the case where file was loaded by : "lua myfile.lua"
    table.insert(luapathtable,"?.lua")
    for i,var in ipairs(luapathtable) do
      -- avoid relative patterns matching absolute ones (e.g. ?.lua matches anything)
      if M.is_path_absolute(var) == is_source_absolute then
        local escaped = string.gsub(M.normalize(var),"[%^%$%(%)%%%.%[%]%*%+%-%?]",function(c) return "%"..c end)
        local pattern = string.gsub(escaped,"%%%?","(.+)")
        local modulename = string.match(normalizedpath,pattern)
        if modulename then
          modulename = string.gsub(modulename,"/",".");
          -- if we find more than 1 possible modulename return the shorter
          if not uri or string.len(uri)>string.len(modulename) then
            uri = modulename
          end
        end
      end
    end
    if uri then return "module:///"..uri end
  end
  return false
end

function M.get_uri (source)
  -- search in cache
  if string.sub(1,1) ~= "@" then
    source = source:lower()
    source = source:match( "(.*\.lua)$")
    if source then
      source = source:match( "scripts/(.*)") or source
      source = "@" .. source
    end
  end
  

  if source then
    local uri = uri_cache[source]
    if uri ~= nil then return uri end
  
    -- not found, create uri
    if util.features.uri == "module" then
      uri = get_module_uri(source)
      if not uri then uri = get_abs_file_uri (source) end
    else
      uri =  get_abs_file_uri (source)
    end
    uri_cache[source] = uri
    return uri
  else
    return nil
  end
end

-- get path file from uri
function M.get_path (uri)
  local parsed_path = assert(url.parse(uri))
  if parsed_path.scheme == "file" then
    return M.to_path(parsed_path)
  else
    -- search in cache
    -- we should surely calculate it instead of find in cache
    for k,v in pairs(uri_cache)do
      if v == uri then
        assert(k:sub(1,1) == "@")
        return k:sub(2)
      end
    end
  end
end

function M.normalize(path)
  local parts = { }
  for w in path:gmatch("[^/]+") do
    if     w == ".." and #parts ~=0 then table.remove(parts)
    elseif w ~= "."  then table.insert(parts, w)
    end
  end
  return (path:sub(1,1) == "/" and "/" or "") .. table.concat(parts, "/")
end

function M.init(executionplatform,workingdirectory)
  --------------------------
  -- define current platform
  --------------------------
  -- check parameter
    
  if executionplatform and executionplatform ~= "unix" and executionplatform ~="win" then
    error("Unable to initialize platform module : execution platform should be 'unix' or 'win'.")
  end

  -- use parameter as current platform
  if executionplatform then
    platform = executionplatform
  else
    --if not define try to guess it.
    local function iswindows()
      local p = io.popen("echo %os%")
      if p then
        local result =p:read("*l")
        p:close()
        return result == "Windows_NT"
      end
      return false
    end

    local status, iswin = pcall(iswindows)
    if status and iswin then
      platform = "win"
    else
      platform = "unix"
    end
  end

  --------------------------
  -- platform dependent function
  --------------------------
  if platform == "unix" then
    -- The Path separator character
    M.path_sep = "/"

    -- TODO the way to get the absolute path can be wrong if the program loads new source files by relative path after a cd.
    -- currently, the directory is registered on start, this allows program to load any source file and then change working dir,
    -- which is the most common use case.
    M.base_dir = workingdirectory or os.getenv("PWD")

    -- convert parsed URL table to file path  for the current OS (see url.parse from luasocket)
    M.to_file_uri = function (path) return url.build{scheme="file",authority="", path=path} end

    -- return true is the path is absolute
    -- the path must be normalized
    M.is_path_absolute = function (path) return path:sub(1,1) == "/" end

    -- convert absolute normalized path file to uri
    M.to_path = function (parsed_url) return url.unescape(parsed_url.path) end
  else
    -- Implementations for Windows, see UNIX versions for documentation.
    M.path_sep = "\\"
    M.is_path_absolute = function (path) return path:match("^%a:/") end
    M.to_file_uri = function (path) return url.build{scheme="file",authority="", path="/"..path} end
    M.to_path = function (parsed_url) return url.unescape(parsed_url.path):gsub("^/", "") end

    local unixnormalize = M.normalize
    M.normalize = function(path) 
    local unixnormalpath = unixnormalize(path:gsub("\\","/"):lower())
    return unixnormalpath
    end

    -- determine base dir
    local function getworkingdirectory()
      local p = io.popen("echo %cd%")
      if p then
        local res = p:read("*l")
        p:close()
        return M.normalize(res)
      end
    end
    M.base_dir = workingdirectory or getworkingdirectory()

  end

  if not M.base_dir then error("Unable to determine the working directory.") end
end

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.platform
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.util
package.preload["debugger.util"] = function(...)
-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------
-- Utility functions.
-------------------------------------------------------------------------------

local M = { }

-- log system
local LEVELS = { ERROR = 0, WARNING = 1, INFO = 2, DETAIL = 3, DEBUG = 4 }
local LOG_LEVEL = LEVELS.INFO

-- Debugger features handling. Any feature can be get like any regular table, setting features result in
-- error for unknown or read-only features.
M.features = setmetatable({ }, {
  -- functions that format/validate data. If function is not provided, the feature cannot be modified.
  validators = {
    multiple_sessions = tonumber,
    encoding = tostring,
    max_children = tonumber,
    max_data = tonumber,
    max_depth = tonumber,
    show_hidden = tonumber,
    uri = tostring,
    log_level = function(level_name)
      -- set numerical index in internal var
      LOG_LEVEL = assert(LEVELS[level_name], "No such level")
      return level_name -- the displayed level is still the name
    end,
  },
  __index = {
    multiple_sessions = 0,
    encoding ="UTF-8",
    max_children = 32,
    max_data = 0xFFFF,
    max_depth = 1,
    show_hidden = 1,
    uri = "file",
    log_level = "WARNING",
    -- read only features
    language_supports_threads = 0,
    language_name = "Lua",
    language_version = _VERSION,
    protocol_version = 1,
    supports_async = 1,
    data_encoding = "base64",
    breakpoint_languages = "Lua",
    breakpoint_types = "line conditional",
  },
  __newindex = function(self, k, v)
    local mt = getmetatable(self)
    local values, validator = mt.__index, mt.validators[k]
    if values[k] == nil then error("No such feature " .. tostring(k)) end
    if not validator then error("The feature " .. tostring(k) .. " is read-only") end
    v = assert(validator(v))
    values[k] = v
  end,
})

-- Wraps debug function and an attached thread
-- also handle stack & coroutine management differencies between Lua versions
local getinfo, getlocal, setlocal = debug.getinfo, debug.getlocal, debug.setlocal

-- Foreign thread is used to debug paused thread
local ForeignThreadMT = {
  getinfo  = function(self, level, what)     return getinfo(self[1], level, what) end,
  getlocal = function(self, level, idx)      return getlocal(self[1], level, idx) end,
  setlocal = function(self, level, idx, val) return setlocal(self[1], level, idx, val) end,
}
ForeignThreadMT.__index = ForeignThreadMT
function M.ForeignThread(coro) return setmetatable({ coro }, ForeignThreadMT) end

-- Current thread is used to debug the thread that caused the hook
-- intended to be used *ONLY* in debug loop (executed in a new thread)
local CurrentThreadMT = {
  getinfo  = function(self, level, what)     return getinfo(self[1], level + 2, what) end,
  getlocal = function(self, level, idx)      return getlocal(self[1], level + 2, idx) end,
  setlocal = function(self, level, idx, val) return setlocal(self[1], level + 2, idx, val) end,
}
CurrentThreadMT.__index = CurrentThreadMT
function M.CurrentThread(coro) return setmetatable({ coro }, CurrentThreadMT) end


-- Some version dependant functions
if DBGP_CLIENT_LUA_VERSION == "Lua 5.1" then
  local loadstring, getfenv, setfenv, debug_getinfo, MainThread =
    loadstring, getfenv, setfenv, debug.getinfo, nil

  -- in 5.1 "t" flag does not exist and trigger an error so remove it from what
  CurrentThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level + 2, what:gsub("t", "", 1)) end
  ForeignThreadMT.getinfo = function(self, level, what) return getinfo(self[1], level, what:gsub("t", "", 1)) end

  -- when we're forced to start debug loop on top of program stack (when on main coroutine)
  -- this requires some hackery to get right stack level

  -- Fallback method to inspect running thread (only for main thread in 5.1 or for conditional breakpoints)
  --- Gets a script stack level with additional debugger logic added
  -- @param l (number) stack level to get for debugged script (0 based)
  -- @return real Lua stack level suitable to be passed through deubg functions
  local function get_script_level(l)
    local hook = debug.gethook()
    for i=2, math.huge do
      if assert(debug.getinfo(i, "f")).func == hook then
        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
      end
    end
  end

  if rawget(_G, "jit") then
    MainThread = {
      [1] = "main", -- as the raw thread object is used as table keys, provide a replacement.
      -- LuaJIT completely eliminates tail calls from stack, so get_script_level retunrs wrong result in this case
      getinfo  = function(self, level, what)     return getinfo(get_script_level(level) - 1, what:gsub("t", "", 1)) end,
      getlocal = function(self, level, idx)      return getlocal(get_script_level(level) - 1, idx) end,
      setlocal = function(self, level, idx, val) return setlocal(get_script_level(level) - 1, idx, val) end,
    }
  else
    MainThread = {
      [1] = "main",
      getinfo  = function(self, level, what)     return getinfo(get_script_level(level) , what:gsub("t", "", 1)) end,
      getlocal = function(self, level, idx)      return getlocal(get_script_level(level), idx) end,
      setlocal = function(self, level, idx, val) return setlocal(get_script_level(level), idx, val) end,
    }
  end



  -- If the VM is vanilla Lua 5.1 or LuaJIT 2 without 5.2 compatibility, there is no way to get a reference to
  -- the main coroutine, so fall back to direct mode: the debugger loop is started on the top of main thread
  -- and the actual level is recomputed each time
  local oldCurrentThread = M.CurrentThread
  M.CurrentThread = function(coro) return coro and oldCurrentThread(coro) or MainThread end

  -- load a piece of code alog with its environment
  function M.loadin(code, env)
    local f,err = loadstring(code)
    if not f then
      return nil, err
    else
      return f and setfenv(f, env)
    end
  end

  -- table that maps [gs]et environment to index
  M.eval_env = setmetatable({ }, {
    __index = function(self, func) return getfenv(func) end,
    __newindex = function(self, func, env) return setfenv(func, env) end,
  })
elseif DBGP_CLIENT_LUA_VERSION == "Lua 5.2" then
  local load, debug_getinfo = load, debug.getinfo
  function M.getinfo(coro, level, what)
    if coro then return debug_getinfo(coro, level, what)
    else return debug_getinfo(level + 1, what) end
  end

  function M.loadin(code, env) return load(code, nil, nil, env) end

  -- no eval_env for 5.2 as functions does not have environments anymore
end

-- ----------------------------------------------------------------------------
-- Bare minimal log system.
-- ----------------------------------------------------------------------------
function M.log(level, msg, ...)
  if (LEVELS[level] or -1) > LOG_LEVEL then return end
  if select("#", ...) > 0 then msg = msg:format(...) end
  env.info(string.format("DEBUGGER\t%s\t%s\n", level, msg))
end

function M.ser(level, ...)
  local msg = ""
  if (LEVELS[level] or -1) > LOG_LEVEL then return end
  if select("#", ...) > 0 then msg = M.serialize(...) end
  env.info(string.format("DEBUGGER\t%s\t%s\n", level, msg))
end

-- porting in Slmod's serialize_slmod2
function M.serialize(tbl)  -- serialization of a table all on a single line, no comments, made to replace old get_table_string function

  lookup_table = {}
  
  local function _Serialize( tbl )

    if type(tbl) == 'table' then --function only works for tables!
    
      if lookup_table[tbl] then
        return lookup_table[object]
      end

      local tbl_str = {}
      
      lookup_table[tbl] = tbl_str
      
      tbl_str[#tbl_str + 1] = '{'

      for ind,val in pairs(tbl) do -- serialize its fields
        local ind_str = {}
        if type(ind) == "number" then
          ind_str[#ind_str + 1] = '['
          ind_str[#ind_str + 1] = tostring(ind)
          ind_str[#ind_str + 1] = ']='
        else --must be a string
          ind_str[#ind_str + 1] = '['
          ind_str[#ind_str + 1] = M.basicserialize(ind)
          ind_str[#ind_str + 1] = ']='
        end

        local val_str = {}
        if ((type(val) == 'number') or (type(val) == 'boolean')) then
          val_str[#val_str + 1] = tostring(val)
          val_str[#val_str + 1] = ','
          tbl_str[#tbl_str + 1] = table.concat(ind_str)
          tbl_str[#tbl_str + 1] = table.concat(val_str)
      elseif type(val) == 'string' then
          val_str[#val_str + 1] = M.basicserialize(val)
          val_str[#val_str + 1] = ','
          tbl_str[#tbl_str + 1] = table.concat(ind_str)
          tbl_str[#tbl_str + 1] = table.concat(val_str)
        elseif type(val) == 'nil' then -- won't ever happen, right?
          val_str[#val_str + 1] = 'nil,'
          tbl_str[#tbl_str + 1] = table.concat(ind_str)
          tbl_str[#tbl_str + 1] = table.concat(val_str)
        elseif type(val) == 'table' then
          if ind == "__index" then
          --  tbl_str[#tbl_str + 1] = "__index"
          --  tbl_str[#tbl_str + 1] = ','   --I think this is right, I just added it
          else

            val_str[#val_str + 1] = _Serialize(val)
            val_str[#val_str + 1] = ','   --I think this is right, I just added it
            tbl_str[#tbl_str + 1] = table.concat(ind_str)
            tbl_str[#tbl_str + 1] = table.concat(val_str)
          end
        elseif type(val) == 'function' then
        --  tbl_str[#tbl_str + 1] = "function " .. tostring(ind)
        --  tbl_str[#tbl_str + 1] = ','   --I think this is right, I just added it
        else
--          env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind))
--          env.info( debug.traceback() )
        end
  
      end
      tbl_str[#tbl_str + 1] = '}'
      return table.concat(tbl_str)
    else
      if type(tbl) == 'string' then
        return tbl
      else
      return tostring(tbl)
      end
    end
  end
  
  local objectreturn = _Serialize(tbl)
  return objectreturn
end

--porting in Slmod's "safestring" basic serialize
function M.basicserialize(s)
  if s == nil then
    return "\"\""
  else
    if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then
      return tostring(s)
    elseif type(s) == 'string' then
      s = string.format('%s', s:gsub( "%%", "%%%%" ) )
      return s
    end
  end
end

return M

end
--------------------------------------------------------------------------------
-- End of moduledebugger.util
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
--  Module debugger.url
package.preload["debugger.url"] = function(...)
-----------------------------------------------------------------------------
-- URI parsing, composition and relative URL resolution
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local base = _G
local table = require("table")

local _ENV = { }
if setfenv then setfenv(1, _ENV) end

-----------------------------------------------------------------------------
-- Module version
-----------------------------------------------------------------------------
_VERSION = "URL 1.0.1"

-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
--   s: binary string to be encoded
-- Returns
--   escaped representation of string binary
-----------------------------------------------------------------------------
function escape(s)
    return string.gsub(s, "([^A-Za-z0-9_])", function(c)
        return string.format("%%%02x", string.byte(c))
    end)
end

-----------------------------------------------------------------------------
-- Protects a path segment, to prevent it from interfering with the
-- url parsing.
-- Input
--   s: binary string to be encoded
-- Returns
--   escaped representation of string binary
-----------------------------------------------------------------------------
local function make_set(t)
	local s = {}
	for i,v in base.ipairs(t) do
		s[t[i]] = 1
	end
	return s
end

-- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
    "-", "_", ".", "!", "~", "*", "'", "(",
	")", ":", "@", "&", "=", "+", "$", ",",
}

local function protect_segment(s)
	return string.gsub(s, "([^A-Za-z0-9_])", function (c)
		if segment_set[c] then return c
		else return string.format("%%%02x", string.byte(c)) end
	end)
end

-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
--   s: binary string to be encoded
-- Returns
--   escaped representation of string binary
-----------------------------------------------------------------------------
function unescape(s)
    return string.gsub(s, "%%(%x%x)", function(hex)
        return string.char(base.tonumber(hex, 16))
    end)
end

-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
--   base_path
--   relative_path
-- Returns
--   corresponding absolute path
-----------------------------------------------------------------------------
local function absolute_path(base_path, relative_path)
    if string.sub(relative_path, 1, 1) == "/" then return relative_path end
    local path = string.gsub(base_path, "[^/]*$", "")
    path = path .. relative_path
    path = string.gsub(path, "([^/]*%./)", function (s)
        if s ~= "./" then return s else return "" end
    end)
    path = string.gsub(path, "/%.$", "/")
    local reduced
    while reduced ~= path do
        reduced = path
        path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
            if s ~= "../../" then return "" else return s end
        end)
    end
    path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
        if s ~= "../.." then return "" else return s end
    end)
    return path
end

-----------------------------------------------------------------------------
-- Parses a url and returns a table with all its parts according to RFC 2396
-- The following grammar describes the names given to the URL parts
-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
-- <authority> ::= <userinfo>@<host>:<port>
-- <userinfo> ::= <user>[:<password>]
-- <path> :: = {<segment>/}<segment>
-- Input
--   url: uniform resource locator of request
--   default: table with default values for each field
-- Returns
--   table with the following fields, where RFC naming conventions have
--   been preserved:
--     scheme, authority, userinfo, user, password, host, port,
--     path, params, query, fragment
-- Obs:
--   the leading '/' in {/<path>} is considered part of <path>
-----------------------------------------------------------------------------
function parse(url, default)
    -- initialize default parameters
    local parsed = {}
    for i,v in base.pairs(default or parsed) do parsed[i] = v end
    -- empty url is parsed to nil
    if not url or url == "" then return nil, "invalid url" end
    -- remove whitespace
    -- url = string.gsub(url, "%s", "")
    -- get fragment
    url = string.gsub(url, "#(.*)$", function(f)
        parsed.fragment = f
        return ""
    end)
    -- get scheme
    url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
        function(s) parsed.scheme = s; return "" end)
    -- get authority
    url = string.gsub(url, "^//([^/]*)", function(n)
        parsed.authority = n
        return ""
    end)
    -- get query stringing
    url = string.gsub(url, "%?(.*)", function(q)
        parsed.query = q
        return ""
    end)
    -- get params
    url = string.gsub(url, "%;(.*)", function(p)
        parsed.params = p
        return ""
    end)
    -- path is whatever was left
    if url ~= "" then parsed.path = url end
    local authority = parsed.authority
    if not authority then return parsed end
    authority = string.gsub(authority,"^([^@]*)@",
        function(u) parsed.userinfo = u; return "" end)
    authority = string.gsub(authority, ":([^:]*)$",
        function(p) parsed.port = p; return "" end)
    if authority ~= "" then parsed.host = authority end
    local userinfo = parsed.userinfo
    if not userinfo then return parsed end
    userinfo = string.gsub(userinfo, ":([^:]*)$",
        function(p) parsed.password = p; return "" end)
    parsed.user = userinfo
    return parsed
end

-----------------------------------------------------------------------------
-- Rebuilds a parsed URL from its components.
-- Components are protected if any reserved or unallowed characters are found
-- Input
--   parsed: parsed URL, as returned by parse
-- Returns
--   a stringing with the corresponding URL
-----------------------------------------------------------------------------
function build(parsed)
    local ppath = parse_path(parsed.path or "")
    local url = build_path(ppath)
    if parsed.params then url = url .. ";" .. parsed.params end
    if parsed.query then url = url .. "?" .. parsed.query end
	local authority = parsed.authority
	if parsed.host then
		authority = parsed.host
		if parsed.port then authority = authority .. ":" .. parsed.port end
		local userinfo = parsed.userinfo
		if parsed.user then
			userinfo = parsed.user
			if parsed.password then
				userinfo = userinfo .. ":" .. parsed.password
			end
		end
		if userinfo then authority = userinfo .. "@" .. authority end
	end
    if authority then url = "//" .. authority .. url end
    if parsed.scheme then url = parsed.scheme .. ":" .. url end
    if parsed.fragment then url = url .. "#" .. parsed.fragment end
    -- url = string.gsub(url, "%s", "")
    return url
end

-----------------------------------------------------------------------------
-- Builds a absolute URL from a base and a relative URL according to RFC 2396
-- Input
--   base_url
--   relative_url
-- Returns
--   corresponding absolute url
-----------------------------------------------------------------------------
function absolute(base_url, relative_url)
    if base.type(base_url) == "table" then
        base_parsed = base_url
        base_url = build(base_parsed)
    else
        base_parsed = parse(base_url)
    end
    local relative_parsed = parse(relative_url)
    if not base_parsed then return relative_url
    elseif not relative_parsed then return base_url
    elseif relative_parsed.scheme then return relative_url
    else
        relative_parsed.scheme = base_parsed.scheme
        if not relative_parsed.authority then
            relative_parsed.authority = base_parsed.authority
            if not relative_parsed.path then
                relative_parsed.path = base_parsed.path
                if not relative_parsed.params then
                    relative_parsed.params = base_parsed.params
                    if not relative_parsed.query then
                        relative_parsed.query = base_parsed.query
                    end
                end
            else    
                relative_parsed.path = absolute_path(base_parsed.path or "",
                    relative_parsed.path)
            end
        end
        return build(relative_parsed)
    end
end

-----------------------------------------------------------------------------
-- Breaks a path into its segments, unescaping the segments
-- Input
--   path
-- Returns
--   segment: a table with one entry per segment
-----------------------------------------------------------------------------
function parse_path(path)
	local parsed = {}
	path = path or ""
	--path = string.gsub(path, "%s", "")
	string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
	for i = 1, #parsed do
		parsed[i] = unescape(parsed[i])
	end
	if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
	if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
	return parsed
end

-----------------------------------------------------------------------------
-- Builds a path component from its segments, escaping protected characters.
-- Input
--   parsed: path segments
--   unsafe: if true, segments are not protected before path is built
-- Returns
--   path: corresponding path stringing
-----------------------------------------------------------------------------
function build_path(parsed, unsafe)
	local path = ""
	local n = #parsed
	if unsafe then
		for i = 1, n-1 do
			path = path .. parsed[i]
			path = path .. "/"
		end
		if n > 0 then
			path = path .. parsed[n]
			if parsed.is_directory then path = path .. "/" end
		end
	else
		for i = 1, n-1 do
			path = path .. protect_segment(parsed[i])
			path = path .. "/"
		end
		if n > 0 then
			path = path .. protect_segment(parsed[n])
			if parsed.is_directory then path = path .. "/" end
		end
	end
	if parsed.is_absolute then path = "/" .. path end
	return path
end

return _ENV

end
--------------------------------------------------------------------------------
-- End of moduledebugger.url
--------------------------------------------------------------------------------


--------------------------------------------------------------------------------
--  Main content
--------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Copyright (c) 2011-2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Sierra Wireless - initial API and implementation
-------------------------------------------------------------------------------

local DBGP_CLIENT_VERSION = "1.4.1"
DBGP_CLIENT_LUA_VERSION = os.getenv "LUA_VERSION" or _VERSION
if DBGP_CLIENT_LUA_VERSION ~= "Lua 5.1" and DBGP_CLIENT_LUA_VERSION ~= "Lua 5.2" then
  print(DBGP_CLIENT_LUA_VERSION .. " is not supported. As fallback, debugger will behave as if it runs on Lua 5.2 vm. You could also try to force it to behave as a Lua 5.1 vm by setting the LUA_VERSION environment variable to 'Lua 5.1'")
  DBGP_CLIENT_LUA_VERSION = "Lua 5.2"
end

local debug = require "debug"

local io = io

-- To avoid cyclic dependency, internal state of the debugger that must be accessed
-- elsewhere (in commands most likely) will be stored in a fake module "debugger.core"
local core = { }
package.loaded["debugger.core"] = core

local util = require "debugger.util"
local platform = require "debugger.platform"
local dbgp = require "debugger.dbgp"
local commands = require "debugger.commands"
local context = require "debugger.context"
local url = require "debugger.url"

local log = util.log
local ser = util.ser

-- TODO complete the stdlib access
local corunning, cocreate, cowrap, coyield, coresume, costatus = coroutine.running, coroutine.create, coroutine.wrap, coroutine.yield, coroutine.resume, coroutine.status


-- register the URI of the debugger, to not jump into with redefined function or coroutine bootstrap stuff
local debugger_uri = nil -- set in init function
local transportmodule_uri = nil -- set in init function

-- will contain the session object, and possibly a list of all sessions if a multi-threaded model is adopted
-- this is only used for async commands.
local active_session = nil

-- 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
core.active_coroutines = { n = 0, from_id = setmetatable({ }, { __mode = "v" }), from_coro = setmetatable({ }, { __mode = "k" }) }

-- "BEGIN VERSION DEPENDENT CODE"
local setbpenv     -- set environment of a breakpoint (compiled function)
if DBGP_CLIENT_LUA_VERSION == "Lua 5.1" then
  local setfenv = setfenv
  setbpenv = setfenv
elseif DBGP_CLIENT_LUA_VERSION == "Lua 5.2" then
  local setupvalue = debug.setupvalue
  -- _ENV is the first upvalue
  setbpenv = function(f, t) return setupvalue(f, 1, t) end
end
-- "END VERSION DEPENDENT CODE"

-------------------------------------------------------------------------------
--  Output redirection handling
-------------------------------------------------------------------------------
-- Override standard output functions & constants to redirect data written to these files to IDE too.
-- This works only for output done in Lua, output written by C extensions is still go to system output file.

-- references to native values
io.base = { output = io.output, stdin = io.stdin, stdout = io.stdout, stderr = io.stderr }

function print(...)
  local buf = {...}
  for i=1, select("#", ...) do
    buf[i] = tostring(buf[i])
  end
  env.info(table.concat(buf, "\t") .. "\n")
end

-- Actually change standard output file but still return the "fake" stdout
function io.output(output)
  io.base.output(output)
  return io.stdout
end

local dummy = function() end

-- metatable for redirecting output (not printed at all in actual output)
core.redirect_output = {
  write = function(self, ...)
    local buf = {...}
    for i=1, select("#", ...) do buf[i] = tostring(buf[i]) end
    buf = table.concat(buf):gsub("\n", "\r\n")
    dbgp.send_xml(self.skt, { tag = "stream", attr = { type=self.mode },  util.b64(buf) } )
  end,
  flush = dummy,
  close = dummy,
  setvbuf = dummy,
  seek = dummy
}
core.redirect_output.__index = core.redirect_output

-- metatable for cloning output (outputs to actual system and send to IDE)
core.copy_output = {
  write = function(self, ...)
    core.redirect_output.write(self, ...)
    io.base[self.mode]:write(...)
  end,
  flush   = function(self, ...) return self.out:flush(...) end,
  close   = function(self, ...) return self.out:close(...) end,
  setvbuf = function(self, ...) return self.out:setvbuf(...) end,
  seek    = function(self, ...) return self.out:seek(...) end,
}
core.copy_output.__index = core.copy_output

-------------------------------------------------------------------------------
--  Breakpoint registry
-------------------------------------------------------------------------------
-- Registry of current stack levels of all running threads
local stack_levels = setmetatable( { }, { __mode = "k" } )

-- File/line mapping for breakpoints (BP). For a given file/line, a list of BP is associated (DBGp specification section 7.6.1
-- require that multiple BP at same place must be handled)
-- A BP is a table with all additional properties (type, condition, ...) the id is the string representation of the table.
core.breakpoints = {
  -- functions to call to match hit conditions
  hit_conditions = {
    [">="] = function(value, target) return value >= target end,
    ["=="] = function(value, target) return value == target end,
    ["%"]  = function(value, target) return (value % target) == 0 end,
  }
}

-- tracks events such as step_into or step_over
core.events = { }

do
  local file_mapping = { }
  local id_mapping = { }
  local waiting_sessions = { } -- sessions that wait for an event (over, into, out)
  local step_into = nil        -- session that registered a step_into event, if any
  local sequence = 0 -- used to generate breakpoint IDs

  --- Inserts a new breakpoint into registry
  -- @param bp (table) breakpoint data
  -- @param uri (string, optional) Absolute file URI, for line breakpoints
  -- @param line (number, optional) Line where breakpoint stops, for line breakpoints
  -- @return breakpoint identifier
  function core.breakpoints.insert(bp)
    local bpid = sequence
    sequence = bpid + 1
    bp.id = bpid
    -- re-encode the URI to avoid any mismatch (with authority for example)
    local uri = url.parse(bp.filename)
    bp.filename = url.build{ scheme=uri.scheme, authority="", path=platform.normalize(uri.path)}

    local filereg = file_mapping[bp.filename]
    if not filereg then
      filereg = { }
      file_mapping[bp.filename] = filereg
    end

    local linereg = filereg[bp.lineno]
    if not linereg then
      linereg = {}
      filereg[bp.lineno] = linereg
    end

    --ser( "INFO", { bp=bp } )

    table.insert(linereg, bp)

    id_mapping[bpid] = bp
    return bpid
  end

  --- If breakpoint(s) exists for given file/line, uptates breakpoint counters
  -- and returns whether a breakpoint has matched (boolean)
  function core.breakpoints.at(file, line)
  
    local bps = file_mapping[file] and file_mapping[file][line]
    if not bps then return nil end
    
    local do_break = false
    for _, bp in pairs(bps) do
      if bp.state == "enabled" then
        local match = true
        if bp.condition then
          -- TODO: this is not the optimal solution because Context can be instantiated twice if the breakpoint matches
          local cxt = context.Context:new(active_session.coro, 0)
          setbpenv(bp.condition, cxt)
          local success, result = pcall(bp.condition)
          if not success then log("ERROR", "Condition evaluation failed for breakpoint at %s:%d: %s", file, line, result) end
          -- debugger always stops if an error occurs
          match = (not success) or result
        end
        if match then
          bp.hit_count = bp.hit_count + 1
          if core.breakpoints.hit_conditions[bp.hit_condition](bp.hit_count, bp.hit_value) then
            if bp.temporary then
              core.breakpoints.remove(bp.id)
            end
            do_break = true
            -- there is no break to handle multiple breakpoints: all hit counts must be updated
          end
        end
      end
    end
    return do_break
  end

  function core.breakpoints.get(id)
    if id then return id_mapping[id]
    else return id_mapping end
  end

  function core.breakpoints.remove(id)
    local bp = id_mapping[id]
    if bp then
      id_mapping[id] = nil
      local linereg = file_mapping[bp.filename][bp.lineno]
      for i=1, #linereg do
        if linereg[i] == bp then
          table.remove(linereg, i)
          break
        end
      end

      -- cleanup file_mapping
      if not next(linereg) then file_mapping[bp.filename][bp.lineno] = nil end
      if not next(file_mapping[bp.filename]) then file_mapping[bp.filename] = nil end
      return true
    end
    return false
  end

  --- Returns an XML data structure that describes given breakpoint
  -- @param id (number) breakpoint ID
  -- @return Table describing a <breakpooint> tag or nil followed by an error message
  function core.breakpoints.get_xml(id)
    local bp = id_mapping[id]
    if not bp then return nil, "No such breakpoint: "..tostring(id) end

    local response = { tag = "breakpoint", attr = { } }
    for k,v in pairs(bp) do response.attr[k] = v end
    if bp.expression then
      response[1] = { tag = "expression",  bp.expression }
    end

    -- internal use only
    response.attr.expression = nil
    response.attr.condition = nil
    response.attr.temporary = nil -- TODO: the specification is not clear whether this should be provided, see other implementations
    return response
  end

  --- Register an event to be triggered.
  -- @param event event name to register (must be "over", "out" or "into")
  function core.events.register(event)
    local thread = active_session.coro[1]
    log("DEBUG", "Registered %s event for %s (%d)", event, tostring(thread), stack_levels[thread])
    if event == "into" then
      step_into = true
    else
      waiting_sessions[thread] = { event, stack_levels[thread] }
    end
  end

  --- Returns if an event (step into, over, out) is triggered.
  -- Does *not* discard events (even if they match) as event must be discarded manually if a breakpoint match before anyway.
  -- @return true if an event has matched, false otherwise
  function core.events.does_match()
    if step_into then return true end

    local thread = active_session.coro[1]
    local event = waiting_sessions[thread]
    if event then
      local event_type, target_level = unpack(event)
      local current_level = stack_levels[thread]

      if (event_type == "over" and current_level <= target_level) or   -- step over
        (event_type == "out"  and current_level <  target_level) then -- step out
        log("DEBUG", "Event %s matched!", event_type)
        return true
      end
    end
    return false
  end

  --- Discards event for current thread (if any)
  function core.events.discard()
    waiting_sessions[active_session.coro[1]] = nil
    step_into = nil
  end
end

-------------------------------------------------------------------------------
--  Debugger main loop
-------------------------------------------------------------------------------

--- Send the XML response to the previous continuation command and clear the previous context
function core.previous_context_response(self, reason)
  self.previous_context.status = self.state
  self.previous_context.reason = reason or "ok"
  dbgp.send_xml(self.skt, { tag = "response", attr = self.previous_context } )
  self.previous_context = nil
end

local function cleanup()
  coroutine.resume, coroutine.wrap = coresume, cowrap
  for _, coro in pairs(core.active_coroutines.from_id) do
    debug.sethook(coro)
  end
  -- to remove hook on the main coroutine, it must be the current one (otherwise, this is a no-op) and this function
  -- have to be called adain later on the main thread to finish cleaup
  debug.sethook()
  core.active_coroutines.from_id, core.active_coroutines.from_coro = { }, { }
end

--- This function handles the debugger commands while the execution is paused. This does not use coroutines because there is no
-- way to get main coro in Lua 5.1 (only in 5.2)
local function debugger_loop(self, async_packet)
  self.skt:settimeout(nil) -- set socket blocking

  -- in async mode, the debugger does not wait for another command before continuing and does not modify previous_context
  local async_mode = async_packet ~= nil

  if self.previous_context and not async_mode then
    self.state = "break"
    core.previous_context_response(self)
  end
  self.stack = context.ContextManager(self.coro) -- will be used to mutualize context allocation for each loop

  while true do
    -- reads packet
    local packet = async_packet or dbgp.read_packet(self.skt)
    if not packet then
      log("WARNING", "lost debugger connection")
      cleanup()
      break
    end

    async_packet = nil
    log("DEBUG", packet)
    local cmd, args, data = dbgp.cmd_parse(packet)

    -- FIXME: command such as continuations sent in async mode could lead both engine and IDE in inconsistent state :
    --        make a blacklist/whitelist of forbidden or allowed commands in async ?
    -- invoke function
    local func = commands[cmd]
    if func then
      local ok, cont = xpcall(function() return func(self, args, data) end, debug.traceback)
      if not ok then -- internal exception
        local code, msg, attr
        if type(cont) == "table" and getmetatable(cont) == dbgp.DBGP_ERR_METATABLE then
          code, msg, attr = cont.code, cont.message, cont.attr
        else
          code, msg, attr = 998, tostring(cont), { }
        end
        log("ERROR", "Command %s caused: (%d) %s", cmd, code, tostring(msg))
        attr.command, attr.transaction_id = cmd, args.i
        dbgp.send_xml(self.skt, { tag = "response", attr = attr, dbgp.make_error(code, msg) } )
      elseif cont then
        self.previous_context = { command = cmd, transaction_id = args.i }
        break
      elseif cont == nil and async_mode then
        break
      elseif cont == false then -- In case of commands that fully resumes debugger loop, the mode is sync
        async_mode = false
      end
    else
      log("Got unknown command: "..cmd)
      dbgp.send_xml(self.skt, { tag = "response", attr = { command = cmd, transaction_id = args.i, }, dbgp.make_error(4) } )
    end
  end

  self.stack = nil -- free allocated contexts
  self.state = "running"
  self.skt:settimeout(0) -- reset socket to async
end

-- Stack handling can be pretty complex sometimes, especially with LuaJIT (as tail-call optimization are
-- more aggressive as stock Lua). So all debugger stuff is done in another coroutine, which leave the program
-- stack in a clean state and allow faster and clearer stack operations (no need to remove all debugger calls
-- from stack for each operation).
-- However, this does not always work with stock Lua 5.1 as the main coroutine cannot be referenced
-- (coroutine.running() return nil). For this particular case, the debugger loop is started on the top of
-- program stack and every stack operation is relative the the hook level (see MainThread in util.lua).
local function line_hook(line)
  local do_break, packet = nil, nil
  local info = active_session.coro:getinfo(0, "S")
  
  local ModifiedSource = info.source
  --ModifiedSource = ModifiedSource:match( '^Scripts/Moose/(.*)' ) or ModifiedSource
  --ModifiedSource = ModifiedSource
  
  --ser( "INFO", { source = ModifiedSource } )
  local uri = platform.get_uri(ModifiedSource)
  --ser( "INFO", {uri=uri} )
  if uri and uri ~= debugger_uri and uri ~= transportmodule_uri then -- the debugger does not break if the source is not known
    do_break = core.breakpoints.at(uri, line) or core.events.does_match()
    if do_break then
      core.events.discard()
    end

    -- check for async commands
    if not do_break then
      packet = dbgp.read_packet(active_session.skt)
      if packet then do_break = true end
    end
  end

  if do_break then
    local success, err = pcall(debugger_loop, active_session, packet)
    if not success then log("ERROR", "Error while debug loop: "..err) end
  end
end

local line_hook_coro = cocreate(function(line)
  while true do
    line_hook(line)
    line = coyield()
  end
end)

local function debugger_hook(event, line)
  local thread = corunning() or "main"
  if event == "call" then
    stack_levels[thread] = stack_levels[thread] + 1
  elseif event == "tail call" then
  -- tail calls has no effects on stack handling: it is only used only for step commands but a such even does not
  -- interfere with any of them
  elseif event == "return" or event == "tail return" then
    stack_levels[thread] = stack_levels[thread] - 1
  else -- line event: check for breakpoint
    active_session.coro = util.CurrentThread(corunning())
    if active_session.coro[1] == "main" then
      line_hook(line)
    else
      -- run the debugger loop in another thread on the other cases (simplifies stack handling)
      assert(coresume(line_hook_coro, line))
    end
    active_session.coro = nil
  end
end

if rawget(_G, "jit") then
  debugger_hook = function(event, line)
    local thread = corunning() or "main"
    if event == "call" then
      if debug.getinfo(2, "S").what == "C" then return end
      stack_levels[thread] = stack_levels[thread] + 1
    elseif event == "return" or event == "tail return" then
      -- 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).
      -- So the only reliable way to know stack depth is to walk it.
      local depth = 2
      -- TODO: find the fastest way to call getinfo ('what' parameter)
      while debug.getinfo(depth, "f") do depth = depth + 1 end
      stack_levels[thread] = depth - 2
    elseif event == "line" then
      active_session.coro = util.CurrentThread(corunning())
      if active_session.coro[1] == "main" then
        line_hook(line)
      else
        -- run the debugger loop in another thread on the other cases (simplifies stack handling)
        assert(coresume(line_hook_coro, line))
      end
      active_session.coro = nil
    end
  end
end

local function sendInitPacket(skt,idekey)
  -- get the root script path (the highest possible stack index)
  local source
  for i=2, math.huge do
    local info = debug.getinfo(i)
    if not info then break end
    source = platform.get_uri(info.source) or source
  end
  if not source then source = "unknown:/" end -- when loaded before actual script (with a command line switch)

  -- generate some kind of thread identifier
  local thread = corunning() or "main"
  stack_levels[thread] = 1 -- the return event will set the counter to 0
  local sessionid = tostring(os.time()) .. "_" .. tostring(thread)

  dbgp.send_xml(skt, { tag = "init", attr = {
    appid = "Lua DBGp",
    idekey = idekey,
    session = sessionid,
    thread = tostring(thread),
    parent = "",
    language = "Lua",
    protocol_version = "1.0",
    fileuri = source
  } })
  
  return sessionid
end

local function init(host, port, idekey, transport, executionplatform, workingdirectory, nbRetry)
  -- get connection data
  local host = host or os.getenv "DBGP_IDEHOST" or "127.0.0.1"
  local port = port or os.getenv "DBGP_IDEPORT" or "10000"
  local idekey = idekey or os.getenv("DBGP_IDEKEY") or "luaidekey"

  -- init plaform module
  local executionplatform = executionplatform or os.getenv("DBGP_PLATFORM") or nil
  local workingdirectory = workingdirectory or os.getenv("DBGP_WORKINGDIR") or nil
  platform.init(executionplatform,workingdirectory)

  -- get transport layer
  local transportpath = transport or os.getenv("DBGP_TRANSPORT") or "debugger.transport.luasocket"
  local transport = require(transportpath)

  -- nb retry for connection
  local nbRetry = nbRetry or os.getenv("DBGP_NBRETRY") or 10
  nbRetry = math.max(nbRetry,1)

  -- install base64 functions into util
  util.b64, util.rawb64, util.unb64 = transport.b64, transport.rawb64, transport.unb64

  -- get the debugger and transport layer URI
  debugger_uri = platform.get_uri(debug.getinfo(1).source)
  transportmodule_uri = platform.get_uri(debug.getinfo(transport.create).source)

  -- try to connect several times: if IDE launches both process and server at same time, first connect attempts may fail
  local skt,ok, err
  print(string.format("Debugger v%s", DBGP_CLIENT_VERSION))
  print(string.format("Debugger: Trying to connect to %s:%s ... ", host, port))

  local timeelapsed = 0
  local sessionid = nil
  for i=1,nbRetry do
    -- try to connect to DBGP server
    skt = assert(transport.create())
    skt:settimeout(nil)
    ok, err = skt:connect(host, port)
    if ok then
      sessionid = sendInitPacket(skt,idekey)
      -- test if socket is closed
      ok, err = skt:receive(0)
      if err == nil then print("Debugger: Connection succeed.") break end
    end

    if err ~= nil then
      -- failed to connect
      print(string.format("Debugger: Failed to connect to %s:%s (%s)", host, port, err))
      skt:close()

      -- wait&retry
      local timetowait = math.min(3, math.max(timeelapsed/2, 0.25))
      if i < nbRetry then 
        print(string.format("Debugger: Retrying to connect to %s:%s in %.2fs ... ", host, port,timetowait))
        transport.sleep(timetowait)
        timeelapsed  = timeelapsed+timetowait
      end
    end
  end
  if err then error(string.format("Cannot connect to %s:%d : %s", host, port, err)) end  

  --FIXME util.CurrentThread(corunning) => util.CurrentThread(corunning()) WHAT DOES IT FIXES ??
  local sess = { skt = skt, state = "starting", id = sessionid, coro = util.CurrentThread(corunning) }
  active_session = sess
  debugger_loop(sess)

  -- set debug hooks
  debug.sethook(debugger_hook, "rlc")

  -- install coroutine collecting functions.
  -- TODO: maintain a list of *all* coroutines can be overkill (for example, the ones created by copcall), make a extension point to
  -- customize debugged coroutines
  -- coroutines are referenced during their first resume (so we are sure that they always have a stack frame)
  local function resume_handler(coro, ...)
    if costatus(coro) == "dead" then
      local coro_id = core.active_coroutines.from_coro[coro]
      core.active_coroutines.from_id[coro_id] = nil
      core.active_coroutines.from_coro[coro] = nil
      stack_levels[coro] = nil
    end
    return ...
  end

  function coroutine.resume(coro, ...)
    if not stack_levels[coro] then
      -- first time referenced
      stack_levels[coro] = 0
      core.active_coroutines.n = core.active_coroutines.n + 1
      core.active_coroutines.from_id[core.active_coroutines.n] = coro
      core.active_coroutines.from_coro[coro] = core.active_coroutines.n
      debug.sethook(coro, debugger_hook, "rlc")
    end
    return resume_handler(coro, coresume(coro, ...))
  end

  -- coroutine.wrap uses directly C API for coroutines and does not trigger our overridden coroutine.resume
  -- so this is an implementation of wrap in pure Lua
  local function wrap_handler(status, ...)
    if not status then error((...)) end
    return ...
  end

  function coroutine.wrap(f)
    local coro = coroutine.create(f)
    return function(...)
      return wrap_handler(coroutine.resume(coro, ...))
    end
  end

  return sess
end

return init
