/home/fresvfqn/waterdamagerestorationgerritsenbeach.com/alt-php80-pecl-luasandbox_4.1.2-2.el8.tar
tests/ipairs.phpt000064400000003034150557451400010100 0ustar00--TEST--
ipairs() and __ipairs
--FILE--
<?php
$lua = <<<LUA
	function ipairs_test1()
		local t = { 'a' }
		local f, s, var = ipairs( t )
		if type( f ) == 'function' and s == t and var == 0 then
			local k, v = f(t, var)
			if k == 1 and v == 'a' then
				return "Ok"
			else
				return "Fail: First call returned " .. k .. "\\t" .. v
			end
		else
			return "Fail:\\n" ..
				tostring(f) .. '\\t' .. tostring(s) .. '\\t' .. tostring(var) .. '\\n' ..
				tostring(next) .. '\\t' .. tostring(t) .. '\\t0'
		end
	end

	function ipairs_test2()
		local t = { 1 }
		setmetatable( t, { __ipairs = function () return 1, 2, 3 end } )
		local f, s, var = ipairs( t )
		if f == 1 and s == 2 and var == 3 then
			return "Ok"
		else
			return "Fail:\\n" ..
				tostring(f) .. '\\t' .. tostring(s) .. '\\t' .. tostring(var) .. '\\n' ..
				'1\\t2\\t3'
		end
	end

	function ipairs_test3()
		ipairs()
		return "Fail: Should have thrown an error"
	end
LUA;

$tests = array(
	'Normal' => 'ipairs_test1',
	'With __ipairs' => 'ipairs_test2',
	'No argument' => 'ipairs_test3',
);

foreach ( $tests as $desc => $func ) {
	echo "$desc: ";
	$sandbox = new LuaSandbox;
	$sandbox->loadString( $lua )->call();
	$sandbox->setCPULimit( 0.25 );
	$sandbox->setMemoryLimit( 100000 );
	try {
		print implode("\n", $sandbox->callFunction( $func ) ) . "\n";
	} catch ( LuaSandboxError $e ) {
		echo "LuaSandboxError: " . $e->getMessage() . "\n";
	}
}

--EXPECT--
Normal: Ok
With __ipairs: Ok
No argument: LuaSandboxError: [string ""]:32: bad argument #1 to 'ipairs' (table expected, got no value)
tests/dump_loadBinary_call.phpt000064400000000611150557451400012713 0ustar00--TEST--
dump -> loadBinary -> call
--FILE--
<?php

var_dump( $sandbox = new LuaSandbox );
var_dump( $f = $sandbox->loadString( 'return 1' ) );
$dump = $f->dump();
var_dump( $restore = $sandbox->loadBinary( $dump ) );
var_dump( $restore->call() );

--EXPECT--
object(LuaSandbox)#1 (0) {
}
object(LuaSandboxFunction)#2 (0) {
}
object(LuaSandboxFunction)#3 (0) {
}
array(1) {
  [0]=>
  int(1)
}
tests/datatypes-unsupported.phpt000064400000005440150557451400013200 0ustar00--TEST--
Handling of unsupported datatypes
--FILE--
<?php

function doTest( $test, $data ) {
	printf( "%s ", "$test (call PHP->Lua):" );
	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	try {
		$ret = $sandbox->loadString( 'return 1' )->call( $data );
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}

	printf( "%s ", "$test (return PHP->Lua):" );
	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	$f = $sandbox->wrapPhpFunction( function () use ( $data ) {
		return [ $data ];
	} );
	try {
		$sandbox->loadString( 'local f = ...; f()' )->call( $f );
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}
}

function doTest2( $test, $lua ) {
	printf( "%s ", "$test (call Lua->PHP):" );
	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	$f = $sandbox->wrapPhpFunction( function ( $val ) {
		echo "PHP received " . preg_replace( '/\s+/', ' ', var_export( $val, 1 ) ) . "\n";
	} );
	try {
		$sandbox->loadString( "local f = ...\n$lua\nf(v)" )->call( $f );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}

	printf( "%s ", "$test (return Lua->PHP):" );
	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	try {
		$ret = $sandbox->loadString( "$lua\nreturn v" )->call();
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret, 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}
}

$test = array();
$test['foo'] = &$test;
doTest( 'recursive array', $test );

$test = new stdClass;
doTest( 'object', $test );

doTest2( 'recursive table', 'v = {}; v.v = v' );

--EXPECTF--
recursive array (call PHP->Lua): %AWarning: LuaSandboxFunction::call(): Cannot pass circular reference to Lua in %s on line %d
%AWarning: LuaSandboxFunction::call(): unable to convert argument 1 to a lua value in %s on line %d
false
recursive array (return PHP->Lua): %AWarning: LuaSandboxFunction::call(): Cannot pass circular reference to Lua in %s on line %d
false
object (call PHP->Lua): %AWarning: LuaSandboxFunction::call(): Unable to convert object of type stdClass in %s on line %d
%AWarning: LuaSandboxFunction::call(): unable to convert argument 1 to a lua value in %s on line %d
false
object (return PHP->Lua): %AWarning: LuaSandboxFunction::call(): Unable to convert object of type stdClass in %s on line %d
false
recursive table (call Lua->PHP): EXCEPTION: Cannot pass circular reference to PHP
recursive table (return Lua->PHP): EXCEPTION: Cannot pass circular reference to PHP
tests/pcall.phpt000064400000002425150557451400007707 0ustar00--TEST--
pcall() catching various errors
--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
LUA;

$tests = array(
	'Normal' => 'return 1',
	'User error' => 'error("runtime error")',
	'Argument check error' => 'string.byte()',
	'Infinite recursion' => 'function foo() foo() end foo()',
	'Infinite loop (timeout)' => 'while true do end',
	'Out of memory' => 'string.rep("x", 1000000)'
);

foreach ( $tests as $desc => $code ) {
	echo "$desc: ";
	$sandbox = new LuaSandbox;
	$sandbox->loadString( $lua )->call();
	$sandbox->setCPULimit( 0.25 );
	$sandbox->setMemoryLimit( 100000 );
	try {
		print implode("\n",
			$sandbox->callFunction( 'pcall_test', $sandbox->loadString( $code ) ) ) . "\n";
	} catch ( LuaSandboxError $e ) {
		echo "LuaSandboxError: " . $e->getMessage() . "\n";
	}
}

--EXPECT--
Normal: success
User error: Caught: [string ""]:1: runtime error
Argument check error: Caught: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value)
Infinite recursion: LuaSandboxError: not enough memory
Infinite loop (timeout): LuaSandboxError: The maximum execution time for this script was exceeded
Out of memory: LuaSandboxError: not enough memory
tests/call.phpt000064400000012150150557451400007523 0ustar00--TEST--
LuaSandboxFunction::call
--FILE--
<?php
$sandbox = new LuaSandbox;
var_dump( $sandbox->loadString( 'return 1' )->call() );

echo "Proper handling of circular tables returned by Lua: ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'local t = {}; t.t = t; return t' )->call();
	echo var_export( $ret, 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Proper handling of circular tables in Lua→PHP call: ";
$sandbox = new LuaSandbox;
$f = $sandbox->wrapPhpFunction( function () {
	echo func_num_args() . " args ok\n";
} );
try {
	$sandbox->loadString( 'local f = ...; local t = {}; t.t = t; f( t )' )->call( $f );
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Passing lots of arguments PHP->Lua doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$ret = call_user_func_array(
	array( $sandbox->loadString( 'return select( "#", ... )' ), 'call' ),
	array_fill( 0, 500, '' )
);
echo "$ret[0] args ok\n";

echo "Passing lots of arguments Lua->PHP doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$f = $sandbox->wrapPhpFunction( function () {
	echo func_num_args() . " args ok\n";
} );
$sandbox->loadString( 'local f = ...; f( string.byte( string.rep( "x", 500 ), 1, -1 ) )' )->call( $f );

echo "Returning lots of values PHP->Lua doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$f = $sandbox->wrapPhpFunction( function () {
	return array_fill( 0, 500, '' );
} );
$ret = $sandbox->loadString( 'local f = ...; return select( "#", f() )' )->call( $f );
echo "$ret[0] values ok\n";

echo "Returning lots of values Lua->PHP doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$ret = $sandbox->loadString( 'return string.byte( string.rep( "x", 500 ), 1, -1 )' )->call();
echo count( $ret ) . " values ok\n";

echo "Passing deeply-nested arrays PHP->Lua doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$v = 1;
for ( $i = 0; $i < 500; $i++ ) {
	$v = array( $v );
}
$lua = <<<LUA
	local ct, t = 0, ...
	while type( t ) == "table" do
		_, t = next( t )
		ct = ct + 1
	end
	return ct
LUA;
$ret = $sandbox->loadString( $lua )->call( $v );
echo "$ret[0] levels ok\n";

echo "Passing deeply-nested tables Lua->PHP doesn't cause a crash: ";
$sandbox = new LuaSandbox;
$ret = $sandbox->loadString( 'local t = 1; for i = 1, 500 do t = { t } end; return t' )->call();
$ct = 0;
$v = $ret[0];
while ( is_array( $v ) ) {
	$v = reset( $v );
	$ct++;
}
echo "$ct levels ok\n";

echo "Proper handling of invalid keys in Lua→PHP conversion (table): ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'return { [{}] = 1 }' )->call();
	echo var_export( $ret[0], 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Proper handling of invalid keys in Lua→PHP conversion (bool): ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'return { [true] = 1 }' )->call();
	echo var_export( $ret[0], 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Proper handling of invalid keys in Lua→PHP conversion (function): ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'return { [tostring] = 1 }' )->call();
	echo var_export( $ret[0], 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Proper handling of unusual keys in Lua→PHP conversion (float): ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'return { [1.5] = 1 }' )->call();
	echo var_export( $ret[0], 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

echo "Proper handling of unusual keys in Lua→PHP conversion (inf): ";
$sandbox = new LuaSandbox;
try {
	$ret = $sandbox->loadString( 'return { [math.huge] = 1 }' )->call();
	echo var_export( $ret[0], 1 ) . "\n";
} catch ( Exception $ex ) {
	echo "Exception: " . $ex->getMessage() . "\n";
}

--EXPECT--
array(1) {
  [0]=>
  int(1)
}
Proper handling of circular tables returned by Lua: Exception: Cannot pass circular reference to PHP
Proper handling of circular tables in Lua→PHP call: Exception: Cannot pass circular reference to PHP
Passing lots of arguments PHP->Lua doesn't cause a crash: 500 args ok
Passing lots of arguments Lua->PHP doesn't cause a crash: 500 args ok
Returning lots of values PHP->Lua doesn't cause a crash: 500 values ok
Returning lots of values Lua->PHP doesn't cause a crash: 500 values ok
Passing deeply-nested arrays PHP->Lua doesn't cause a crash: 500 levels ok
Passing deeply-nested tables Lua->PHP doesn't cause a crash: 500 levels ok
Proper handling of invalid keys in Lua→PHP conversion (table): Exception: Cannot use table as an array key when passing data from Lua to PHP
Proper handling of invalid keys in Lua→PHP conversion (bool): Exception: Cannot use boolean as an array key when passing data from Lua to PHP
Proper handling of invalid keys in Lua→PHP conversion (function): Exception: Cannot use function as an array key when passing data from Lua to PHP
Proper handling of unusual keys in Lua→PHP conversion (float): array (
  '1.5' => 1,
)
Proper handling of unusual keys in Lua→PHP conversion (inf): array (
  'inf' => 1,
)
tests/extending-LuaSandbox.phpt000064400000001515150557451400012636 0ustar00--TEST--
Extending LuaSandbox
--FILE--
<?php
// bugs T59292 and T205370
#[AllowDynamicProperties]
class ExtendedLuaSandbox extends LuaSandbox {
	public $var1;
	public $var2;
	public $var3;
	public $var4;
	public $var5;
}
$sandbox = new ExtendedLuaSandbox;

for($i=1; $i<=5; $i++){
	$sandbox->{"var$i"} = $i;
}
var_dump( $sandbox );

for($i=6; $i<=10; $i++){
	$sandbox->{"var$i"} = $i;
}
var_dump( $sandbox );

echo "ok\n";

--EXPECT--
object(ExtendedLuaSandbox)#1 (5) {
  ["var1"]=>
  int(1)
  ["var2"]=>
  int(2)
  ["var3"]=>
  int(3)
  ["var4"]=>
  int(4)
  ["var5"]=>
  int(5)
}
object(ExtendedLuaSandbox)#1 (10) {
  ["var1"]=>
  int(1)
  ["var2"]=>
  int(2)
  ["var3"]=>
  int(3)
  ["var4"]=>
  int(4)
  ["var5"]=>
  int(5)
  ["var6"]=>
  int(6)
  ["var7"]=>
  int(7)
  ["var8"]=>
  int(8)
  ["var9"]=>
  int(9)
  ["var10"]=>
  int(10)
}
ok
tests/array-key-conversion.phpt000064400000006154150557451400012706 0ustar00--TEST--
Array key conversion
--FILE--
<?php

function testPhpToLua( $test, $array ) {
	printf( "PHP→Lua %-30s ", "$test:" );

	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	try {
		$ret = $sandbox
			->loadString( 'local t, r = ..., {}; for k, v in pairs( t ) do r[v] = type(k) end return r' )
			->call( $array );
		if ( is_array( $ret[0] ) ) {
			ksort( $ret[0], SORT_STRING );
		}
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}
}

function testLuaToPhp( $test, $lua ) {
	printf( "Lua→PHP %-30s ", "$test:" );

	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	try {
		$ret = $sandbox->loadString( "return { $lua }" )->call();
		if ( is_array( $ret[0] ) ) {
			ksort( $ret[0], SORT_STRING );
		}
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}
}

if ( PHP_INT_MAX > 9007199254740992 ) {
	$a = [
		'9007199254740992' => 'max', '9007199254740993' => 'max+1',
		'-9007199254740992' => 'min', '-9007199254740993' => 'min-1',
	];
	$max = '9223372036854775807';
	$max2 = '9223372036854775808';
	$min = '-9223372036854775808';
	$min2 = '-9223372036854775809';
} else {
	$a = [
		'2147483647' => 'max', '2147483648' => 'max+1',
		'-2147483648' => 'min', '-2147483649' => 'min-1',
	];
	$max = '2147483647';
	$max2 = '2147483648';
	$min = '-2147483648';
	$min2 = '-2147483649';
}

testPhpToLua( 'simple integers', [ -10 => 'minus ten', 0 => 'zero', 10 => 'ten' ] );
testPhpToLua( 'maximal values', $a );

testLuaToPhp( 'simple integers', '[-10] = "minus ten", [0] = "zero", [10] = "ten"' );
testLuaToPhp( 'stringified integers', '["-10"] = "minus ten", ["0"] = "zero", ["10"] = "ten"' );
testLuaToPhp( 'maximal integers', "['$max'] = 'max', ['$max2'] = 'max+1', ['$min'] = 'min', ['$min2'] = 'min-1'" );
testLuaToPhp( 'collision (0)', '[0] = "number zero", ["0"] = "string zero"' );
testLuaToPhp( 'collision (float)', '[1.5] = "number 1.5", ["1.5"] = "string 1.5"' );
testLuaToPhp( 'collision (inf)', '[1/0] = "number inf", ["inf"] = "string inf"' );

--EXPECTF--
PHP→Lua simple integers:               array ( 'minus ten' => 'number', 'ten' => 'number', 'zero' => 'number', )
PHP→Lua maximal values:                array ( 'max' => 'number', 'max+1' => 'string', 'min' => 'number', 'min-1' => 'string', )
Lua→PHP simple integers:               array ( -10 => 'minus ten', 0 => 'zero', 10 => 'ten', )
Lua→PHP stringified integers:          array ( -10 => 'minus ten', 0 => 'zero', 10 => 'ten', )
Lua→PHP maximal integers:              array ( -%d => 'min', '-%d' => 'min-1', %d => 'max', '%d' => 'max+1', )
Lua→PHP collision (0):                 EXCEPTION: Collision for array key 0 when passing data from Lua to PHP
Lua→PHP collision (float):             EXCEPTION: Collision for array key 1.5 when passing data from Lua to PHP
Lua→PHP collision (inf):               EXCEPTION: Collision for array key inf when passing data from Lua to PHP
tests/profiler.phpt000064400000003054150557451400010435 0ustar00--TEST--
profiler
--FILE--
<?php

// Note these tests have to waste CPU cycles rather than sleep(), because the
// timer counts CPU time used and sleep() doesn't use CPU time.

$lua = <<<LUA
	lua = {}

	function lua.test()
		local t = os.clock() + 0.2
		while os.clock() < t do end
	end
LUA;

$sandbox = new LuaSandbox;
$sandbox->loadString( $lua )->call();
$sandbox->enableProfiler( 0.02 );

$sandbox->callFunction( 'lua.test' );

echo "Samples: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SAMPLES )['clock'] . "\n";
echo "Seconds: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] . "\n";
echo "Seconds > 0: "
	. ( $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] > 0 ? 'yes' : 'no' )
	. "\n";
echo "Percent: " . $sandbox->getProfilerFunctionReport( LuaSandbox::PERCENT )['clock'] . "\n";

// Test that re-enabling the profiler doesn't explode
$sandbox->enableProfiler( 0.03 );

$sandbox->callFunction( 'lua.test' );

echo "Samples: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SAMPLES )['clock'] . "\n";
echo "Seconds: " . $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] . "\n";
echo "Seconds > 0: "
	. ( $sandbox->getProfilerFunctionReport( LuaSandbox::SECONDS )['clock'] > 0 ? 'yes' : 'no' )
	. "\n";
echo "Percent: " . $sandbox->getProfilerFunctionReport( LuaSandbox::PERCENT )['clock'] . "\n";

// Test that disabling the profiler doesn't explode
$sandbox->disableProfiler();

--EXPECTF--
Samples: %d
Seconds: %f
Seconds > 0: yes
Percent: %f
Samples: %d
Seconds: %f
Seconds > 0: yes
Percent: %f
tests/callback_exception.phpt000064400000000610150557451400012420 0ustar00--TEST--
Exception in a PHP function called from Lua
--FILE--
<?php

function throw_exception() {
	throw new Exception('message');
}
$sandbox = new LuaSandbox;
$sandbox->registerLibrary( 'test', array( 'throw_exception' => 'throw_exception' ) );
$f = $sandbox->loadString('test.throw_exception()');
try {
	$f->call();
} catch ( Exception $e ) {
	print $e->getMessage();
}

--EXPECT--
message
tests/reentrant.phpt000064400000002147150557451400010617 0ustar00--TEST--
Re-entering Lua during a callback to PHP
--FILE--
<?php
$sandbox = new LuaSandbox;
$chunk = $sandbox->loadString('
	function factorial(n)
		if n <= 1 then
			return 1
		else
			return n * test.factorial(n - 1)
		end
	end

	return factorial
');

$ret = $chunk->call();
$luaFactorial = $ret[0];

$sandbox->registerLibrary( 'test', array( 'factorial' => 'factorial' ) );

function factorial($n) {
	global $luaFactorial;
	if ($n <= 1) {
		return array(1);
	} else {
		$ret = $luaFactorial->call($n - 1);
		return array($n * $ret[0]);
	}
}

print implode('', factorial(10)) . "\n";
var_dump( $luaFactorial->call(10) );

try {
	$luaFactorial->call(1000000000);
} catch ( LuaSandboxError $e ) {
	print $e->getMessage() . "\n";
}
try {
	factorial(1000000000);
} catch ( LuaSandboxError $e ) {
	print $e->getMessage() . "\n";
}

--EXPECTF--
3628800
array(1) {
  [0]=>
  int(3628800)
}
%AWarning: LuaSandboxFunction::call(): Failed to generate Lua trace (C stack overflow) in %s on line %d
C stack overflow
%AWarning: LuaSandboxFunction::call(): Failed to generate Lua trace (C stack overflow) in %s on line %d
C stack overflow
tests/lua_catches_php_exception.phpt000064400000002532150557451400014013 0ustar00--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
tests/loadString.phpt000064400000000516150557451400010721 0ustar00--TEST--
loadString 1
--FILE--
<?php
var_dump($sandbox = new LuaSandbox);
var_dump($f = $sandbox->loadString('foo()'));
try {
	$f = $sandbox->loadString('foo');
} catch( Exception $e ) {
	print $e->getMessage();
}

--EXPECTF--
object(LuaSandbox)#1 (0) {
}
object(LuaSandboxFunction)#2 (0) {
}
[string ""]:1: '=' expected near '<eof>'
tests/profiler-sorting.phpt000064400000002267150557451400012125 0ustar00--TEST--
profiler sorting
--FILE--
<?php

// Note these tests have to busy-loop. Even if Lua had an "os.sleep", it'd just
// say "sleep" used all the time. And we can't directly loop on os.clock() here
// either, because that would say "clock" used most of the time.

$lua = <<<LUA
	function test1()
		for i = 0, 1e6 do end
	end

	function test2()
		for i = 0, 4e6 do end
	end

	function test3()
		for i = 0, 2e6 do end
	end

	function test()
		local t = os.clock() + 0.5
		while os.clock() < t do
			test1()
			test2()
			test3()
		end
	end
LUA;

$sandbox = new LuaSandbox;
$sandbox->loadString( $lua )->call();
$sandbox->enableProfiler( 0.01 );

$sandbox->callFunction( 'test' );

foreach( [
	'samples' => LuaSandbox::SAMPLES,
	'seconds' => LuaSandbox::SECONDS,
	'percent' => LuaSandbox::PERCENT
] as $name => $stat ) {
	$result = $sandbox->getProfilerFunctionReport( $stat );
	// "clone" and sort
	$sorted = array_combine( array_keys( $result ), array_values( $result ) );
	arsort( $sorted );
	if ( $result === $sorted ) {
		echo "$name: OK\n";
	} else {
		echo "$name: FAIL\n";
		var_export( [
			'result' => $result,
			'sorted' => $sorted,
		] );
	}
}

--EXPECTF--
samples: OK
seconds: OK
percent: OK
tests/datatypes.phpt000064400000004672150557451410010621 0ustar00--TEST--
Data type round-tripping
--FILE--
<?php

function doTest( $test, $data ) {
	printf( "%-25s ", "$test:" );

	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	$sandbox->setCPULimit( 0.1 );
	try {
		$ret = $sandbox->loadString( 'return ...' )->call( $data );
		if ( is_array( $ret[0] ) ) {
			ksort( $ret[0], SORT_STRING );
		}
		printf( "%s\n", preg_replace( '/\s+/', ' ', var_export( $ret[0], 1 ) ) );
	} catch ( LuaSandboxError $e ) {
		printf( "EXCEPTION: %s\n", $e->getMessage() );
	}
}

doTest( 'null', null );
doTest( 'int', 123 );
if ( is_int( 17179869184 ) ) {
	doTest( 'long', 17179869184 );
} else {
	// Fake it for 32-bit systems
	printf( "%-25s %s\n", "long:", "17179869184" );
}
doTest( 'double', 3.125 );
doTest( 'NAN', NAN );
doTest( 'INF', INF );
doTest( 'true', true );
doTest( 'false', false );
doTest( 'string', 'foobar' );
doTest( 'empty string', '' );
doTest( 'string containing NULs', "foo\0bar" );
doTest( 'array', array( 'foo', 'bar' ) );
doTest( 'associative array', array( 'foo', 'bar' => 'baz' ) );

$var = 42;
doTest( 'array with reference', [ &$var ] );

$sandbox = new LuaSandbox;
$sandbox->setMemoryLimit( 100000 );
$sandbox->setCPULimit( 0.1 );
$func = $sandbox->wrapPhpFunction( function ( $x ) { return [ "FUNC: $x" ]; } );
try {
	$ret = $sandbox->loadString( 'return ...' )->call( $func );
	$ret2 = $ret[0]->call( "ok" );
	printf( "%-25s %s\n", "function, pass-through:", $ret2[0] );

	$ret = $sandbox->loadString( 'f = ...; return f( "ok" )' )->call( $func );
	printf( "%-25s %s\n", "function, called:", $ret[0] );

	$ret = $sandbox->loadString( 'return function ( x ) return "FUNC: " .. x end' )->call();
	$ret2 = $ret[0]->call( "ok" );
	printf( "%-25s %s\n", "function, returned:", $ret2[0] );
} catch ( LuaSandboxError $e ) {
	printf( "EXCEPTION: %s\n", $e->getMessage() );
}

--EXPECT--
null:                     NULL
int:                      123
long:                     17179869184
double:                   3.125
NAN:                      NAN
INF:                      INF
true:                     true
false:                    false
string:                   'foobar'
empty string:             ''
string containing NULs:   'foo' . "\0" . 'bar'
array:                    array ( 0 => 'foo', 1 => 'bar', )
associative array:        array ( 0 => 'foo', 'bar' => 'baz', )
array with reference:     array ( 0 => 42, )
function, pass-through:   FUNC: ok
function, called:         FUNC: ok
function, returned:       FUNC: ok

tests/errors-at-call-boundaries.phpt000064400000003760150557451410013600 0ustar00--TEST--
Errors at PHP→Lua call boundaries
--FILE--
<?php

$sandbox = null; // Will be filled in later

function doTest( $str, callable $c ) {
	global $sandbox;

	echo "$str: ";
	$sandbox = new LuaSandbox;
	$sandbox->setMemoryLimit( 100000 );
	try {
		$ret = $sandbox->loadString( 'local f = ...; return f()' )
			->call( $sandbox->wrapPhpFunction( $c ) );
		var_dump( $ret );
	} catch ( Exception $ex ) {
		echo "Exception: " . $ex->getMessage() . "\n";
	}
}

doTest( 'LuaSandbox::callFunction', function () {
	global $sandbox;

	$sandbox->loadString( 'function foo() return "no error" end' )->call();
	return $sandbox->callFunction( 'foo',
		str_repeat( 'a', 33334 ),
		str_repeat( 'b', 33334 ),
		str_repeat( 'c', 33334 ),
		str_repeat( 'd', 33334 )
	);
} );

doTest( 'LuaSandbox::registerLibrary 1', function () {
	global $sandbox;

	$sandbox->registerLibrary( str_repeat( 'a', 33334 ), [ 'foo' => function () {} ] );
	$sandbox->registerLibrary( str_repeat( 'b', 33334 ), [ 'foo' => function () {} ] );
	$sandbox->registerLibrary( str_repeat( 'c', 33334 ), [ 'foo' => function () {} ] );
	$sandbox->registerLibrary( str_repeat( 'd', 33334 ), [ 'foo' => function () {} ] );

	return [ 'no error' ];
} );

doTest( 'LuaSandbox::registerLibrary 2', function () {
	global $sandbox;

	$sandbox->registerLibrary( 'foo', [
		str_repeat( 'a', 33334 ) => function () {},
		str_repeat( 'b', 33334 ) => function () {},
		str_repeat( 'c', 33334 ) => function () {},
		str_repeat( 'd', 33334 ) => function () {},
	] );

	return [ 'no error' ];
} );

doTest( 'LuaSandboxFunction::call', function () {
	global $sandbox;

	return $sandbox->loadString( 'return "no error"' )->call(
		str_repeat( 'a', 33334 ),
		str_repeat( 'b', 33334 ),
		str_repeat( 'c', 33334 ),
		str_repeat( 'd', 33334 )
	);
} );

--EXPECT--
LuaSandbox::callFunction: Exception: not enough memory
LuaSandbox::registerLibrary 1: Exception: not enough memory
LuaSandbox::registerLibrary 2: Exception: not enough memory
LuaSandboxFunction::call: Exception: not enough memory
tests/xpcall.phpt000064400000005153150557451410010101 0ustar00--TEST--
xpcall() basic behaviour
--FILE--
<?php

$lua = <<<LUA
	function xpcall_test(f, err)
		local status, msg
		status, msg = xpcall(f, err)
		if not status then
			return msg
		else
			return "success"
		end
	end
LUA;

$xperr = 'return "xp: " .. msg';

$tests = array(
	'Normal' => array(
		'return 1',
		$xperr
	),
	'User error' => array(
		'error("runtime error")',
		$xperr
	),
	'Error in error handler' => array(
		'error("original error")',
		'error("error in handler")'
	),
	'Unconvertible error in error handler' => array(
		'error("original error")',
		'error({})'
	),
	'Numeric error in error handler' => array(
		'error("original error")',
		'error(2)',
	),
	'Argument check error' => array(
		'string.byte()',
		$xperr
	),
	'Protected infinite recursion' => array(
		'function foo() foo() end foo()',
		$xperr
	),
	'Infinite recursion in handler' => array(
		'error("x")',
		'function foo() foo() end foo()'
	),
	'Protected infinite loop' => array(
		'while true do end',
		$xperr,
	),
	'Infinite loop in handler' => array(
		'error("x")',
		'while true do end',
	),
	'Out of memory in handler' => array(
		'error("x")',
		'string.rep("x", 1000000)'
	),
);

$sandbox = new LuaSandbox;
$sandbox->loadString( $lua )->call();
$sandbox->setCPULimit( 0.25 );
$sandbox->setMemoryLimit( 100000 );

foreach ( $tests as $desc => $info ) {
	$sandbox = new LuaSandbox;
	$sandbox->loadString( $lua )->call();
	$sandbox->setCPULimit( 0.25 );
	$sandbox->setMemoryLimit( 100000 );
	echo "$desc: ";
	list( $code, $errorCode ) = $info;
	$func = $sandbox->loadString( $code );
	$errorCode = "return function(msg) $errorCode end";
	$ret = $sandbox->loadString( $errorCode )->call();
	$errorFunc = $ret[0];

	try {
		print implode("\n",
			$sandbox->callFunction( 'xpcall_test', $func, $errorFunc ) ) . "\n";
	} catch ( LuaSandboxError $e ) {
		echo "LuaSandboxError: " . $e->getMessage() . "\n";
	}
}

--EXPECT--
Normal: success
User error: xp: [string ""]:1: runtime error
Error in error handler: LuaSandboxError: [string ""]:1: error in handler
Unconvertible error in error handler: LuaSandboxError: unknown error
Numeric error in error handler: LuaSandboxError: [string ""]:1: 2
Argument check error: xp: [string ""]:1: bad argument #1 to 'byte' (string expected, got no value)
Protected infinite recursion: LuaSandboxError: not enough memory
Infinite recursion in handler: LuaSandboxError: not enough memory
Protected infinite loop: LuaSandboxError: The maximum execution time for this script was exceeded
Infinite loop in handler: LuaSandboxError: The maximum execution time for this script was exceeded
Out of memory in handler: LuaSandboxError: not enough memory
tests/pairs.phpt000064400000006500150557451410007731 0ustar00--TEST--
pairs() and __pairs
--FILE--
<?php
$lua = <<<LUA
	function pairs_test1()
		local t = { a = 1 }
		local f, s, var = pairs( t )
		if type( f ) == 'function' and s == t and var == nil then
			local k, v = f(t, var)
			if k == 'a' and v == 1 then
				return "Ok"
			else
				return "Fail: First call returned " .. k .. "\\t" .. v
			end
		else
			return "Fail:\\n" ..
				tostring(f) .. '\\t' .. tostring(s) .. '\\t' .. tostring(var) .. '\\n' ..
				tostring(next) .. '\\t' .. tostring(t) .. '\\tnil'
		end
	end

	function pairs_test2()
		local t = { a = 1 }
		setmetatable( t, { __pairs = function () return 1, 2, 3 end } )
		local f, s, var = pairs( t )
		if f == 1 and s == 2 and var == 3 then
			return "Ok"
		else
			return "Fail:\\n" ..
				tostring(f) .. '\\t' .. tostring(s) .. '\\t' .. tostring(var) .. '\\n' ..
				'1\\t2\\t3'
		end
	end

	function pairs_test3()
		pairs()
		return "Fail: Should have thrown an error"
	end

	function pairs_return()
		local data = { a = 1, b = 2 }
		local t = {}
		setmetatable( t, { __pairs = function () return pairs( data ) end } )
		return t
	end

	function pairs_error()
		local t = {}
		setmetatable( t, { __pairs = function () error( "Error from __pairs function" ) end } )
		pairs( t )
		return "Fail: Should have thrown an error"
	end

	function pairs_return_error()
		local t = {}
		setmetatable( t, { __pairs = function () error( "Error from __pairs function" ) end } )
		return t
	end

	function pairs_next_error()
		local t = {}
		setmetatable( t, {
			__pairs = function ()
				return function() error( "Error from next function" ) end
			end
		} )
		for k, v in pairs( t ) do end
		return "Fail: Should have thrown an error"
	end

	function pairs_return_next_error()
		local t = {}
		setmetatable( t, {
			__pairs = function ()
				return function() error( "Error from next function" ) end
			end
		} )
		return t
	end
LUA;

$tests = array(
	'Normal' => 'pairs_test1',
	'With __pairs' => 'pairs_test2',
	'No argument' => 'pairs_test3',
	'With __pairs throwing an error' => 'pairs_error',
	'With next func throwing an error' => 'pairs_next_error',
	'Table with __pairs returned to PHP' => 'pairs_return',
	'Table with __pairs throwing an error returned to PHP' => 'pairs_return_error',
	'Table with next func throwing an error returned to PHP' => 'pairs_return_next_error',
);

foreach ( $tests as $desc => $func ) {
	echo "$desc: ";
	$sandbox = new LuaSandbox;
	$sandbox->loadString( $lua )->call();
	$sandbox->setCPULimit( 0.25 );
	$sandbox->setMemoryLimit( 100000 );
	try {
		print var_export( $sandbox->callFunction( $func ), 1 ) . "\n";
	} catch ( LuaSandboxError $e ) {
		echo "LuaSandboxError: " . $e->getMessage() . "\n";
	}
}

--EXPECT--
Normal: array (
  0 => 'Ok',
)
With __pairs: array (
  0 => 'Ok',
)
No argument: LuaSandboxError: [string ""]:32: bad argument #1 to 'pairs' (table expected, got no value)
With __pairs throwing an error: LuaSandboxError: [string ""]:45: Error from __pairs function
With next func throwing an error: LuaSandboxError: [string ""]:60: Error from next function
Table with __pairs returned to PHP: array (
  0 => 
  array (
    'a' => 1,
    'b' => 2,
  ),
)
Table with __pairs throwing an error returned to PHP: LuaSandboxError: [string ""]:52: Error from __pairs function
Table with next func throwing an error returned to PHP: LuaSandboxError: [string ""]:71: Error from next function
tests/LuaSandboxFunction_construct.phpt000064400000000375150557451410014471 0ustar00--TEST--
LuaSandboxFunction::construct() is private
--SKIPIF--
<?php if (!extension_loaded("luasandbox")) print "skip"; ?>
--FILE--
<?php
new LuaSandboxFunction;
?>
--EXPECTF--
%AFatal error:%sCall to private%S LuaSandboxFunction::__construct%S from %a