https://www.mediawiki.org/wiki/Special:Code/MediaWiki/115020

Revision: 115020
Author:   tstarling
Date:     2012-04-24 10:39:43 +0000 (Tue, 24 Apr 2012)
Log Message:
-----------
Expose Lua backtraces via a new luaTrace property in LuaRuntimeError. The array 
format is identical to the one implemented in the standalone interpreter in 
da06273e.

Modified Paths:
--------------
    trunk/php/luasandbox/data_conversion.c
    trunk/php/luasandbox/luasandbox.c
    trunk/php/luasandbox/php_luasandbox.h

Modified: trunk/php/luasandbox/data_conversion.c
===================================================================
--- trunk/php/luasandbox/data_conversion.c      2012-04-24 00:10:27 UTC (rev 
115019)
+++ trunk/php/luasandbox/data_conversion.c      2012-04-24 10:39:43 UTC (rev 
115020)
@@ -17,6 +17,7 @@
        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);
 
 extern zend_class_entry *luasandboxfunction_ce;
 extern zend_class_entry *luasandboxplaceholder_ce;
@@ -27,6 +28,10 @@
  */
 int luasandbox_fatal_error_marker = 0;
 
+/**
+ * Same as luasandbox_fatal_error_marker but for trace errors
+ */
+int luasandbox_trace_error_marker = 0;
 
 /** {{{ luasandbox_data_conversion_init
  *
@@ -393,24 +398,32 @@
  */
 int luasandbox_is_fatal(lua_State * L, int index)
 {
+       return luasandbox_has_error_marker(L, index, 
&luasandbox_fatal_error_marker);
+}
+/* }}} */
+
+/** {{{ luasandbox_is_trace_error
+ */
+int luasandbox_is_trace_error(lua_State * L, int index)
+{
+       return luasandbox_has_error_marker(L, index, 
&luasandbox_trace_error_marker);
+}
+/* }}} */
+
+/** {{{
+ *
+ * Check if the error at the given stack index has a given marker userdata
+ */
+static int luasandbox_has_error_marker(lua_State * L, int index, void * marker)
+{
        void * ud;
-       int haveIndex2 = 0;
-
        if (!lua_istable(L, index)) {
                return 0;
        }
-
        lua_rawgeti(L, index, 1);
        ud = lua_touserdata(L, -1);
        lua_pop(L, 1);
-       if (ud != &luasandbox_fatal_error_marker) {
-               return 0;
-       }
-
-       lua_rawgeti(L, index, 2);
-       haveIndex2 = !lua_isnil(L, -1);
-       lua_pop(L, 1);
-       return haveIndex2;
+       return ud == marker;
 }
 /* }}} */
 
@@ -428,7 +441,10 @@
 const char * luasandbox_error_to_string(lua_State * L, int index)
 {
        const char * s;
-       if (luasandbox_is_fatal(L, index)) {
+       if (index < 0) {
+               index += lua_gettop(L) + 1;
+       }
+       if (luasandbox_is_fatal(L, index) || luasandbox_is_trace_error(L, 
index)) {
                lua_rawgeti(L, index, 2);
                s = lua_tostring(L, -1);
                lua_pop(L, 1);
@@ -443,3 +459,64 @@
 }
 /* }}} */
 
+/** {{{ luasandbox_attach_trace
+ *
+ * Error callback function for lua_pcall(): wrap the error value in a table 
that
+ * includes backtrace information.
+ */
+int luasandbox_attach_trace(lua_State * L)
+{
+       if (luasandbox_is_fatal(L, 1)) {
+               // Pass fatals through unaltered
+               return 1;
+       }
+
+       // Create the table and put the marker in it as element 1
+       lua_createtable(L, 0, 3);
+       lua_pushlightuserdata(L, &luasandbox_trace_error_marker);
+       lua_rawseti(L, -2, 1);
+       
+       // Swap the table with the input value, so that the value is on the top,
+       // then put the value in the table as element 2
+       lua_insert(L, -2);
+       lua_rawseti(L, -2, 2);
+
+       // Put the backtrace in element 3
+       luasandbox_push_structured_trace(L, 1);
+       lua_rawseti(L, -2, 3);
+       
+       return 1;
+}
+/* }}} */
+
+/** {{{ luasandbox_push_structured_trace
+ *
+ * Make a table representing the current backtrace and push it to the stack.
+ * "level" is the call stack level to start at, 1 for the current function.
+ */
+void luasandbox_push_structured_trace(lua_State * L, int level)
+{
+       lua_Debug ar;
+       lua_newtable(L);
+       int i;
+
+       for (i = level; lua_getstack(L, i, &ar); i++) {
+               lua_getinfo(L, "nSl", &ar);
+               lua_createtable(L, 0, 8);
+               lua_pushstring(L, ar.short_src);
+               lua_setfield(L, -2, "short_src");
+               lua_pushstring(L, ar.what);
+               lua_setfield(L, -2, "what");
+               lua_pushnumber(L, ar.currentline);
+               lua_setfield(L, -2, "currentline");
+               lua_pushstring(L, ar.name);
+               lua_setfield(L, -2, "name");
+               lua_pushstring(L, ar.namewhat);
+               lua_setfield(L, -2, "namewhat");
+               lua_pushnumber(L, ar.linedefined);
+               lua_setfield(L, -2, "linedefined");
+               lua_rawseti(L, -2, i - level + 1);
+       }
+}
+/* }}} */
+

Modified: trunk/php/luasandbox/luasandbox.c
===================================================================
--- trunk/php/luasandbox/luasandbox.c   2012-04-24 00:10:27 UTC (rev 115019)
+++ trunk/php/luasandbox/luasandbox.c   2012-04-24 10:39:43 UTC (rev 115020)
@@ -39,7 +39,7 @@
 static void luasandbox_call_helper(lua_State * L, zval * sandbox_zval, 
        php_luasandbox_obj * sandbox,
        zval *** args, zend_uint numArgs, zval * return_value TSRMLS_DC);
-static void luasandbox_handle_error(lua_State * L, int status);
+static void luasandbox_handle_error(php_luasandbox_obj * sandbox, int status);
 static void luasandbox_handle_emergency_timeout(php_luasandbox_obj * sandbox);
 static int luasandbox_call_php(lua_State * L);
 static int luasandbox_dump_writer(lua_State * L, const void * p, size_t sz, 
void * ud);
@@ -498,7 +498,7 @@
        // Parse the string into a function on the stack
        status = luaL_loadbuffer(L, code, codeLength, chunkName);
        if (status != 0) {
-               luasandbox_handle_error(L, status);
+               luasandbox_handle_error(sandbox, status);
                return;
        }
 
@@ -565,38 +565,67 @@
  * status is returned and an error message pushed to the stack. Throws a 
suitable
  * exception.
  */
-static void luasandbox_handle_error(lua_State * L, int status)
+static void luasandbox_handle_error(php_luasandbox_obj * sandbox, int status)
 {
+       lua_State * L = sandbox->state;
        const char * errorMsg = luasandbox_error_to_string(L, -1);
        zend_class_entry * ce;
-       if (!EG(exception)) {
-               if (luasandbox_is_fatal(L, -1) && !strcmp(errorMsg, 
luasandbox_timeout_message)) {
-                       ce = luasandboxtimeouterror_ce;
-               }
-               switch (status) {
-                       case LUA_ERRRUN:
-                               if (luasandbox_is_fatal(L, -1)) {
-                                       if (!strcmp(errorMsg, 
luasandbox_timeout_message)) {
-                                               ce = luasandboxtimeouterror_ce;
-                                       } else {
-                                               ce = luasandboxfatalerror_ce;
-                                       }
+       zval *zex, *ztrace;
+       
+       if (EG(exception)) {
+               lua_pop(L, 1);
+               return;
+       }
+
+       if (luasandbox_is_fatal(L, -1) && !strcmp(errorMsg, 
luasandbox_timeout_message)) {
+               ce = luasandboxtimeouterror_ce;
+       }
+       switch (status) {
+               case LUA_ERRRUN:
+                       if (luasandbox_is_fatal(L, -1)) {
+                               if (!strcmp(errorMsg, 
luasandbox_timeout_message)) {
+                                       ce = luasandboxtimeouterror_ce;
                                } else {
-                                       ce = luasandboxruntimeerror_ce;
+                                       ce = luasandboxfatalerror_ce;
                                }
-                               break;
-                       case LUA_ERRSYNTAX:
-                               ce = luasandboxsyntaxerror_ce;
-                               break;
-                       case LUA_ERRMEM:
-                               ce = luasandboxmemoryerror_ce;
-                               break;
-                       case LUA_ERRERR:
-                               ce = luasandboxerrorerror_ce;
-                               break;
-               }
-               zend_throw_exception(ce, (char*)errorMsg, status);
+                       } else {
+                               ce = luasandboxruntimeerror_ce;
+                       }
+                       break;
+               case LUA_ERRSYNTAX:
+                       ce = luasandboxsyntaxerror_ce;
+                       break;
+               case LUA_ERRMEM:
+                       ce = luasandboxmemoryerror_ce;
+                       break;
+               case LUA_ERRERR:
+                       ce = luasandboxerrorerror_ce;
+                       break;
        }
+
+       MAKE_STD_ZVAL(zex);
+       object_init_ex(zex, ce);
+
+       if (luasandbox_is_trace_error(L, -1)) {
+               // Push the trace on to the top of the stack
+               lua_rawgeti(L, -1, 3);
+               // Convert it to a zval
+               MAKE_STD_ZVAL(ztrace);
+               luasandbox_lua_to_zval(ztrace, L, -1, sandbox->current_zval, 
NULL);
+               // Put it in the exception object
+               zend_update_property(ce, zex, "luaTrace", sizeof("luaTrace")-1, 
ztrace TSRMLS_CC);
+               zval_ptr_dtor(&ztrace);
+               lua_pop(L, 1);
+       }
+
+       // Initialise standard properties
+       // We would get Zend to do this, but the code for it is wrapped inside 
some
+       // very inconvenient interfaces (so inconvenient that Zend itself 
+       // duplicates this code in several places).
+       zend_update_property_string(ce, zex, "message", sizeof("message")-1, 
errorMsg TSRMLS_CC);
+       zend_update_property_long(ce, zex, "code", sizeof("code")-1, status 
TSRMLS_CC);
+
+       zend_throw_exception_object(zex TSRMLS_CC);
        lua_pop(L, 1);
 }
 /* }}} */
@@ -883,6 +912,8 @@
 {
        // Save the top position
        int origTop = lua_gettop(L);
+       // Keep track of the stack index where the return values will appear
+       int retIndex = origTop + 2;
        int status;
        int cpu_limited = 0;
        luasandbox_timer_set t;
@@ -897,6 +928,12 @@
                RETURN_FALSE;
        }
 
+       // Push the error function
+       lua_pushcfunction(L, luasandbox_attach_trace);
+
+       // Push the function to be called
+       lua_pushvalue(L, origTop);
+
        // Push the arguments
        for (i = 0; i < numArgs; i++) {
                if (!luasandbox_push_zval(L, *(args[i]))) {
@@ -924,7 +961,7 @@
 
        // Call the function
        sandbox->in_lua++;
-       status = lua_pcall(L, numArgs, LUA_MULTRET, 0);
+       status = lua_pcall(L, numArgs, LUA_MULTRET, origTop + 1);
        sandbox->in_lua--;
        sandbox->current_zval = old_zval;
 
@@ -939,20 +976,20 @@
 
        // Handle normal errors
        if (status) {
-               luasandbox_handle_error(L, status);
+               luasandbox_handle_error(sandbox, status);
                lua_settop(L, origTop - 1);
                RETURN_FALSE;
        }
 
        // Calculate the number of results and create an array of that capacity
-       numResults = lua_gettop(L) - origTop + 1;
+       numResults = lua_gettop(L) - retIndex + 1;
        array_init_size(return_value, numResults);
 
        // Fill the array with the results
        for (i = 0; i < numResults; i++) {
                zval * element;
                MAKE_STD_ZVAL(element);
-               luasandbox_lua_to_zval(element, L, origTop + i, sandbox_zval, 
NULL TSRMLS_CC);
+               luasandbox_lua_to_zval(element, L, retIndex + i, sandbox_zval, 
NULL TSRMLS_CC);
                zend_hash_next_index_insert(Z_ARRVAL_P(return_value), 
                        (void*)&element,
                        sizeof(zval*), NULL);

Modified: trunk/php/luasandbox/php_luasandbox.h
===================================================================
--- trunk/php/luasandbox/php_luasandbox.h       2012-04-24 00:10:27 UTC (rev 
115019)
+++ trunk/php/luasandbox/php_luasandbox.h       2012-04-24 10:39:43 UTC (rev 
115020)
@@ -140,7 +140,10 @@
        zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
 void luasandbox_wrap_fatal(lua_State * L);
 int luasandbox_is_fatal(lua_State * L, int index);
+int luasandbox_is_trace_error(lua_State * L, int index);
 const char * luasandbox_error_to_string(lua_State * L, int index);
+int luasandbox_attach_trace(lua_State * L);
+void luasandbox_push_structured_trace(lua_State * L, int level);
 
 
 #endif /* PHP_LUASANDBOX_H */


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to