Anomie has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/84985


Change subject: Handle __pairs when marshalling Lua-to-PHP
......................................................................

Handle __pairs when marshalling Lua-to-PHP

Tables that have a custom __pairs method are not being PHPized properly,
it's effectively using "rawpairs". This means that, for example, passing
a table from mw.loadData to PHP doesn't work right.

The fix is to use the __pairs function if it exists in the metatable. We
could, in fact, just use 'pairs' itself in all cases, but it's a lot
clearer to just use lua_next for the common case where there is no
__pairs.

Change-Id: I8cd51efc624554db6d375562aaa9421559603fae
---
M data_conversion.c
1 file changed, 75 insertions(+), 27 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/php/luasandbox 
refs/changes/85/84985/1

diff --git a/data_conversion.c b/data_conversion.c
index be7af4e..3150b1c 100644
--- a/data_conversion.c
+++ b/data_conversion.c
@@ -15,6 +15,8 @@
 
 static int luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
        zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
+static int luasandbox_lua_pair_to_array(HashTable *ht, lua_State *L,
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
 static int luasandbox_free_zval_userdata(lua_State * L);
 static int luasandbox_push_hashtable(lua_State * L, HashTable * ht);
 static int luasandbox_has_error_marker(lua_State * L, int index, void * 
marker);
@@ -380,10 +382,6 @@
 static int luasandbox_lua_to_array(HashTable *ht, lua_State *L, int index,
        zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
 {
-       const char * str;
-       size_t length;
-       zval *value;
-       lua_Number n;
        int top = lua_gettop(L);
 
        // Normalise the input index so that we can push without invalidating 
it.
@@ -391,37 +389,87 @@
                index += top + 1;
        }
 
-       lua_pushnil(L);
-       while (lua_next(L, index) != 0) {
-               MAKE_STD_ZVAL(value);
-               if (!luasandbox_lua_to_zval(value, L, -1, sandbox_zval, 
recursionGuard TSRMLS_CC)) {
-                       // Conversion failed, fix stack and bail
-                       lua_settop(L, top);
-                       return 0;
-               }
+       // If the input table has a __pairs function, we need to use that 
instead
+       // of lua_next.
+       if (luaL_getmetafield(L, index, "__pairs")) {
+               lua_pushvalue(L, index);
+               lua_call(L, 1, 3);
+               while (1) {
+                       // We need to copy static-data and func so we can reuse 
them for
+                       // each iteration.
+                       lua_pushvalue(L, -3);
+                       lua_insert(L, -2);
+                       lua_pushvalue(L, -3);
+                       lua_insert(L, -2);
 
-               if (lua_type(L, -2) == LUA_TNUMBER) {
-                       n = lua_tonumber(L, -2);
-                       if (n == floor(n)) {
-                               // Integer key
-                               zend_hash_index_update(ht, n, (void*)&value, 
sizeof(zval*), NULL);
-                               lua_settop(L, top + 1);
-                               continue;
+                       // Call the custom 'next' function from __pairs
+                       lua_call(L, 2, 2);
+
+                       if (lua_isnil(L, -2)) {
+                               // Nil key == end. Cleanup stack and exit loop.
+                               lua_settop(L, top);
+                               break;
+                       }
+                       if (!luasandbox_lua_pair_to_array(ht, L, sandbox_zval, 
recursionGuard TSRMLS_CC)) {
+                               // Failed to convert value. Cleanup stack and 
return failure.
+                               lua_settop(L, top);
+                               return 0;
                        }
                }
-
-               // Make a copy of the key so that we can call lua_tolstring() 
which is destructive
-               lua_pushvalue(L, -2);
-               str = lua_tolstring(L, -1, &length);
-               zend_hash_update(ht, str, length + 1, (void*)&value, 
sizeof(zval*), NULL);
-
-               // Pop temporary values off the stack
-               lua_settop(L, top + 1);
+       } else {
+               // No __pairs, we can use lua_next
+               lua_pushnil(L);
+               while (lua_next(L, index) != 0) {
+                       if (!luasandbox_lua_pair_to_array(ht, L, sandbox_zval, 
recursionGuard TSRMLS_CC)) {
+                               // Failed to convert value. Cleanup stack and 
return failure.
+                               lua_settop(L, top);
+                               return 0;
+                       }
+               }
        }
        return 1;
 }
 /* }}} */
 
+/** {{{ luasandbox_lua_pair_to_array
+ *
+ * Take the lua key-value pair at the top of the Lua stack and add it to the 
given HashTable.
+ * On success the value is popped, but the key remains on the stack.
+ */
+static int luasandbox_lua_pair_to_array(HashTable *ht, lua_State *L,
+       zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC)
+{
+       const char * str;
+       size_t length;
+       zval *value;
+       lua_Number n;
+
+       // Convert value, then remove it
+       MAKE_STD_ZVAL(value);
+       if (!luasandbox_lua_to_zval(value, L, -1, sandbox_zval, recursionGuard 
TSRMLS_CC)) {
+               return 0;
+       }
+       lua_pop(L, 1);
+
+       // Convert key, but leave it there
+       if (lua_type(L, -1) == LUA_TNUMBER) {
+               n = lua_tonumber(L, -1);
+               if (n == floor(n)) {
+                       // Integer key
+                       zend_hash_index_update(ht, n, (void*)&value, 
sizeof(zval*), NULL);
+                       return 1;
+               }
+       }
+
+       // Make a copy of the key so that we can call lua_tolstring() which is 
destructive
+       lua_pushvalue(L, -1);
+       str = lua_tolstring(L, -1, &length);
+       zend_hash_update(ht, str, length + 1, (void*)&value, sizeof(zval*), 
NULL);
+       lua_pop(L, 1);
+       return 1;
+}
+/* }}} */
+
 /** {{{ luasandbox_wrap_fatal
  *
  * Pop a value off the top of the stack, and push a fatal error wrapper 

-- 
To view, visit https://gerrit.wikimedia.org/r/84985
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8cd51efc624554db6d375562aaa9421559603fae
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/php/luasandbox
Gerrit-Branch: master
Gerrit-Owner: Anomie <bjor...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to