From b405afeae81e9ce71c45971bc5dfebcc809f9763 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Thu, 5 Jan 2017 15:50:44 +0100 Subject: [PATCH] Improved compatibility with PUC Lua 5.1 - 5.3 --- pretty.lua | 24 +++++++---- test/TestSuite.lua | 21 ++++++++++ test/test_pretty.lua | 91 ++++++++++++++++++++++++++++------------ test/test_resilience.lua | 25 ++++++++++- 4 files changed, 126 insertions(+), 35 deletions(-) diff --git a/pretty.lua b/pretty.lua index b14c4f9..2c1c190 100644 --- a/pretty.lua +++ b/pretty.lua @@ -192,6 +192,8 @@ local function escape_string (str) end local function get_function_info (f) + -- NOTE: Functions best in LuaJIT or Lua 5.2+ + -- Regarding get-info: -- * No need to includ 'f'. Function is already known -- * No need to include 'L' (active lines) option. Ignored @@ -199,11 +201,19 @@ local function get_function_info (f) local info = debug.getinfo(f, 'Su') info.params = {} info.ups = {} - info.env = debug.getfenv(f) + info.env = debug.getfenv and debug.getfenv(f) info.builtin = info.source == '=[C]' - for i = 1, info.nparams do info.params[i] = debug.getlocal(f, i) end - if info.isvararg then info.params[#info.params+1] = '...' end - for i = 1, info.nups do local k, v = debug.getupvalue(f, i); info.ups[k] = v end + for i = 1, info.nparams or 0 do info.params[i] = debug.getlocal(f, i) end + if info.isvararg or not info.nparams then info.params[#info.params+1] = '...' end + -- Get upvalues + for i = 1, info.nups do + local k, v = debug.getupvalue(f, i) + if k == '_ENV' and not debug.getfenv then + info.env = v + else + info.ups[k] = v + end + end if info.source:sub(1,1) == '=' then info.defined_how = 'C' elseif info.source:sub(1,1) == '@' then info.defined_how = 'file' @@ -361,7 +371,7 @@ local function format_key_and_value_string_map (l, key, value, options, depth) l[#l+1] = key l[#l+1] = { #key, 'key' } l[#l+1] = ' = ' - format_value(value, options, depth, l) + return format_value(value, options, depth, l) end local function format_key_and_value_arbitr_map (l, key, value, options, depth) @@ -371,11 +381,11 @@ local function format_key_and_value_arbitr_map (l, key, value, options, depth) l[#l+1] = ']' l[#l+1] = { width_of_strings_in_l(l, index_before_key), 'key' } l[#l+1] = ' = ' - format_value(value, options, depth, l) + return format_value(value, options, depth, l) end local function format_key_and_value_sequence (l, key, value, options, depth) - format_value(value, options, depth, l) + return format_value(value, options, depth, l) end local TABLE_TYPE_TO_PAIR_FORMAT = { diff --git a/test/TestSuite.lua b/test/TestSuite.lua index 67d3435..2b1d96c 100644 --- a/test/TestSuite.lua +++ b/test/TestSuite.lua @@ -57,6 +57,27 @@ local function find_cutoff_index_for_traceback_string (str) return index end +-------------------------------------------------------------------------------- +-- Lua 5.2 compat + +if not setfenv then -- Lua 5.2 + -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html + -- this assumes f is a function + local function findenv(f) + local level = 1 + repeat + local name, value = debug.getupvalue(f, level) + if name == '_ENV' then return level, value end + level = level + 1 + until name == nil + return nil end + getfenv = function (f) return(select(2, findenv(f)) or _G) end + setfenv = function (f, t) + local level = findenv(f) + if level then debug.setupvalue(f, level, t) end + return f end +end + -------------------------------------------------------------------------------- local TestSuite = {} diff --git a/test/test_pretty.lua b/test/test_pretty.lua index 7d37ff7..287aeec 100644 --- a/test/test_pretty.lua +++ b/test/test_pretty.lua @@ -12,8 +12,19 @@ Approximate strings not similar enough: -------------------------------------------------------------------------------- +-- Lua 5.1 compat: + +local HAS_ADV_GETLOCAL = not not debug.getinfo(1, 'u').nparams + +-- Lua 5.3 compat: + +if not loadstring then + loadstring = load +end + local function format_test (t) - if t.longterm then return end + if t.longterm then return end + if t.adv_getlocal and not HAS_ADV_GETLOCAL then return end SUITE:addTest(t.expect, function () local input_value = t.input local input_options = t.options @@ -175,26 +186,31 @@ format_test { -- Function printing format_test { + adv_getlocal = true, input = function () end, expect = 'function () ... end', } format_test { + adv_getlocal = true, input = function (a) end, expect = 'function (a) ... end', } format_test { + adv_getlocal = true, input = function (a, b) end, expect = 'function (a, b) ... end', } format_test { + adv_getlocal = true, input = function (...) end, expect = 'function (...) ... end', } format_test { + adv_getlocal = true, input = function (a, b, ...) end, expect = 'function (a, b, ...) ... end', } @@ -203,72 +219,86 @@ do local SOME_RANDOM_UPVALUE = false format_test { + adv_getlocal = true, input = function () l = SOME_RANDOM_UPVALUE end, expect = 'function () ... end', } format_test { + adv_getlocal = true, input = function () SOME_RANDOM_UPVALUE = true end, expect = 'function () ... end', } format_test { -- More function info is ignored if not at depth 0. + adv_getlocal = true, input = { a = function () SOME_RANDOM_UPVALUE = true end }, options = { more_function_info = true }, expect = '{\n\ta = function () ... end\n}', } + + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function format_test { input = function () l = SOME_RANDOM_UPVALUE end, + adv_getlocal = true, options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: 223]\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend' + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: '..func_line..']\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend' } + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function format_test { input = function () SOME_RANDOM_UPVALUE = true end, + adv_getlocal = true, options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: 230]\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend' + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: '..func_line..']\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend' } end -format_test { - input = function () end, - options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: 238]\n\n\t...\nend' -} +do + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function + format_test { + input = function () end, + adv_getlocal = true, + options = { more_function_info = true }, + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: '..func_line..']\n\n\t...\nend' + } +end do local index = 0 format_test { input = function () index = index + 1; return index end, + adv_getlocal = true, expect = 'function () ... end' } + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function format_test { input = function () index = index + 1; return index end, + adv_getlocal = true, options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: 253]\n\t-- up_values: { index = 0 }\n\n\t...\nend' + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: '..func_line..']\n\t-- up_values: { index = 0 }\n\n\t...\nend' } end format_test { + adv_getlocal = true, input = loadstring('return function () end')(), expect = 'function () end', } format_test { + adv_getlocal = true, input = loadstring('return function () return function () end end')(), expect = 'function () return function () end end', } format_test { longterm = true, + adv_getlocal = true, input = loadstring('return function () return function () end\nend')()(), expect = 'function () end', } @@ -276,6 +306,7 @@ format_test { format_test { -- NOTE: This is HARD to fix. It's thus longerterm longterm = true, + adv_getlocal = true, input = loadstring('return function () return function () end end')()(), expect = 'function () end', } @@ -294,21 +325,25 @@ format_test { expect = 'function (a, b)\n\treturn a + b\nend', } -format_test { - input = function () - -- NOTE: This function must cover 3 lines of code! - end, - options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Lines: 298 - 300]\n\n\t...\nend', -} +do + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function + format_test { + input = function () + -- NOTE: This function must cover 3 lines of code! + end, + adv_getlocal = true, + options = { more_function_info = true }, + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Lines: '..func_line..' - '..(func_line+2)..']\n\n\t...\nend', + } -format_test { - input = function () --[[ NOTE: This function must cover a single line of code! ]] end, - options = { more_function_info = true }, - -- TODO: Make this more general, such that it won't fail when adding more tests above it. - expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: 307]\n\n\t...\nend', -} + local func_line = debug.getinfo(1).currentline + 2 -- Must be exactly 2 lines above function + format_test { + input = function () --[[ NOTE: This function must cover a single line of code! ]] end, + adv_getlocal = true, + options = { more_function_info = true }, + expect = 'function ()\n\t-- source_file: \'./test/test_pretty.lua\' [Line: '..func_line..']\n\n\t...\nend', + } +end format_test { input = math.abs, @@ -348,12 +383,14 @@ format_test { format_test { -- The function part should align, if some are builtin and some are not. + adv_getlocal = true, input = { random = math.random, abs = function (x) return x < 0 and -x or x end }, expect = '{\n\tabs = function (x) ... end,\n\trandom = builtin function ([m [, n]) ... end\n}', } format_test { -- No special indent if no special function modifier. + adv_getlocal = true, input = { max = function(a, b) return a > b and a or b end, abs = function (x) return x < 0 and -x or x end }, diff --git a/test/test_resilience.lua b/test/test_resilience.lua index db31515..121ed91 100644 --- a/test/test_resilience.lua +++ b/test/test_resilience.lua @@ -1,7 +1,30 @@ +-------------------------------------------------------------------------------- +-- Lua 5.2 compat + +if not setfenv then -- Lua 5.2 + -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html + -- this assumes f is a function + local function findenv(f) + local level = 1 + repeat + local name, value = debug.getupvalue(f, level) + if name == '_ENV' then return level, value end + level = level + 1 + until name == nil + return nil end + getfenv = function (f) return(select(2, findenv(f)) or _G) end + setfenv = function (f, t) + local level = findenv(f) + if level then debug.setupvalue(f, level, t) end + return f end +end + +-------------------------------------------------------------------------------- + local SUITE = require('TestSuite').new('resilience') SUITE:setEnviroment{ - --format = require('pretty') + setfenv = setfenv } --------------------------------------------------------------------------------