blob: 6e6479e7c70f0d6679663627912b3e071bdb5e92 [file] [log] [blame]
-------------------------------------------------------------------------------
-- 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