Skip to content

Commit

Permalink
make __len and __index behavior consistent for arrays
Browse files Browse the repository at this point in the history
This allows encoding proxy arrays correctly (as in the included
test case), across all Lua versions.
  • Loading branch information
hishamhm committed Jun 6, 2024
1 parent 8b3546f commit 073735d
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 15 deletions.
46 changes: 31 additions & 15 deletions lua_cjson.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,6 @@
#define json_lightudata_mask(ludata) (ludata)
#endif

#if LUA_VERSION_NUM == 501
#define lua_geti(L, t, i) lua_rawgeti(L, (t), (i))
#endif

#if LUA_VERSION_NUM == 502
#define lua_objlen(L,i) lua_len(L, (i))
#elif LUA_VERSION_NUM > 502
Expand Down Expand Up @@ -669,9 +665,9 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
/* json_append_array args:
* - lua_State
* - JSON strbuf
* - Size of passwd Lua array (top of stack) */
* - Size of passed Lua array (top of stack) */
static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth,
strbuf_t *json, int array_length)
strbuf_t *json, int array_length, int raw)
{
int comma, i, json_pos, err;

Expand All @@ -683,7 +679,17 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept
if (comma++ > 0)
strbuf_append_char(json, ',');

lua_geti(l, -1, i);
if (raw) {
lua_rawgeti(l, -1, i);
} else {
#if LUA_VERSION_NUM == 501
lua_pushinteger(l, i);
lua_gettable(l, -2);
#else
lua_geti(l, -1, i);
#endif
}

err = json_append_data(l, cfg, current_depth, json);
if (err) {
strbuf_set_length(json, json_pos);
Expand Down Expand Up @@ -798,6 +804,7 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
int len;
int as_array = 0;
int has_metatable;
int raw = 1;

switch (lua_type(l, -1)) {
case LUA_TSTRING:
Expand All @@ -823,22 +830,29 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
lua_rawget(l, LUA_REGISTRYINDEX);
as_array = lua_rawequal(l, -1, -2);
if (as_array) {
raw = 1;
lua_pop(l, 2);
len = lua_objlen(l, -1);
} else {
lua_pop(l, 1);
as_array = (luaL_getmetafield(l, -1, "__len") != LUA_TNIL);
lua_pop(l, 1);
raw = 0;
lua_pop(l, 2);
if (luaL_getmetafield(l, -1, "__len")) {
lua_pushvalue(l, -2);
lua_call(l, 1, 1);
len = lua_tonumber(l, -1);
lua_pop(l, 1);
as_array = 1;
}
}
}

if (as_array) {
len = lua_objlen(l, -1);
json_append_array(l, cfg, current_depth, json, len);
json_append_array(l, cfg, current_depth, json, len, raw);
} else {
len = lua_array_length(l, cfg, json);

if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) {
json_append_array(l, cfg, current_depth, json, len);
json_append_array(l, cfg, current_depth, json, len, raw);
} else {
if (has_metatable) {
lua_getmetatable(l, -1);
Expand All @@ -848,7 +862,9 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
as_array = lua_rawequal(l, -1, -2);
lua_pop(l, 2); /* pop pointer + metatable */
if (as_array) {
json_append_array(l, cfg, current_depth, json, 0);
len = lua_objlen(l, -1);
raw = 1;
json_append_array(l, cfg, current_depth, json, len, raw);
break;
}
}
Expand All @@ -863,7 +879,7 @@ static int json_append_data(lua_State *l, json_config_t *cfg,
if (lua_touserdata(l, -1) == NULL) {
strbuf_append_mem(json, "null", 4);
} else if (lua_touserdata(l, -1) == json_lightudata_mask(&json_array)) {
json_append_array(l, cfg, current_depth, json, 0);
json_append_array(l, cfg, current_depth, json, 0, 1);
}
break;
default:
Expand Down
18 changes: 18 additions & 0 deletions tests/agentzh.t
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,21 @@ Cannot serialise function: type not supported

{"valid":"valid"}
["one","two","three"]



=== TEST 23: array-like proxy object with __len and __index
--- lua
local cjson = require "cjson"
local real_array = {"foo", "bar", "baz"}
local proxy_array = {}
setmetatable(proxy_array, {
__len = function() return 3 end,
__index = function(t, k)
return real_array[k]
end,
})

print(cjson.encode(proxy_array))
--- out
["foo","bar","baz"]

0 comments on commit 073735d

Please sign in to comment.