Implemented AST pretty-printer to capture expression passed to assert()

This commit is contained in:
Dmitry Stogov 2015-02-17 22:45:10 +03:00
parent 7a059b66d5
commit 4a2d9c0953
5 changed files with 1264 additions and 7 deletions

View file

@ -0,0 +1,240 @@
--TEST--
AST pretty-peinter
--INI--
zend.assertions=1
assert.exception=0
--FILE--
<?php
assert(0 && ($a = function () {
global $a, $$b;
static $c, $d = 0;
unset($e);
$x = isset($a) && !empty($b) || eval($c);
$x = $a ? $b : $c;
$x = $a ?: $c;
$x = $a ?? $b;
list($a, $b, $c) = [1, 2=>'x', 'z'=>'c'];
@foo();
$y = clone $x;
yield 1 => 2;
}));
assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X {
abstract class A extends B implements C, D {
const X = 12;
const Y = self::X, Z = "aaa";
public $a = 1, $b;
protected $c;
static private $d = null;
abstract function foo();
static private function f1() {
for ($i = 0, $j = 100; $i < $j; $i++, --$j) {
$s[$i] = $a[$j];
}
foreach ($a as $key => &$val) {
print "$key => $val\n";
}
while ($s[$i]) {
$i++;
}
do {
$i--;
} while ($s[$i]);
$x = foo($a + 1, 4, ...[1,2,3]);
$x = ${$a . "_1"}();
$x = A::foo();
$x = ${$a . "_1"}::foo();
$x = A::${$a . "_1"}();
$x = $x->foo();
$x = ${$a . "_1"}->foo();
$x = $x->{$a . "_1"}();
$x->a = C::C;
${$a . "_1"}->a = ${$a . "_1"}::C;
$x->{a . "_1"} = C::C;
$x = C::$z;
$x = ${$a . "_1"}::$z;
$x = C::${$z . "_1"};
}
}
}));
assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X {
final class A {
final protected function f2() {
if (!$x) {
return 0;
}
if ($x == 1) {
return 1;
} else if ($x == 2) {
return 2;
} else if ($x == 3) {
return 3;
} else {
if ($x == 9) {
return 9;
}
L0:
switch ($x) {
case 4: break;
case 5: continue;
case 6: break 2;
case 7: continue 2;
case 8: goto L0;
default: return;
}
}
}
}
}));
assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X {
class A {
use T1, T2 {
T1::foo insteadof foo;
T2::foo as bar;
}
use T3;
}
}));
assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X {
declare(A=1,B=2);
try {
$i++;
} catch (MyException $e) {
echo 1;
} catch (Exception $e) {
echo 2;
} finally {
echo 3;
}
}));
?>
--EXPECTF--
Warning: Unsupported declare 'A' in %sexpect_015.php on line %d
Warning: Unsupported declare 'B' in %sexpect_015.php on line %d
Warning: assert(): assert(0 && ($a = function () {
global $a;
global $$b;
static $c;
static $d = 0;
unset($e);
$x = isset($a) && !empty($b) || eval($c);
$x = $a ? $b : $c;
$x = $a ?: $c;
$x = $a ?? $b;
list($a, $b, $c) = [1, 2 => 'x', 'z' => 'c'];
@foo();
$y = clone $x;
yield 1 => 2;
})) failed in %sexpect_015.php on line %d
Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null) use($c, &$d): X {
abstract class A extends B implements C, D {
const X = 12;
const Y = self::X, Z = 'aaa';
public $a = 1, $b;
protected $c;
private static $d = null;
public abstract function foo();
private static function f1() {
for ($i = 0, $j = 100; $i < $j; $i++, --$j) {
$s[$i] = $a[$j];
}
foreach ($a as $key => & $val) {
print "$key => $val\n";
}
while ($s[$i]) {
$i++;
}
do {
$i--;
} while ($s[$i]);
$x = foo($a + 1, 4, ... [1, 2, 3]);
$x = ${$a . '_1'}();
$x = A::foo();
$x = ${$a . '_1'}::foo();
$x = A::${$a . '_1'}();
$x = $x->foo();
$x = ${$a . '_1'}->foo();
$x = $x->{$a . '_1'}();
$x->a = C::C;
${$a . '_1'}->a = ${$a . '_1'}::C;
$x->{a . '_1'} = C::C;
$x = C::$z;
$x = ${$a . '_1'}::$z;
$x = C::${$z . '_1'};
}
}
})) failed in %sexpect_015.php on line %d
Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null) use($c, &$d): X {
final class A {
protected final function f2() {
if (!$x) {
return 0;
}
if ($x == 1) {
return 1;
} else if ($x == 2) {
return 2;
} else if ($x == 3) {
return 3;
} else {
if ($x == 9) {
return 9;
}
L0:
switch ($x) {
case 4:
break;
case 5:
continue;
case 6:
break 2;
case 7:
continue 2;
case 8:
goto L0;
default:
return;
}
}
}
}
})) failed in %sexpect_015.php on line %d
Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null) use($c, &$d): X {
class A {
use T1, T2 {
T1::foo insteadof foo;
T2::foo as bar;
}
use T3;
}
})) failed in %sexpect_015.php on line %d
Warning: assert(): assert(0 && ($a = function &(array &$a, X $b = null) use($c, &$d): X {
declare(A = 1, B = 2);
try {
$i++;
} catch ('MyException''e') {
echo 1;
} catch ('Exception''e') {
echo 2;
} finally {
echo 3;
}
})) failed in %sexpect_015.php on line %d

File diff suppressed because it is too large Load diff

View file

@ -203,6 +203,7 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki
ZEND_API zend_ast *zend_ast_list_add(zend_ast *list, zend_ast *op);
ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope);
ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix);
ZEND_API zend_ast *zend_ast_copy(zend_ast *ast);
ZEND_API void zend_ast_destroy(zend_ast *ast);

View file

@ -2832,11 +2832,16 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *
opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node);
zend_alloc_cache_slot(opline->op2.constant);
if (args->children == 1) {
/* TODO: add "assert(condition) as assertion message ??? */
if (args->children == 1 &&
(args->child[0]->kind != ZEND_AST_ZVAL ||
Z_TYPE_P(zend_ast_get_zval(args->child[0])) != IS_STRING)) {
/* add "assert(condition) as assertion message */
zend_ast_list_add((zend_ast*)args,
zend_ast_create_zval_from_str(
zend_ast_export("assert(", args->child[0], ")")));
}
zend_compile_call_common(result, args, fbc);
zend_compile_call_common(result, (zend_ast*)args, fbc);
CG(active_op_array)->opcodes[check_op_number].op2.opline_num = get_next_op_number(CG(active_op_array));
}

View file

@ -188,7 +188,7 @@ warning_count = 'NULL'
Magic, magic properties:
mysqli->affected_rows = ''/NULL (''/NULL)
Warning: assert(): Assertion failed in %s on line %d
Warning: assert(): assert(@mysqli_get_client_info() === @$mysqli->client_info) failed in %s on line %d
mysqli->client_info = ''/NULL ('%s'/%s)
mysqli->client_version = '%s'/integer ('%s'/integer)
mysqli->errno = ''/NULL (''/NULL)
@ -199,7 +199,7 @@ mysqli->sqlstate = ''/NULL (''/NULL)
mysqli->host_info = ''/NULL (''/NULL)
mysqli->info = ''/NULL (''/NULL)
Warning: assert(): Assertion failed in %s on line %d
Warning: assert(): assert(@mysqli_thread_id($mysqli) > @$mysqli->thread_id) failed in %s on line %d
mysqli->thread_id = ''/NULL (''/NULL)
mysqli->protocol_version = ''/NULL (''/NULL)
mysqli->server_info = ''/NULL (''/NULL)
@ -261,7 +261,7 @@ warning_count = 'NULL'
Magic, magic properties:
mysqli->affected_rows = ''/NULL (''/NULL)
Warning: assert(): Assertion failed in %s on line %d
Warning: assert(): assert(@mysqli_get_client_info() === @$mysqli->client_info) failed in %s on line %d
mysqli->client_info = ''/NULL ('%s'/%s)
mysqli->client_version = '%s'/integer ('%s'/integer)
mysqli->errno = ''/NULL (''/NULL)
@ -272,7 +272,7 @@ mysqli->sqlstate = ''/NULL (''/NULL)
mysqli->host_info = ''/NULL (''/NULL)
mysqli->info = ''/NULL (''/NULL)
Warning: assert(): Assertion failed in %s on line %d
Warning: assert(): assert(@mysqli_thread_id($mysqli) > @$mysqli->thread_id) failed in %s on line %d
mysqli->thread_id = ''/NULL (''/NULL)
mysqli->protocol_version = ''/NULL (''/NULL)
mysqli->server_info = ''/NULL (''/NULL)