Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH] lua: lua_field_inspect_table without pushcfunction
@ 2020-05-18  9:37 Sergey Kaplun
  2020-05-18 21:52 ` Vladislav Shpilevoy
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Sergey Kaplun @ 2020-05-18  9:37 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Vladislav Shpilevoy

Currently on encoding table we push cfunction (lua_field_try_serialize)
to lua stack with additional lightuserdata and table value and after
pcall that function to avoid a raise of error.

In this case LuaJIT creates new object which will not live long time,
so it increase amount of dead object and also increase time and
frequency of garbage collection inside LuaJIT.
Also this pcall is necessary only in case when metafield __serialize
of serilizable object has LUA_TFUNCTION type.

So instead pushcfunction with pcall we can directly call the function
trying to serialize an object.
---

branch: https://github.com/tarantool/tarantool/tree/skaplun/no-ticket-lua-inspect-table-refactoring

 src/lua/utils.c | 132 ++++++++++++++++--------------------------------
 1 file changed, 44 insertions(+), 88 deletions(-)

diff --git a/src/lua/utils.c b/src/lua/utils.c
index d410a3d03..58715a55e 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -461,91 +461,69 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
 }
 
 /**
- * A helper structure to simplify safe call of __serialize method.
- * It passes some arguments into the serializer called via pcall,
- * and carries out some results.
+ * Call __serialize method of a table object by index if the former exists.
+ *
+ * If __serialize not exists function does nothing and the function returns 1;
+ *
+ * If __serialize exists, is correct and is a function then
+ * a result of serialization replaces old value by the index and
+ * the function returns 0;
+ *
+ * If the serialization hints like 'array' or 'map', then field->type,
+ * field->syze and field->compact sets if necessary
+ * and the function returns 0;
+ *
+ * Otherwise it is an error, set diag and the funciton returns -1;
+ *
+ * Return values:
+ * -1 - error, set diag
+ *  0 - has serialize, success replace and have to finish
+ *  1 - hasn't serialize, need to process
  */
-struct lua_serialize_status {
-	/**
-	 * True if an attempt to call __serialize has failed. A
-	 * diag message is set.
-	 */
-	bool is_error;
-	/**
-	 * True, if __serialize exists. Otherwise an ordinary
-	 * default serialization is used.
-	 */
-	bool is_serialize_used;
-	/**
-	 * True, if __serialize not only exists, but also returned
-	 * a new value which should replace the original one. On
-	 * the contrary __serialize could be a string like 'array'
-	 * or 'map' and do not push anything but rather say how to
-	 * interpret the target table. In such a case there is
-	 * nothing to replace with.
-	 */
-	bool is_value_returned;
-	/** Parameters, passed originally to luaL_tofield. */
-	struct luaL_serializer *cfg;
-	struct luaL_field *field;
-};
 
-/**
- * Call __serialize method of a table object if the former exists.
- * The function expects 2 values pushed onto the Lua stack: a
- * value to serialize, and a pointer at a struct
- * lua_serialize_status object. If __serialize exists, is correct,
- * and is a function then one value is pushed as a result of
- * serialization. If it is correct, but just a serialization hint
- * like 'array' or 'map', then nothing is pushed. Otherwise it is
- * an error. All the described outcomes can be distinguished via
- * lua_serialize_status attributes.
- */
 static int
-lua_field_try_serialize(struct lua_State *L)
+lua_field_try_serialize(struct lua_State *L, int idx,
+			struct luaL_serializer *cfg, struct luaL_field *field)
 {
-	struct lua_serialize_status *s =
-		(struct lua_serialize_status *) lua_touserdata(L, 2);
-	s->is_serialize_used = (luaL_getmetafield(L, 1, LUAL_SERIALIZE) != 0);
-	s->is_error = false;
-	s->is_value_returned = false;
-	if (! s->is_serialize_used)
-		return 0;
-	struct luaL_serializer *cfg = s->cfg;
-	struct luaL_field *field = s->field;
+	bool is_serialize_used = (luaL_getmetafield(L, idx,
+						    LUAL_SERIALIZE) != 0);
+	if (!is_serialize_used)
+		return 1;
 	if (lua_isfunction(L, -1)) {
 		/* copy object itself */
-		lua_pushvalue(L, 1);
-		lua_call(L, 1, 1);
-		s->is_error = (luaL_tofield(L, cfg, NULL, -1, field) != 0);
-		s->is_value_returned = true;
-		return 1;
+		lua_pushvalue(L, idx);
+		if (lua_pcall(L, 1, 1, 0) != 0) {
+			diag_set(LuajitError, lua_tostring(L, -1));
+			return -1;
+		}
+		if (luaL_tofield(L, cfg, NULL, -1, field) != 0)
+			return -1;
+		lua_replace(L, idx);
+		return 0;
 	}
 	if (!lua_isstring(L, -1)) {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		s->is_error = true;
-		lua_pop(L, 1);
-		return 0;
+		return -1;
 	}
 	const char *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, 1);
+		field->size = luaL_arrlen(L, idx);
 		/* YAML: use flow mode if __serialize == 'seq' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 	} else if (strcmp(type, "map") == 0 || strcmp(type, "mapping") == 0) {
 		field->type = MP_MAP;   /* Override type */
-		field->size = luaL_maplen(L, 1);
+		field->size = luaL_maplen(L, idx);
 		/* YAML: use flow mode if __serialize == 'map' */
 		if (cfg->has_compact && type[3] == '\0')
 			field->compact = true;
 	} else {
 		diag_set(LuajitError, "invalid " LUAL_SERIALIZE " value");
-		s->is_error = true;
+		return -1;
 	}
-	lua_pop(L, 1);
+	lua_pop(L, 1); /* remove value was setted by luaL_getmetafield */
 	return 0;
 }
 
@@ -559,36 +537,14 @@ lua_field_inspect_table(struct lua_State *L, struct luaL_serializer *cfg,
 
 	if (cfg->encode_load_metatables) {
 		int top = lua_gettop(L);
-		struct lua_serialize_status s;
-		s.cfg = cfg;
-		s.field = field;
-		lua_pushcfunction(L, lua_field_try_serialize);
-		lua_pushvalue(L, idx);
-		lua_pushlightuserdata(L, &s);
-		if (lua_pcall(L, 2, 1, 0) != 0) {
-			diag_set(LuajitError, lua_tostring(L, -1));
-			return -1;
-		}
-		if (s.is_error)
+		int res = lua_field_try_serialize(L, idx, cfg, field);
+		if (res == -1)
 			return -1;
-		/*
-		 * lua_call/pcall always returns the specified
-		 * number of values. Even if the function returned
-		 * less - others are filled with nils. So when a
-		 * nil is not needed, it should be popped
-		 * manually.
-		 */
-		assert(lua_gettop(L) == top + 1);
-		(void) top;
-		if (s.is_serialize_used) {
-			if (s.is_value_returned)
-				lua_replace(L, idx);
-			else
-				lua_pop(L, 1);
+		assert(lua_gettop(L) == top);
+		(void)top;
+		if (res == 0)
 			return 0;
-		}
-		assert(! s.is_value_returned);
-		lua_pop(L, 1);
+		/* Fall throuth with res == 1 */
 	}
 
 	field->type = MP_ARRAY;
-- 
2.24.1

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-06-10 13:39 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-18  9:37 [Tarantool-patches] [PATCH] lua: lua_field_inspect_table without pushcfunction Sergey Kaplun
2020-05-18 21:52 ` Vladislav Shpilevoy
2020-05-19 10:29   ` Sergey Kaplun
2020-05-19 20:08     ` Vladislav Shpilevoy
2020-05-22 11:48 ` Aleksandr Lyapunov
2020-05-22 14:05 ` Alexander Turenko
2020-05-25 12:06   ` Sergey Kaplun
2020-06-02  0:19 ` Igor Munkin
2020-06-02  9:36   ` Igor Munkin
2020-06-10 12:15 ` Kirill Yukhin
2020-06-10 13:01   ` Igor Munkin
2020-06-10 13:39     ` Kirill Yukhin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox