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

Revision: 114817
Author:   tstarling
Date:     2012-04-10 05:34:03 +0000 (Tue, 10 Apr 2012)
Log Message:
-----------
* Added the ability to raise a normal Lua error in a PHP library by throwing a 
special kind of exception. This allows PHP libraries to have the same kind of 
error handling conventions as built-in libraries.
* Fixed a couple of compiler warnings.

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

Added Paths:
-----------
    trunk/php/luasandbox/tests/lua_catches_php_exception.phpt

Modified: trunk/php/luasandbox/data_conversion.c
===================================================================
--- trunk/php/luasandbox/data_conversion.c      2012-04-10 00:13:21 UTC (rev 
114816)
+++ trunk/php/luasandbox/data_conversion.c      2012-04-10 05:34:03 UTC (rev 
114817)
@@ -412,9 +412,9 @@
  * described in that function's documentation. The string is valid until the 
  * Lua value is destroyed.
  */
-char * luasandbox_error_to_string(lua_State * L, int index)
+const char * luasandbox_error_to_string(lua_State * L, int index)
 {
-       char * s;
+       const char * s;
        if (luasandbox_is_fatal(L, index)) {
                lua_rawgeti(L, index, 2);
                s = lua_tostring(L, -1);

Modified: trunk/php/luasandbox/luasandbox.c
===================================================================
--- trunk/php/luasandbox/luasandbox.c   2012-04-10 00:13:21 UTC (rev 114816)
+++ trunk/php/luasandbox/luasandbox.c   2012-04-10 05:34:03 UTC (rev 114817)
@@ -46,6 +46,12 @@
 
 zend_class_entry *luasandbox_ce;
 zend_class_entry *luasandboxerror_ce;
+zend_class_entry *luasandboxruntimeerror_ce;
+zend_class_entry *luasandboxfatalerror_ce;
+zend_class_entry *luasandboxsyntaxerror_ce;
+zend_class_entry *luasandboxmemoryerror_ce;
+zend_class_entry *luasandboxerrorerror_ce;
+zend_class_entry *luasandboxtimeout_ce;
 zend_class_entry *luasandboxemergencytimeout_ce;
 zend_class_entry *luasandboxplaceholder_ce;
 zend_class_entry *luasandboxfunction_ce;
@@ -168,9 +174,33 @@
        luasandboxerror_ce = zend_register_internal_class_ex(
                        &ce, zend_exception_get_default(TSRMLS_C), NULL 
TSRMLS_CC);
 
+       INIT_CLASS_ENTRY(ce, "LuaSandboxRuntimeError", 
luasandbox_empty_methods);
+       luasandboxruntimeerror_ce = zend_register_internal_class_ex(
+                       &ce, luasandboxerror_ce, NULL TSRMLS_CC);
+
+       INIT_CLASS_ENTRY(ce, "LuaSandboxFatalError", luasandbox_empty_methods);
+       luasandboxfatalerror_ce = zend_register_internal_class_ex(
+                       &ce, luasandboxerror_ce, NULL TSRMLS_CC);
+
+       INIT_CLASS_ENTRY(ce, "LuaSandboxSyntaxError", luasandbox_empty_methods);
+       luasandboxsyntaxerror_ce = zend_register_internal_class_ex(
+               &ce, luasandboxfatalerror_ce, NULL TSRMLS_CC);
+
+       INIT_CLASS_ENTRY(ce, "LuaSandboxMemoryError", luasandbox_empty_methods);
+       luasandboxmemoryerror_ce = zend_register_internal_class_ex(
+                       &ce, luasandboxfatalerror_ce, NULL TSRMLS_CC);
+
+       INIT_CLASS_ENTRY(ce, "LuaSandboxErrorError", luasandbox_empty_methods);
+       luasandboxerrorerror_ce = zend_register_internal_class_ex(
+                       &ce, luasandboxfatalerror_ce, NULL TSRMLS_CC);
+
+       INIT_CLASS_ENTRY(ce, "LuaSandboxTimeout", luasandbox_empty_methods);
+       luasandboxtimeout_ce = zend_register_internal_class_ex(
+                       &ce, luasandboxfatalerror_ce, NULL TSRMLS_CC);
+
        INIT_CLASS_ENTRY(ce, "LuaSandboxEmergencyTimeout", 
luasandbox_empty_methods);
        luasandboxemergencytimeout_ce = zend_register_internal_class_ex(
-               &ce, luasandboxerror_ce, NULL TSRMLS_CC);
+               &ce, luasandboxfatalerror_ce, NULL TSRMLS_CC);
 
        zend_declare_class_constant_long(luasandboxerror_ce, 
                "RUN", sizeof("RUN"), LUA_ERRRUN);
@@ -534,9 +564,24 @@
 static void luasandbox_handle_error(lua_State * L, int status)
 {
        const char * errorMsg = luasandbox_error_to_string(L, -1);
+       zend_class_entry * ce;
        lua_pop(L, 1);
        if (!EG(exception)) {
-               zend_throw_exception(luasandboxerror_ce, (char*)errorMsg, 
status);
+               switch (status) {
+                       case LUA_ERRRUN:
+                               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;
+               }
+               zend_throw_exception(ce, (char*)errorMsg, status);
        }
 }
 /* }}} */
@@ -1039,6 +1084,23 @@
 }
 /* }}} */
 
+/** {{{ luasandbox_instanceof
+ * Based on is_derived_class in zend_object_handlers.c
+ */
+static inline zend_bool luasandbox_instanceof(
+       zend_class_entry *child_class, zend_class_entry *parent_class)
+{
+       while (child_class) {
+               if (child_class == parent_class) {
+                       return 1;
+               }
+               child_class = child_class->parent;
+       }
+
+       return 0;
+}
+/* }}} */
+
 /** {{{ luasandbox_call_php
  *
  * The Lua callback for calling PHP functions. See the doc comment on 
@@ -1113,10 +1175,24 @@
        efree(temp);
        luasandbox_leave_php(L, intern);
 
-       // If an exception occurred, convert it to a Lua error (just to unwind 
the stack)
+       // If an exception occurred, convert it to a Lua error
        if (EG(exception)) {
-               lua_pushstring(L, "[exception]");
-               luasandbox_wrap_fatal(L);
+               // Get the error message and push it to the stack
+               zend_class_entry * ce = Z_OBJCE_P(EG(exception));
+               zval * zmsg = zend_read_property(ce, EG(exception), "message", 
sizeof("message")-1, 1 TSRMLS_CC);
+               if (zmsg && Z_TYPE_P(zmsg) == IS_STRING) {
+                       lua_pushlstring(L, Z_STRVAL_P(zmsg), Z_STRLEN_P(zmsg));
+               } else {
+                       lua_pushliteral(L, "[unknown exception]");
+               }
+
+               // If the exception was a LuaSandboxRuntimeError or a subclass, 
clear the 
+               // exception and raise a non-fatal (catchable) error
+               if (luasandbox_instanceof(ce, luasandboxruntimeerror_ce)) {
+                       zend_clear_exception(TSRMLS_C);
+               } else {
+                       luasandbox_wrap_fatal(L);
+               }
                lua_error(L);
        }
        return num_results;

Modified: trunk/php/luasandbox/php_luasandbox.h
===================================================================
--- trunk/php/luasandbox/php_luasandbox.h       2012-04-10 00:13:21 UTC (rev 
114816)
+++ trunk/php/luasandbox/php_luasandbox.h       2012-04-10 05:34:03 UTC (rev 
114817)
@@ -92,6 +92,7 @@
 
 
 php_luasandbox_obj * luasandbox_get_php_obj(lua_State * L);
+void luasandbox_timer_timeout_error(lua_State *L);
 
 /** {{{ luasandbox_enter_php
  *
@@ -139,7 +140,7 @@
        zval * sandbox_zval, HashTable * recursionGuard TSRMLS_DC);
 void luasandbox_wrap_fatal(lua_State * L);
 int luasandbox_is_fatal(lua_State * L, int index);
-char * luasandbox_error_to_string(lua_State * L, int index);
+const char * luasandbox_error_to_string(lua_State * L, int index);
 
 
 #endif /* PHP_LUASANDBOX_H */

Added: trunk/php/luasandbox/tests/lua_catches_php_exception.phpt
===================================================================
--- trunk/php/luasandbox/tests/lua_catches_php_exception.phpt                   
        (rev 0)
+++ trunk/php/luasandbox/tests/lua_catches_php_exception.phpt   2012-04-10 
05:34:03 UTC (rev 114817)
@@ -0,0 +1,56 @@
+--TEST--
+PHP throwing exceptions to be caught by pcall()
+--FILE--
+<?php
+$lua = <<<LUA
+       function pcall_test(f)
+               local status, msg
+               status, msg = pcall(f)
+               if not status then
+                       return "Caught: " .. msg
+               else
+                       return "success"
+               end
+       end
+
+       function hang_test(f)
+               pcall_test(f)
+               while true do end
+       end
+LUA;
+
+function runtime_error() {
+       throw new LuaSandboxRuntimeError("runtime error");
+}
+function fatal_error() {
+       throw new LuaSandboxFatalError("fatal error");
+}
+function plain_exception() {
+       throw new Exception("exception");
+}
+
+$tests = array(
+       'Runtime error' => array( 'pcall_test', 'runtime_error' ),
+       'Fatal error' => array( 'hang_test', 'fatal_error' ),
+       'Plain Exception' => array( 'hang_test', 'plain_exception' ),
+);
+
+foreach ( $tests as $desc => $info ) {
+       list( $wrapper, $funcName ) = $info;
+       echo "$desc: ";
+       try {
+               $sandbox = new LuaSandbox;
+               $sandbox->loadString( $lua )->call();
+               $sandbox->setCPULimit( 0.25 );
+               $sandbox->registerLibrary( 'test', array( 'test' => $funcName ) 
);
+               $res = $sandbox->loadString( 'return test.test' )->call();
+               print implode("\n",
+                       $sandbox->callFunction( $wrapper, $res[0] ) ) . "\n";
+       } catch ( Exception $e ) {
+               echo get_class( $e ) . ': ' . $e->getMessage() . "\n";
+       }
+}
+--EXPECT--
+Runtime error: Caught: runtime error
+Fatal error: LuaSandboxFatalError: fatal error
+Plain Exception: Exception: exception


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

Reply via email to