From 6e7f2dce9b214232547d66f08cb5562d1dfb9c6b Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 5 Apr 2019 16:46:20 -0300 Subject: [PATCH 1/2] include evaluation of __tostring for userdata values Closes #38. --- inspect.lua | 20 ++++++++++++++++++++ spec/inspect_spec.lua | 11 +++++++++++ 2 files changed, 31 insertions(+) diff --git a/inspect.lua b/inspect.lua index e2e3806..da01bdf 100644 --- a/inspect.lua +++ b/inspect.lua @@ -124,6 +124,19 @@ local function getNonSequentialKeys(t) return keys, keysLength, sequenceLength end +local function getToStringResultSafely(t, mt) + if type(t) ~= "userdata" then + return + end + local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') + local str, ok + if type(__tostring) == 'function' then + ok, str = pcall(__tostring, t) + str = ok and str or 'error: ' .. tostring(str) + end + if type(str) == 'string' and #str > 0 then return str end +end + local function countTableAppearances(t, tableAppearances) tableAppearances = tableAppearances or {} @@ -293,6 +306,13 @@ function Inspector:putValue(v) self:puts(tostring(v)) elseif tv == 'table' then self:putTable(v) + elseif tv == 'userdata' then + local mt = getmetatable(v) + local toStringResult = mt and getToStringResultSafely(v, mt) + self:puts('<', tv, ' ', self:getId(v), '>') + if toStringResult then + self:puts(' -- ', escape(toStringResult)) + end else self:puts('<', tv, ' ', self:getId(v), '>') end diff --git a/spec/inspect_spec.lua b/spec/inspect_spec.lua index edea720..995361d 100644 --- a/spec/inspect_spec.lua +++ b/spec/inspect_spec.lua @@ -363,6 +363,17 @@ describe( 'inspect', function() ]]), inspect(bar)) end) + it('includes the __tostring metamethod of userdata', function() + local tbl = { + f = io.tmpfile(), + } + assert.equals(unindent([[ + { + f = -- $FILE + } + ]]):gsub("$FILE", tostring(tbl.f)), inspect(tbl)) + end) + it('can be used on the __tostring metamethod of a table without errors', function() local f = function(x) return inspect(x) end local tbl = setmetatable({ x = 1 }, { __tostring = f }) From c32cdda2bb58b63ab26ac9031295422e89fb81ca Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Fri, 5 Apr 2019 16:59:40 -0300 Subject: [PATCH 2/2] add a special case for NULL userdata When interpreting userdata values using 'inspect', the numeric identifiers are useful to match identify of pointers (comparing the low counters like ` is easier than reading long pointers like `userdata: 0x749abc39efa29`). However, the `NULL` pointer is enough of a special case that it is important to know when one of those numbered pointers happens to be `NULL`. When one is dealing with things like JSON parsers, where the only usual userdata is `cjson.null`, one gets accostumed over time to interpret `` to mean `NULL`. This is not obvious, however, and a seasoned user will trip up the day another userdata is used in the same table and `NULL` is now ``. Adding a test for this would require compiling and loading a C extension. --- inspect.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/inspect.lua b/inspect.lua index da01bdf..73f0a23 100644 --- a/inspect.lua +++ b/inspect.lua @@ -309,9 +309,16 @@ function Inspector:putValue(v) elseif tv == 'userdata' then local mt = getmetatable(v) local toStringResult = mt and getToStringResultSafely(v, mt) - self:puts('<', tv, ' ', self:getId(v), '>') if toStringResult then + self:puts('') self:puts(' -- ', escape(toStringResult)) + else + local str = tostring(v) + if str == "userdata: NULL" then + self:puts('') + else + self:puts('') + end end else self:puts('<', tv, ' ', self:getId(v), '>')