mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
fix(lua): fix vim.deepcopy for metatables & cycled tables (#16435)
vim.deepcopy previously didn't retain metatables in copies and caused stackoverflow on recursive tables/cycled tables this fixes these issues
This commit is contained in:
parent
3451121a4e
commit
eb876a0a6f
@ -12,7 +12,7 @@ local vim = vim or {}
|
|||||||
--- same functions as those in the input table. Userdata and threads are not
|
--- same functions as those in the input table. Userdata and threads are not
|
||||||
--- copied and will throw an error.
|
--- copied and will throw an error.
|
||||||
---
|
---
|
||||||
---@param orig Table to copy
|
---@param orig table Table to copy
|
||||||
---@returns New table of copied keys and (nested) values.
|
---@returns New table of copied keys and (nested) values.
|
||||||
function vim.deepcopy(orig) end -- luacheck: no unused
|
function vim.deepcopy(orig) end -- luacheck: no unused
|
||||||
vim.deepcopy = (function()
|
vim.deepcopy = (function()
|
||||||
@ -21,17 +21,16 @@ vim.deepcopy = (function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local deepcopy_funcs = {
|
local deepcopy_funcs = {
|
||||||
table = function(orig)
|
table = function(orig, cache)
|
||||||
|
if cache[orig] then return cache[orig] end
|
||||||
local copy = {}
|
local copy = {}
|
||||||
|
|
||||||
if vim._empty_dict_mt ~= nil and getmetatable(orig) == vim._empty_dict_mt then
|
cache[orig] = copy
|
||||||
copy = vim.empty_dict()
|
local mt = getmetatable(orig)
|
||||||
end
|
|
||||||
|
|
||||||
for k, v in pairs(orig) do
|
for k, v in pairs(orig) do
|
||||||
copy[vim.deepcopy(k)] = vim.deepcopy(v)
|
copy[vim.deepcopy(k, cache)] = vim.deepcopy(v, cache)
|
||||||
end
|
end
|
||||||
return copy
|
return setmetatable(copy, mt)
|
||||||
end,
|
end,
|
||||||
number = _id,
|
number = _id,
|
||||||
string = _id,
|
string = _id,
|
||||||
@ -40,10 +39,10 @@ vim.deepcopy = (function()
|
|||||||
['function'] = _id,
|
['function'] = _id,
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(orig)
|
return function(orig, cache)
|
||||||
local f = deepcopy_funcs[type(orig)]
|
local f = deepcopy_funcs[type(orig)]
|
||||||
if f then
|
if f then
|
||||||
return f(orig)
|
return f(orig, cache or {})
|
||||||
else
|
else
|
||||||
error("Cannot deepcopy object of type "..type(orig))
|
error("Cannot deepcopy object of type "..type(orig))
|
||||||
end
|
end
|
||||||
|
@ -396,6 +396,20 @@ describe('lua stdlib', function()
|
|||||||
return t1.f() ~= t2.f()
|
return t1.f() ~= t2.f()
|
||||||
]]))
|
]]))
|
||||||
|
|
||||||
|
ok(exec_lua([[
|
||||||
|
local t1 = {a = 5}
|
||||||
|
t1.self = t1
|
||||||
|
local t2 = vim.deepcopy(t1)
|
||||||
|
return t2.self == t2 and t2.self ~= t1
|
||||||
|
]]))
|
||||||
|
|
||||||
|
ok(exec_lua([[
|
||||||
|
local mt = {mt=true}
|
||||||
|
local t1 = setmetatable({a = 5}, mt)
|
||||||
|
local t2 = vim.deepcopy(t1)
|
||||||
|
return getmetatable(t2) == mt
|
||||||
|
]]))
|
||||||
|
|
||||||
eq('Error executing lua: vim/shared.lua:0: Cannot deepcopy object of type thread',
|
eq('Error executing lua: vim/shared.lua:0: Cannot deepcopy object of type thread',
|
||||||
pcall_err(exec_lua, [[
|
pcall_err(exec_lua, [[
|
||||||
local thread = coroutine.create(function () return 0 end)
|
local thread = coroutine.create(function () return 0 end)
|
||||||
|
Loading…
Reference in New Issue
Block a user