From b16a01191925c9563133dd1b14f5182e2acf5702 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 24 Dec 2024 14:11:45 +0530 Subject: [PATCH] fix: deepcopy should copy same table exactly only once --- apisix/core/table.lua | 12 +++-- t/core/table.t | 112 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/apisix/core/table.lua b/apisix/core/table.lua index 4346863079cf..b69db8e80be0 100644 --- a/apisix/core/table.lua +++ b/apisix/core/table.lua @@ -118,16 +118,20 @@ end local deepcopy do local function _deepcopy(orig, copied) - -- prevent infinite loop when a field refers its parent - copied[orig] = true -- If the array-like table contains nil in the middle, -- the len might be smaller than the expected. -- But it doesn't affect the correctness. local len = #orig local copy = new_tab(len, nkeys(orig) - len) + -- prevent infinite loop when a field refers its parent + copied[orig] = copy for orig_key, orig_value in pairs(orig) do - if type(orig_value) == "table" and not copied[orig_value] then - copy[orig_key] = _deepcopy(orig_value, copied) + if type(orig_value) == "table" then + if copied[orig_value] then + copy[orig_key] = copied[orig_value] + else + copy[orig_key] = _deepcopy(orig_value, copied) + end else copy[orig_key] = orig_value end diff --git a/t/core/table.t b/t/core/table.t index c3ec5a7c0d1f..c5d93ecb198d 100644 --- a/t/core/table.t +++ b/t/core/table.t @@ -215,3 +215,115 @@ GET /t GET /t --- response_body ok + + + +=== TEST 8: deepcopy copy same table only once +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local tmp = { name = "tmp", priority = 1, enabled = true } + local origin = { a = { b = tmp }, c = tmp} + local copy = core.table.deepcopy(origin) + if not core.table.deep_eq(copy, origin) then + ngx.say("copy: ", json.encode(expect), ", origin: ", json.encode(actual)) + return + end + if copy.a.b ~= copy.c then + ngx.say("copy.a.b should be the same as copy.c") + return + end + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +ok + + + +=== TEST 9: reference same table +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local deepcopy = core.table.deepcopy + local tab1 = {name = "tab1"} + local tab2 = { + a = tab1, + b = tab1 + } + local tab_copied = deepcopy(tab2) + + ngx.say("table copied: ", require("toolkit.json").encode(tab_copied)) + + ngx.say("tab1 == tab2.a: ", tab1 == tab2.a) + ngx.say("tab2.a == tab2.b: ", tab2.a == tab2.b) + + ngx.say("tab_copied.a == tab1: ", tab_copied.a == tab1) + ngx.say("tab_copied.a == tab_copied.b: ", tab_copied.a == tab_copied.b) + } + } +--- request +GET /t +--- response_body +table copied: {"a":{"name":"tab1"},"b":{"name":"tab1"}} +tab1 == tab2.a: true +tab2.a == tab2.b: true +tab_copied.a == tab1: false +tab_copied.a == tab_copied.b: true + + + +=== TEST 10: reference table self(root node) +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local deepcopy = core.table.deepcopy + local tab1 = {name = "tab1"} + local tab2 = { + a = tab1, + } + tab2.c = tab2 + + local tab_copied = deepcopy(tab2) + + ngx.say("tab_copied.a == tab1: ", tab_copied.a == tab_copied.b) + ngx.say("tab_copied == tab_copied.c: ", tab_copied == tab_copied.c) + } + } +--- request +GET /t +--- response_body +tab_copied.a == tab1: false +tab_copied == tab_copied.c: true + + + +=== TEST 11: reference table self(sub node) +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local deepcopy = core.table.deepcopy + local tab_org = { + a = { + a2 = "a2" + }, + } + tab_org.b = tab_org.a + + local tab_copied = deepcopy(tab_org) + ngx.say("table copied: ", require("toolkit.json").encode(tab_copied)) + ngx.say("tab_copied.a == tab_copied.b: ", tab_copied.a == tab_copied.b) + } + } +--- request +GET /t +--- response_body +table copied: {"a":{"a2":"a2"},"b":{"a2":"a2"}} +tab_copied.a == tab_copied.b: true +