rsi/objc/objc_inspect.lua

247 lines
6.6 KiB
Lua
Raw Normal View History

2023-08-27 12:32:38 +00:00
local objc = require'objc'
local inspect = {}
objc.inspect = inspect
--pretty helpers
local _ = string.format
local function p(...) --formatted line
print(_(...))
end
local function isort(iter, method) --sort an iterator of object by a method
local t = {}
while true do
local v = iter()
if v == nil then break end
t[#t+1] = v
end
table.sort(t, function(a, b) return a[method](a) < b[method](b) end)
local i = 0
return function()
i = i + 1
if t[i] == nil then return end
return t[i]
end
end
local function icount(iter)
local n = 0
for _ in iter do
n = n + 1
end
return n
end
--class header
local function protocols_spec(protocols)
local t = {}
for proto in isort(protocols, 'name') do
t[#t+1] = proto:name() .. protocols_spec(proto:protocols())
end
return #t > 0 and _(' <%s>', table.concat(t, ', ')) or ''
end
local function class_spec(cls, indent)
indent = indent or 1
local super_spec = objc.superclass(cls) and
_('\n%s<- %s', (' '):rep(indent), class_spec(objc.superclass(cls), indent + 1)) or ''
return objc.classname(cls) .. protocols_spec(objc.protocols(cls)) .. super_spec
end
local function protocol_spec(proto)
return proto:name() .. protocols_spec(proto:protocols())
end
function inspect.class_header(cls)
p('Class %s', class_spec(objc.class(cls)))
end
--classes
function inspect.classes()
for cls in objc.classes() do
p('%-50s protocols: %-5s properties: %-5s ivars: %-5s methods (i): %-5s methods (c): %-5s',
objc.classname(cls),
icount(objc.protocols(cls)),
icount(objc.properties(cls)),
icount(objc.ivars(cls)),
icount(objc.methods(cls)),
icount(objc.methods(objc.metaclass(cls))))
end
end
--protocols
function inspect.protocols()
for proto in objc.protocols() do
p('%-50s %-10s protocols: %-5s properties: %-5s methods (i/o): %-5s methods (i/r): %-5s methods (c/o): %-5s methods (c/r): %-5s',
proto:name(), proto.formal and 'formal' or 'informal',
icount(proto:protocols()),
icount(proto:properties()),
icount(proto:methods(true, false)),
icount(proto:methods(true, true)),
icount(proto:methods(false, false)),
icount(proto:methods(false, true)))
end
end
--properties
local function inspect_properties(name, props)
for prop in isort(props, 'name') do
p('%-30s %-40s %-8s %-20s %-20s %-20s %-20s',
name, prop:name(), prop:readonly() and 'r/o' or 'r/w',
prop:ctype(), prop:ivar() or '', prop:getter(), prop:setter() or '')
end
end
local function not_(arg, list, process, ...)
if arg then return end
for arg in list() do
process(arg, ...)
end
return true
end
function inspect.protocol_properties(proto)
if not_(proto, objc.protocols, inspect.protocol_properties) then return end
proto = objc.protocol(proto)
inspect_properties(proto:name(), proto:properties())
end
function inspect.class_properties(cls)
if not_(cls, objc.classes, inspect.class_properties) then return end
cls = objc.class(cls)
inspect_properties(objc.classname(cls), objc.properties(cls))
end
--methods
function inspect.class_methods(cls, inst)
if not_(cls, objc.classes, inspect.class_methods, inst) then return end
cls = inst and objc.class(cls) or objc.metaclass(cls)
for meth in isort(objc.methods(cls), 'name') do
p('%-40s %-50s %-50s %s', objc.classname(cls), meth:name(),
objc.ftype_ctype(objc.method_ftype(cls, meth:selector(), meth)), meth:mtype())
end
end
function inspect.protocol_methods(proto, inst, required)
if not_(proto, objc.protocols, inspect.protocol_methods, inst, required) then return end
proto = objc.protocol(proto)
for selname, mtype in proto:methods(inst or false, required or false) do
p('%-40s %-50s %-50s %s', proto:name(), selname, objc.ftype_ctype(objc.mtype_ftype(mtype)), mtype)
end
end
--ivars
function inspect.class_ivars(cls)
if not_(cls, objc.classes, inspect.ivars) then return end
cls = objc.class(cls)
for ivar in isort(objc.ivars(cls), 'name') do
p('%-40s %-50s %-50s %-5s %s', objc.classname(cls), ivar:name(), ivar:ctype(),
tonumber(ivar:offset()), ivar:stype())
end
end
--full class inspection
function inspect.class(cls)
print''
inspect.class_header(cls)
print'\nProperties:\n'
inspect.class_properties(cls)
print'\nIvars:\n'
inspect.class_ivars(cls)
print'\nMethods:\n'
inspect.class_methods(cls)
end
function inspect.protocol(proto)
print''
print(objc.protocol(proto):name())
print'\nProperties:\n'
inspect.protocol_properties(proto)
print'\nMethods (i/o):\n'
inspect.protocol_methods(proto, true, false)
print'\nMethods (i/r):\n'
inspect.protocol_methods(proto, true, true)
print'\nMethods (c/o):\n'
inspect.protocol_methods(proto, false, false)
print'\nMethods (c/r):\n'
inspect.protocol_methods(proto, false, true)
end
function inspect.find(patt)
--TODO: find framework / load all frameworks
local function find_in_class(prefix, cls)
for meth in objc.methods(cls) do
if meth:name():find(patt) then
p('%-20s [%s %s]', prefix..' method', objc.classname(cls), meth:name())
end
end
for prop in objc.properties(cls) do
if prop:name():find(patt) then
p('%-20s %s.%s', prefix..' property', objc.classname(cls), prop:name())
end
end
for ivar in objc.ivars(cls) do
if ivar:name():find(patt) then
p('%-20s %s.%s', prefix..' ivar', objc.classname(cls), ivar:name())
end
end
end
for cls in objc.classes() do
if objc.classname(cls):find(patt) then
p('%-20s %s', 'class', objc.classname(cls))
end
find_in_class('instance', cls)
find_in_class('class', cls)
end
local function find_proto_method(proto, postfix, inst, required)
for selname in proto:methods(inst, required) do
if selname:find(patt) then
p('%-20s [%s %s]', 'protocol method ('..postfix..')', proto:name(), selname)
end
end
end
for proto in objc.protocols() do
if proto:name():find(patt) then
p('%-20s %s', 'protocol', proto:name())
end
find_proto_method(proto, 'i/o', true, false)
find_proto_method(proto, 'i/r', true, true)
find_proto_method(proto, 'c/o', false, false)
find_proto_method(proto, 'c/r', false, true)
for prop in proto:properties() do
if prop:name():find(patt) then
p('%-20s %s.%s', 'protocol property', proto:name(), prop:name())
end
end
end
local function find_global(title, prefix, namespace)
for k in pairs(namespace) do
if type(k) == 'string' and k:find(patt) then
p('%-20s %s%s', title, prefix, k)
end
end
end
find_global('global', 'objc.', objc)
find_global('C global', 'objc.', objc.debug.cnames.global)
find_global('C struct', '', objc.debug.cnames.struct)
end
if not ... then
for k,v in pairs(inspect) do
print(_('%-10s %s', type(v), 'objc.inspect.'..k))
end
end
return inspect