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