void luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg, struct mpstream *stream, int index) { /* Performs keyfy() logic */ struct tuple *tuple = luaT_istuple(L, index); if (tuple != NULL) return tuple_to_mpstream(tuple, stream); struct luaL_field field; if (luaL_tofield(L, cfg, index, &field) < 0) luaT_error(L); if (field.type == MP_ARRAY) { lua_pushvalue(L, index); luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); } else if (field.type == MP_NIL) { mpstream_encode_array(stream, 0); } else { mpstream_encode_array(stream, 1); lua_pushvalue(L, index); luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); } }
static void lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg, int idx, struct luaL_field *field) { if (!cfg->encode_load_metatables) return; /* * Try to call LUAL_SERIALIZE method on udata/cdata * LuaJIT specific: lua_getfield/lua_gettable raises exception on * cdata if field doesn't exist. */ int top = lua_gettop(L); lua_pushcfunction(L, lua_gettable_wrapper); lua_pushvalue(L, idx); lua_pushliteral(L, LUAL_SERIALIZE); if (lua_pcall(L, 2, 1, 0) == 0 && !lua_isnil(L, -1)) { if (!lua_isfunction(L, -1)) luaL_error(L, "invalid " LUAL_SERIALIZE " value"); /* copy object itself */ lua_pushvalue(L, idx); lua_pcall(L, 1, 1, 0); /* replace obj with the unpacked value */ lua_replace(L, idx); luaL_tofield(L, cfg, idx, field); } /* else ignore lua_gettable exceptions */ lua_settop(L, top); /* remove temporary objects */ }
static void lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx, struct luaL_field *field) { int top = lua_gettop(L); lua_getglobal(L, "tostring"); lua_pushvalue(L, idx); lua_call(L, 1, 1); lua_replace(L, idx); lua_settop(L, top); luaL_tofield(L, cfg, idx, field); }
/* * Encode CALL result. * Please read gh-291 carefully before "fixing" this code. */ static inline uint32_t luamp_encode_call(lua_State *L, struct luaL_serializer *cfg, struct mpstream *stream) { int nrets = lua_gettop(L); if (nrets == 0) { return 0; } else if (nrets > 1) { /* * Multireturn: * `return 1, box.tuple.new(...), array, 3, ...` */ for (int i = 1; i <= nrets; ++i) { struct luaL_field field; luaL_tofield(L, cfg, i, &field); struct tuple *tuple; if (field.type == MP_EXT && (tuple = lua_istuple(L, i)) != NULL) { /* `return ..., box.tuple.new(...), ...` */ tuple_to_mpstream(tuple, stream); } else if (field.type != MP_ARRAY) { /* * `return ..., scalar, ... => * ..., { scalar }, ...` */ lua_pushvalue(L, i); luamp_encode_array(cfg, stream, 1); luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); } else { /* `return ..., array, ...` */ luamp_encode(L, cfg, stream, i); } } return nrets; } assert(nrets == 1); /* * Inspect the first result */ struct luaL_field root; luaL_tofield(L, cfg, 1, &root); struct tuple *tuple; if (root.type == MP_EXT && (tuple = lua_istuple(L, 1)) != NULL) { /* `return box.tuple()` */ tuple_to_mpstream(tuple, stream); return 1; } else if (root.type != MP_ARRAY) { /* * `return scalar` * `return map` */ luamp_encode_array(cfg, stream, 1); assert(lua_gettop(L) == 1); luamp_encode_r(L, cfg, stream, &root, 0); return 1; } assert(root.type == MP_ARRAY); if (root.size == 0) { /* `return {}` => `{ box.tuple() }` */ luamp_encode_array(cfg, stream, 0); return 1; } /* `return { tuple, scalar, tuple }` */ assert(root.type == MP_ARRAY && root.size > 0); for (uint32_t t = 1; t <= root.size; t++) { lua_rawgeti(L, 1, t); struct luaL_field field; luaL_tofield(L, cfg, -1, &field); if (field.type == MP_EXT && (tuple = lua_istuple(L, -1))) { tuple_to_mpstream(tuple, stream); } else if (field.type != MP_ARRAY) { /* The first member of root table is not tuple/array */ if (t == 1) { /* * `return { scalar, ... } => * box.tuple.new(scalar, ...)` */ luamp_encode_array(cfg, stream, root.size); /* * Encode the first field of tuple using * existing information from luaL_tofield */ luamp_encode_r(L, cfg, stream, &field, 0); lua_pop(L, 1); assert(lua_gettop(L) == 1); /* Encode remaining fields as usual */ for (uint32_t f = 2; f <= root.size; f++) { lua_rawgeti(L, 1, f); luamp_encode(L, cfg, stream, -1); lua_pop(L, 1); } return 1; } /* * `return { tuple/array, ..., scalar, ... } => * { tuple/array, ..., { scalar }, ... }` */ luamp_encode_array(cfg, stream, 1); luamp_encode_r(L, cfg, stream, &field, 0); } else { /* `return { tuple/array, ..., tuple/array, ... }` */ luamp_encode_r(L, cfg, stream, &field, 0); } lua_pop(L, 1); assert(lua_gettop(L) == 1); } return root.size; }
static void lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg, int idx, struct luaL_field *field) { assert(lua_type(L, idx) == LUA_TTABLE); const char *type; uint32_t size = 0; uint32_t max = 0; /* Try to get field LUAL_SERIALIZER_TYPE from metatable */ if (!cfg->encode_load_metatables || !luaL_getmetafield(L, idx, LUAL_SERIALIZE)) goto skip; if (lua_isfunction(L, -1)) { /* copy object itself */ lua_pushvalue(L, idx); lua_call(L, 1, 1); /* replace obj with the unpacked value */ lua_replace(L, idx); luaL_tofield(L, cfg, idx, field); return; } else if (!lua_isstring(L, -1)) { luaL_error(L, "invalid " LUAL_SERIALIZE " value"); } type = lua_tostring(L, -1); if (strcmp(type, "array") == 0 || strcmp(type, "seq") == 0 || strcmp(type, "sequence") == 0) { field->type = MP_ARRAY; /* Override type */ field->size = luaL_arrlen(L, idx); /* YAML: use flow mode if __serialize == 'seq' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ return; } else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) { field->type = MP_MAP; /* Override type */ field->size = luaL_maplen(L, idx); /* YAML: use flow mode if __serialize == 'map' */ if (cfg->has_compact && type[3] == '\0') field->compact = true; lua_pop(L, 1); /* type */ return; } else { luaL_error(L, "invalid " LUAL_SERIALIZE " value"); } skip: field->type = MP_ARRAY; /* Calculate size and check that table can represent an array */ lua_pushnil(L); while (lua_next(L, idx)) { size++; lua_pop(L, 1); /* pop the value */ lua_Number k; if (lua_type(L, -1) != LUA_TNUMBER || ((k = lua_tonumber(L, -1)) != size && (k < 1 || floor(k) != k))) { /* Finish size calculation */ while (lua_next(L, idx)) { size++; lua_pop(L, 1); /* pop the value */ } field->type = MP_MAP; field->size = size; return; } if (k > max) max = k; } /* Encode excessively sparse arrays as objects (if enabled) */ if (cfg->encode_sparse_ratio > 0 && max > size * (uint32_t)cfg->encode_sparse_ratio && max > (uint32_t)cfg->encode_sparse_safe) { if (!cfg->encode_sparse_convert) luaL_error(L, "excessively sparse array"); field->type = MP_MAP; field->size = size; return; } assert(field->type == MP_ARRAY); field->size = max; }