Skip to content

Commit

Permalink
lua-yaml: enable aliasing for objects returned by __serialize
Browse files Browse the repository at this point in the history
The YAML serializer fails to detect aliases in objects returned by
the __serialize method:

tarantool> x = {}
---
...

tarantool> {a = x, b = x}
---
- a: &0 []
  b: *0
...

tarantool> setmetatable({}, {
         >     __serialize = function() return {a = x, b = x} end,
         > })
---
- a: []
  b: []
...

Fix this by scanning the object returned by the __serialize method
(called by luaL_checkfield) for references.

Closes #8240

NO_DOC=bug fix
  • Loading branch information
locker committed Feb 15, 2023
1 parent 310de56 commit b42302f
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 3 deletions.
4 changes: 4 additions & 0 deletions changelogs/unreleased/gh-8240-yaml-alias-serialize-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## bugfix/lua

* Fixed alias detection in the YAML serializer in case the input contains
objects that implement the `__serialize` meta method (gh-8240).
9 changes: 6 additions & 3 deletions src/lua/serializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ lua_field_try_serialize(struct lua_State *L, struct luaL_serializer *cfg,
if (luaL_tofield(L, cfg, -1, field) != 0)
return -1;
lua_replace(L, idx);
field->serialized = true;
return 0;
}
if (!lua_isstring(L, -1)) {
Expand Down Expand Up @@ -433,6 +434,11 @@ int
luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
struct luaL_field *field)
{
field->type = MP_NIL;
field->ext_type = MP_UNKNOWN_EXTENSION;
field->compact = false;
field->serialized = false;

if (index < 0)
index = lua_gettop(L) + index + 1;

Expand Down Expand Up @@ -568,10 +574,7 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
field->type = MP_STR;
return 0;
case LUA_TTABLE:
{
field->compact = false;
return lua_field_inspect_table(L, cfg, index, field);
}
case LUA_TLIGHTUSERDATA:
case LUA_TUSERDATA:
field->sval.data = NULL;
Expand Down
2 changes: 2 additions & 0 deletions src/lua/serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ struct luaL_field {
/* subtypes of MP_EXT */
enum mp_extension_type ext_type;
bool compact; /* a flag used by YAML serializer */
/** Set if __serialize metamethod was called for this field. */
bool serialized;
};

/**
Expand Down
14 changes: 14 additions & 0 deletions test/app-luatest/gh_8240_yaml_alias_serialize_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@ g.test_objects_that_implement_serialize_are_aliased = function()
t.assert_is(nested2[1], nested2.a[1][1])
t.assert_is(nested2[1], nested2.a.b.c)
end

g.test_objects_returned_by_serialize_are_aliased = function()
local o1 = {}
local o2 = serialize(setmetatable({}, {
__serialize = function() return {o1, {a = o1}} end,
}))
local o3 = serialize(setmetatable({}, {
__serialize = function() return {o2, o2} end
}))
t.assert_equals(o2, {o1, {a = o1}})
t.assert_is(o2[1], o2[2].a)
t.assert_equals(o3, {{o1, {a = o1}}, {o1, {a = o1}}})
t.assert_is(o3[1], o3[2])
end
4 changes: 4 additions & 0 deletions third_party/lua-yaml/lyaml.cc
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,8 @@ static int yaml_is_flow_mode(struct lua_yaml_dumper *dumper) {
return 0;
}

static void find_references(struct lua_yaml_dumper *dumper);

static int dump_node(struct lua_yaml_dumper *dumper)
{
size_t len = 0;
Expand All @@ -635,6 +637,8 @@ static int dump_node(struct lua_yaml_dumper *dumper)

int top = lua_gettop(dumper->L);
luaL_checkfield(dumper->L, dumper->cfg, top, &field);
if (field.serialized)
find_references(dumper);
switch(field.type) {
case MP_UINT:
snprintf(buf, sizeof(buf) - 1, "%" PRIu64, field.ival);
Expand Down

0 comments on commit b42302f

Please sign in to comment.