mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
fix(lua): handle array with holes in luaeval() (#26630)
This commit is contained in:
parent
d956bc6379
commit
7d279a09e0
@ -170,11 +170,12 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate)
|
|||||||
|
|
||||||
/// Helper structure for nlua_pop_typval
|
/// Helper structure for nlua_pop_typval
|
||||||
typedef struct {
|
typedef struct {
|
||||||
typval_T *tv; ///< Location where conversion result is saved.
|
typval_T *tv; ///< Location where conversion result is saved.
|
||||||
bool container; ///< True if tv is a container.
|
size_t list_len; ///< Maximum length when tv is a list.
|
||||||
bool special; ///< If true then tv is a _VAL part of special dictionary
|
bool container; ///< True if tv is a container.
|
||||||
///< that represents mapping.
|
bool special; ///< If true then tv is a _VAL part of special dictionary
|
||||||
int idx; ///< Container index (used to detect self-referencing structures).
|
///< that represents mapping.
|
||||||
|
int idx; ///< Container index (used to detect self-referencing structures).
|
||||||
} TVPopStackItem;
|
} TVPopStackItem;
|
||||||
|
|
||||||
/// Convert lua object to Vimscript typval_T
|
/// Convert lua object to Vimscript typval_T
|
||||||
@ -192,7 +193,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
const int initial_size = lua_gettop(lstate);
|
const int initial_size = lua_gettop(lstate);
|
||||||
kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE;
|
kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE;
|
||||||
kvi_init(stack);
|
kvi_init(stack);
|
||||||
kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 }));
|
kvi_push(stack, ((TVPopStackItem){ .tv = ret_tv }));
|
||||||
while (ret && kv_size(stack)) {
|
while (ret && kv_size(stack)) {
|
||||||
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
|
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
|
||||||
semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
|
semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
|
||||||
@ -231,19 +232,14 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
});
|
});
|
||||||
kvi_push(stack, cur);
|
kvi_push(stack, cur);
|
||||||
tv_list_append_list(cur.tv->vval.v_list, kv_pair);
|
tv_list_append_list(cur.tv->vval.v_list, kv_pair);
|
||||||
cur = (TVPopStackItem) {
|
cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)) };
|
||||||
.tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)),
|
|
||||||
.container = false,
|
|
||||||
.special = false,
|
|
||||||
.idx = 0,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
dictitem_T *const di = tv_dict_item_alloc_len(s, len);
|
dictitem_T *const di = tv_dict_item_alloc_len(s, len);
|
||||||
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
|
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
kvi_push(stack, cur);
|
kvi_push(stack, cur);
|
||||||
cur = (TVPopStackItem) { &di->di_tv, false, false, 0 };
|
cur = (TVPopStackItem){ .tv = &di->di_tv };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lua_pop(lstate, 1);
|
lua_pop(lstate, 1);
|
||||||
@ -251,23 +247,18 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(cur.tv->v_type == VAR_LIST);
|
assert(cur.tv->v_type == VAR_LIST);
|
||||||
lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1);
|
if ((size_t)tv_list_len(cur.tv->vval.v_list) == cur.list_len) {
|
||||||
if (lua_isnil(lstate, -1)) {
|
lua_pop(lstate, 1);
|
||||||
lua_pop(lstate, 2);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1);
|
||||||
// Not populated yet, need to create list item to push.
|
// Not populated yet, need to create list item to push.
|
||||||
tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) {
|
tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) {
|
||||||
.v_type = VAR_UNKNOWN,
|
.v_type = VAR_UNKNOWN,
|
||||||
});
|
});
|
||||||
kvi_push(stack, cur);
|
kvi_push(stack, cur);
|
||||||
// TODO(ZyX-I): Use indexes, here list item *will* be reallocated.
|
// TODO(ZyX-I): Use indexes, here list item *will* be reallocated.
|
||||||
cur = (TVPopStackItem) {
|
cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)) };
|
||||||
.tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)),
|
|
||||||
.container = false,
|
|
||||||
.special = false,
|
|
||||||
.idx = 0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(!cur.container);
|
assert(!cur.container);
|
||||||
@ -331,6 +322,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
|
cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
|
||||||
cur.tv->vval.v_list->lua_table_ref = table_ref;
|
cur.tv->vval.v_list->lua_table_ref = table_ref;
|
||||||
tv_list_ref(cur.tv->vval.v_list);
|
tv_list_ref(cur.tv->vval.v_list);
|
||||||
|
cur.list_len = table_props.maxidx;
|
||||||
if (table_props.maxidx != 0) {
|
if (table_props.maxidx != 0) {
|
||||||
cur.container = true;
|
cur.container = true;
|
||||||
cur.idx = lua_gettop(lstate);
|
cur.idx = lua_gettop(lstate);
|
||||||
@ -354,6 +346,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
cur.tv = &val_di->di_tv;
|
cur.tv = &val_di->di_tv;
|
||||||
cur.tv->vval.v_list->lua_table_ref = table_ref;
|
cur.tv->vval.v_list->lua_table_ref = table_ref;
|
||||||
assert(cur.tv->v_type == VAR_LIST);
|
assert(cur.tv->v_type == VAR_LIST);
|
||||||
|
cur.list_len = table_props.string_keys_num;
|
||||||
} else {
|
} else {
|
||||||
cur.tv->v_type = VAR_DICT;
|
cur.tv->v_type = VAR_DICT;
|
||||||
cur.tv->vval.v_dict = tv_dict_alloc();
|
cur.tv->vval.v_dict = tv_dict_alloc();
|
||||||
@ -371,9 +364,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||||||
cur.tv->vval.v_float = (float_T)table_props.val;
|
cur.tv->vval.v_float = (float_T)table_props.val;
|
||||||
break;
|
break;
|
||||||
case kObjectTypeNil:
|
case kObjectTypeNil:
|
||||||
emsg(_("E5100: Cannot convert given lua table: table "
|
emsg(_("E5100: Cannot convert given lua table: table should "
|
||||||
"should either have a sequence of positive integer keys "
|
"contain either only integer keys or only string keys"));
|
||||||
"or contain only string keys"));
|
|
||||||
ret = false;
|
ret = false;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1077,7 +1069,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
|||||||
const int initial_size = lua_gettop(lstate);
|
const int initial_size = lua_gettop(lstate);
|
||||||
kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE;
|
kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE;
|
||||||
kvi_init(stack);
|
kvi_init(stack);
|
||||||
kvi_push(stack, ((ObjPopStackItem) { &ret, false }));
|
kvi_push(stack, ((ObjPopStackItem){ .obj = &ret }));
|
||||||
while (!ERROR_SET(err) && kv_size(stack)) {
|
while (!ERROR_SET(err) && kv_size(stack)) {
|
||||||
ObjPopStackItem cur = kv_pop(stack);
|
ObjPopStackItem cur = kv_pop(stack);
|
||||||
if (cur.container) {
|
if (cur.container) {
|
||||||
@ -1087,8 +1079,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
|||||||
}
|
}
|
||||||
if (cur.obj->type == kObjectTypeDictionary) {
|
if (cur.obj->type == kObjectTypeDictionary) {
|
||||||
// stack: …, dict, key
|
// stack: …, dict, key
|
||||||
if (cur.obj->data.dictionary.size
|
if (cur.obj->data.dictionary.size == cur.obj->data.dictionary.capacity) {
|
||||||
== cur.obj->data.dictionary.capacity) {
|
|
||||||
lua_pop(lstate, 2);
|
lua_pop(lstate, 2);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1112,10 +1103,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
|||||||
.size = len,
|
.size = len,
|
||||||
};
|
};
|
||||||
kvi_push(stack, cur);
|
kvi_push(stack, cur);
|
||||||
cur = (ObjPopStackItem) {
|
cur = (ObjPopStackItem){ .obj = &cur.obj->data.dictionary.items[idx].value };
|
||||||
.obj = &cur.obj->data.dictionary.items[idx].value,
|
|
||||||
.container = false,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
// stack: …, dict
|
// stack: …, dict
|
||||||
lua_pop(lstate, 1);
|
lua_pop(lstate, 1);
|
||||||
@ -1130,10 +1118,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
|||||||
const size_t idx = cur.obj->data.array.size++;
|
const size_t idx = cur.obj->data.array.size++;
|
||||||
lua_rawgeti(lstate, -1, (int)idx + 1);
|
lua_rawgeti(lstate, -1, (int)idx + 1);
|
||||||
kvi_push(stack, cur);
|
kvi_push(stack, cur);
|
||||||
cur = (ObjPopStackItem) {
|
cur = (ObjPopStackItem){ .obj = &cur.obj->data.array.items[idx] };
|
||||||
.obj = &cur.obj->data.array.items[idx],
|
|
||||||
.container = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(!cur.container);
|
assert(!cur.container);
|
||||||
|
@ -69,15 +69,13 @@ describe('luaeval()', function()
|
|||||||
command([[let s = luaeval('"\0"')]])
|
command([[let s = luaeval('"\0"')]])
|
||||||
eq('\000', meths.get_var('s'))
|
eq('\000', meths.get_var('s'))
|
||||||
end)
|
end)
|
||||||
it('are successfully converted to special dictionaries in table keys',
|
it('are successfully converted to special dictionaries in table keys', function()
|
||||||
function()
|
|
||||||
command([[let d = luaeval('{["\0"]=1}')]])
|
command([[let d = luaeval('{["\0"]=1}')]])
|
||||||
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n'}}, 1}}}, meths.get_var('d'))
|
eq({_TYPE={}, _VAL={{{_TYPE={}, _VAL={'\n'}}, 1}}}, meths.get_var('d'))
|
||||||
eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
|
eq(1, funcs.eval('d._TYPE is v:msgpack_types.map'))
|
||||||
eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
eq(1, funcs.eval('d._VAL[0][0]._TYPE is v:msgpack_types.string'))
|
||||||
end)
|
end)
|
||||||
it('are successfully converted to blobs from a list',
|
it('are successfully converted to blobs from a list', function()
|
||||||
function()
|
|
||||||
command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
|
command([[let l = luaeval('{"abc", "a\0b", "c\0d", "def"}')]])
|
||||||
eq({'abc', 'a\000b', 'c\000d', 'def'}, meths.get_var('l'))
|
eq({'abc', 'a\000b', 'c\000d', 'def'}, meths.get_var('l'))
|
||||||
end)
|
end)
|
||||||
@ -117,6 +115,12 @@ describe('luaeval()', function()
|
|||||||
eq({4, 2}, funcs.luaeval('{4, 2}'))
|
eq({4, 2}, funcs.luaeval('{4, 2}'))
|
||||||
eq(3, eval('type(luaeval("{4, 2}"))'))
|
eq(3, eval('type(luaeval("{4, 2}"))'))
|
||||||
|
|
||||||
|
eq({NIL, 20}, funcs.luaeval('{[2] = 20}'))
|
||||||
|
eq(3, eval('type(luaeval("{[2] = 20}"))'))
|
||||||
|
|
||||||
|
eq({10, NIL, 30}, funcs.luaeval('{[1] = 10, [3] = 30}'))
|
||||||
|
eq(3, eval('type(luaeval("{[1] = 10, [3] = 30}"))'))
|
||||||
|
|
||||||
local level = 30
|
local level = 30
|
||||||
eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
|
eq(nested_by_level[level].o, funcs.luaeval(nested_by_level[level].s))
|
||||||
|
|
||||||
@ -182,7 +186,7 @@ describe('luaeval()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('issues an error in some cases', function()
|
it('issues an error in some cases', function()
|
||||||
eq("Vim(call):E5100: Cannot convert given lua table: table should either have a sequence of positive integer keys or contain only string keys",
|
eq("Vim(call):E5100: Cannot convert given lua table: table should contain either only integer keys or only string keys",
|
||||||
exc_exec('call luaeval("{1, foo=2}")'))
|
exc_exec('call luaeval("{1, foo=2}")'))
|
||||||
|
|
||||||
startswith("Vim(call):E5107: Error loading lua [string \"luaeval()\"]:",
|
startswith("Vim(call):E5107: Error loading lua [string \"luaeval()\"]:",
|
||||||
|
Loading…
Reference in New Issue
Block a user