--!A cross-platform build utility based on Lua -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Copyright (C) 2015-present, Xmake Open Source Community. -- -- @author OpportunityLiu -- @file dump.lua -- -- define module local dump = dump or {} -- load modules local todisplay = require("base/todisplay") -- print string function dump._print_string(str, as_key) io.write(todisplay._print_string(str, as_key)) end -- print keyword function dump._print_keyword(keyword) io.write(todisplay._print_keyword(keyword)) end -- print function function dump._print_function(func, as_key) if as_key then io.write(todisplay._translate("${reset}${color.dump.function}"), todisplay._format("text.dump.default_format", "%s", func), todisplay._translate("${reset}")) else io.write(todisplay._print_function(func)) end end -- print scalar value function dump._print_scalar(value, as_key) if type(value) == "string" then dump._print_string(value, as_key) elseif type(value) == "function" then dump._print_function(value, as_key) else io.write(todisplay._print_scalar(value)) end end -- print anchor function dump._print_anchor(printed_set_value) io.write(todisplay._translate("${color.dump.anchor}"), todisplay._format("text.dump.anchor", "&%s", printed_set_value.id), todisplay._translate("${reset}")) end -- print reference function dump._print_reference(printed_set_value) io.write(todisplay._translate("${color.dump.reference}"), todisplay._format("text.dump.reference", "*%s", printed_set_value.id), todisplay._translate("${reset}")) end -- print anchor and store to printed_set function dump._print_table_anchor(value, printed_set) io.write(" ") if printed_set[value].id then dump._print_anchor(printed_set[value]) printed_set.refs[value] = printed_set[value] end end -- print metatable of value function dump._print_metatable(value, metatable, inner_indent, printed_set, print_archor) if not metatable then return false end -- print metamethods local has_record = false local has_index_table = false for k, v in pairs(metatable) do if k == "__index" and type(v) == "table" then has_index_table = true elseif k:startswith("__") then if not has_record then has_record = true if print_archor then dump._print_table_anchor(value, printed_set) end end io.write("\n", inner_indent) local funcname = k:sub(3) dump._print_keyword(funcname) io.write(todisplay._translate("${reset} ${dim}=${reset} ")) if funcname == "tostring" or funcname == "len" or funcname == "todisplay" then local ok, result = pcall(v, value, value) if ok then if funcname == "todisplay" and type(result) == "string" then io.write(todisplay._translate(result)) else dump._print_scalar(result) end io.write(todisplay._translate("${dim} (evaluated)${reset}")) else dump._print_scalar(v) end elseif v and printed_set.refs[v] then dump._print_reference(printed_set.refs[v]) else dump._print_scalar(v) end io.write(",") end end if not has_index_table then return has_record end -- print index methods local index_table = metatable and rawget(metatable, "__index") for k, v in pairs(index_table) do -- hide private interfaces if type(k) ~= "string" or not k:startswith("_") then if not has_record then has_record = true if print_archor then dump._print_table_anchor(value, printed_set) end end io.write("\n", inner_indent) dump._print_keyword("(") dump._print_scalar(k, true) dump._print_keyword(")") io.write(todisplay._translate("${reset} ${dim}=${reset} ")) if v and printed_set.refs[v] then dump._print_reference(printed_set.refs[v]) else dump._print_scalar(v) end io.write(",") end end return has_record end -- init printed_set function dump._init_printed_set(printed_set, value) assert(type(value) == "table") for k, v in pairs(value) do if type(v) == "table" then -- has reference? v -> printed_set[v].obj if printed_set[v] then local obj = printed_set[v].obj if not printed_set[obj].id then printed_set.id = printed_set.id + 1 printed_set[obj].id = printed_set.id end else printed_set[v] = {obj = v, name = k} dump._init_printed_set(printed_set, v) end end end end -- returns printed_set, is_first_level function dump._get_printed_set(printed_set, value) local first_level = not printed_set if type(printed_set) ~= "table" then printed_set = {id = 0, refs = {}} if type(value) == "table" then printed_set[value] = {obj = value} dump._init_printed_set(printed_set, value) end end return printed_set, first_level end -- print udata function dump._print_udata(value, first_indent, remain_indent, printed_set) local first_level local metatable = debug.getmetatable(value) printed_set, first_level = dump._get_printed_set(printed_set, metatable) io.write(first_indent) if not first_level then io.write(todisplay._print_udata_scalar(value)) end local inner_indent = remain_indent .. " " -- print open brackets io.write(todisplay._translate("${reset}${color.dump.udata}[${reset}")) -- print metatable local no_value = not dump._print_metatable(value, metatable, inner_indent, printed_set, false) -- print close brackets if no_value then io.write(todisplay._translate(" ${color.dump.udata}]${reset}")) else io.write("\b \n", remain_indent, todisplay._translate("${reset}${color.dump.udata}]${reset}")) end end -- print table function dump._print_table(value, first_indent, remain_indent, printed_set) local first_level printed_set, first_level = dump._get_printed_set(printed_set, value) io.write(first_indent) local metatable = debug.getmetatable(value) local tostringmethod = metatable and (rawget(metatable, "__todisplay") or rawget(metatable, "__tostring")) if not first_level and tostringmethod then return dump._print_scalar(value) end local inner_indent = remain_indent .. " " local first_value = true -- print open brackets io.write(todisplay._translate("${reset}${color.dump.table}{${reset}")) local function print_newline() if first_value then dump._print_table_anchor(value, printed_set) first_value = false end io.write("\n", inner_indent) end -- print metatable if first_level then first_value = not dump._print_metatable(value, metatable, inner_indent, printed_set, true) end -- print array items local is_arr = (value[1] ~= nil) and (table.maxn(value) < 2 * #value) if is_arr then for i = 1, table.maxn(value) do print_newline() local v = value[i] if type(v) == "table" then if printed_set.refs[v] then dump._print_reference(printed_set.refs[v]) else dump._print_table(v, "", inner_indent, printed_set) end else dump._print_scalar(v) end io.write(",") end end -- print data for k, v in pairs(value) do if not is_arr or type(k) ~= "number" then print_newline() dump._print_scalar(k, true) io.write(todisplay._translate("${reset} ${dim}=${reset} ")) if type(v) == "table" then if printed_set.refs[v] then dump._print_reference(printed_set.refs[v]) else dump._print_table(v, "", inner_indent, printed_set) end else dump._print_scalar(v) end io.write(",") end end -- print close brackets if first_value then io.write(todisplay._translate(" ${color.dump.table}}${reset}")) else io.write("\b \n", remain_indent, todisplay._translate("${reset}${color.dump.table}}${reset}")) end end -- print value function dump._print(value, indent, verbose) indent = tostring(indent or "") if type(value) == "table" then dump._print_table(value, indent, indent:gsub(".", " "), not verbose) elseif type(value) == "userdata" then dump._print_udata(value, indent, indent:gsub(".", " "), not verbose) else io.write(indent) dump._print_scalar(value) end io.write("\n") end return dump._print