diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt new file mode 100644 index 00000000000..a2fa4861a62 --- /dev/null +++ b/Zend/tests/assert/expect_015.phpt @@ -0,0 +1,240 @@ +--TEST-- +AST pretty-peinter +--INI-- +zend.assertions=1 +assert.exception=0 +--FILE-- +'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 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 242c99ff7f5..8a4c9cf9959 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -22,6 +22,9 @@ #include "zend_ast.h" #include "zend_API.h" #include "zend_operators.h" +#include "zend_language_parser.h" +#include "zend_smart_str.h" +#include "zend_exceptions.h" ZEND_API zend_ast_process_t zend_ast_process = NULL; @@ -441,3 +444,1011 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn) { } } } + +/* + * Operator Precendence + * ==================== + * proirity associativity operatiers + * ----------------------------------- + * 10 left inclue, include_once, eval, require, require_once + * 20 left , + * 30 left or + * 40 left xor + * 50 left and + * 60 right print + * 70 right yield + * 80 right => + * 90 right = += -= *= /= .= %= &= |= ^= <<= >>= **= + * 100 left ? : + * 110 right ?? + * 120 left || + * 130 left && + * 140 left | + * 150 left ^ + * 160 left & + * 170 non-associative == != === !== + * 180 non-associative < <= > >= + * 190 left << >> + * 200 left + - . + * 210 left * / % + * 220 right ! + * 230 non-associative instanceof + * 240 right + - ++ -- ~ (type) @ + * 250 right ** + * 260 left [ + * 270 non-associative clone new + */ + +static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent); + +static void zend_ast_export_str(smart_str *str, zend_string *s) +{ + size_t i; + + for (i = 0; i < s->len; i++) { + unsigned char c = s->val[i]; + if (c == '\'' || c == '\\') { + smart_str_appendc(str, '\\'); + smart_str_appendc(str, c); + } else { + smart_str_appendc(str, c); + } + } +} + +static void zend_ast_export_dstr(smart_str *str, zend_string *s) +{ + size_t i; + + for (i = 0; i < s->len; i++) { + unsigned char c = s->val[i]; + if (c < ' ') { + switch (c) { + case '\n': + smart_str_appends(str, "\\n"); + break; + case '\r': + smart_str_appends(str, "\\r"); + break; + case '\t': + smart_str_appends(str, "\\t"); + break; + case '\f': + smart_str_appends(str, "\\f"); + break; + case '\v': + smart_str_appends(str, "\\v"); + break; +#ifdef PHP_WIN32 + case VK_ESCAPE: +#else + case '\e': +#endif + smart_str_appends(str, "\\e"); + break; + default: + smart_str_appends(str, "\\0"); + smart_str_appendc(str, '0' + (c / 8)); + smart_str_appendc(str, '0' + (c % 8)); + break; + } + } else { + if (c == '"' || c == '`' || c == '$' || c == '\\') { + smart_str_appendc(str, '\\'); + } + smart_str_appendc(str, c); + } + } +} + +static void zend_ast_export_indent(smart_str *str, int indent) +{ + while (indent > 0) { + smart_str_appendc(str, '\t'); + indent--; + } +} + +static void zend_ast_export_name(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + + if (Z_TYPE_P(zv) == IS_STRING) { + smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + return; + } + } + zend_ast_export_ex(str, ast, priority, indent); +} + +static void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent) +{ + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING) { + smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + return; + } + } else if (ast->kind == ZEND_AST_VAR) { + zend_ast_export_ex(str, ast, 0, indent); + return; + } + smart_str_appendc(str, '{'); + zend_ast_export_name(str, ast, 0, indent); + smart_str_appendc(str, '}'); +} + +static void zend_ast_export_list(smart_str *str, zend_ast_list *list, int separator, int priority, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0 && separator) { + smart_str_appends(str, ", "); + } + zend_ast_export_ex(str, list->child[i], priority, indent); + i++; + } +} + +static void zend_ast_export_encaps_list(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + zend_ast *ast; + + while (i < list->children) { + ast = list->child[i]; + if (ast->kind == ZEND_AST_ZVAL) { + zval *zv = zend_ast_get_zval(ast); + + if (Z_TYPE_P(zv) == IS_STRING) { + zend_ast_export_dstr(str, Z_STR_P(zv)); + } else { + zend_ast_export_ex(str, ast, 0, indent); + } + } else if (ast->kind == ZEND_AST_VAR && ast->child[0]->kind == ZEND_AST_ZVAL) { + zend_ast_export_ex(str, ast, 0, indent); + } else { + smart_str_appends(str, "${"); + zend_ast_export_ex(str, ast, 0, indent); + smart_str_appendc(str, '}'); + } + i++; + } +} + +static void zend_ast_export_name_list(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0) { + smart_str_appends(str, ", "); + } + zend_ast_export_name(str, list->child[i], 0, indent); + i++; + } +} + +static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i = 0; + + while (i < list->children) { + if (i != 0) { + smart_str_appends(str, ", "); + } + if (list->child[i]->attr) { + smart_str_appendc(str, '&'); + } + smart_str_appendc(str, '$'); + zend_ast_export_name(str, list->child[i], 20, indent); + i++; + } +} + +static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) +{ + if (ast->kind == ZEND_AST_STMT_LIST || + ast->kind == ZEND_AST_TRAIT_ADAPTATIONS) { + zend_ast_list *list = (zend_ast_list*)ast; + uint32_t i = 0; + + while (i < list->children) { + ast = list->child[i]; + zend_ast_export_stmt(str, ast, indent); + i++; + } + } else { + zend_ast_export_indent(str, indent); + zend_ast_export_ex(str, ast, 0, indent); + switch (ast->kind) { + case ZEND_AST_LABEL: + case ZEND_AST_IF: + case ZEND_AST_SWITCH: + case ZEND_AST_WHILE: + case ZEND_AST_TRY: + case ZEND_AST_FOR: + case ZEND_AST_FOREACH: + case ZEND_AST_FUNC_DECL: + case ZEND_AST_METHOD: + case ZEND_AST_CLASS: + case ZEND_AST_USE_TRAIT: + case ZEND_AST_NAMESPACE: + break; + default: + smart_str_appendc(str, ';'); + break; + } + smart_str_appendc(str, '\n'); + } +} + +static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int indent) +{ + zend_long idx; + zend_string *key; + zval *val; + int first; + + ZVAL_DEREF(zv); + switch (Z_TYPE_P(zv)) { + case IS_NULL: + smart_str_appends(str, "null"); + break; + case IS_FALSE: + smart_str_appends(str, "false"); + break; + case IS_TRUE: + smart_str_appends(str, "true"); + break; + case IS_LONG: + smart_str_append_long(str, Z_LVAL_P(zv)); + break; + case IS_DOUBLE: + key = zend_strpprintf(0, "%.*G", (int) EG(precision), Z_DVAL_P(zv)); + smart_str_appendl(str, key->val, key->len); + zend_string_release(key); + break; + case IS_STRING: + smart_str_appendc(str, '\''); + zend_ast_export_str(str, Z_STR_P(zv)); + smart_str_appendc(str, '\''); + break; + case IS_ARRAY: + smart_str_appendc(str, '['); + first = 1; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zv), idx, key, val) { + if (first) { + first = 0; + } else { + smart_str_appends(str, ", "); + } + if (key) { + smart_str_appendc(str, '\''); + zend_ast_export_str(str, key); + smart_str_appends(str, "' => "); + } else { + smart_str_append_long(str, idx); + smart_str_appends(str, " => "); + } + zend_ast_export_zval(str, val, 0, indent); + } ZEND_HASH_FOREACH_END(); + smart_str_appendc(str, ']'); + break; + case IS_CONSTANT: + smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + break; + case IS_CONSTANT_AST: + zend_ast_export_ex(str, Z_ASTVAL_P(zv), priority, indent); + break; + default: + ZEND_ASSERT(0); + } +} + +#define BINARY_OP(_op, _p, _pl, _pr) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + pr = _pr; \ + goto binary_op; \ + } while (0) + +#define PREFIX_OP(_op, _p, _pl) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + goto prefix_op; \ + } while (0) + +#define FUNC_OP(_op) do { \ + op = _op; \ + goto func_op; \ + } while (0) + +#define POSTFIX_OP(_op, _p, _pl) do { \ + op = _op; \ + p = _p; \ + pl = _pl; \ + goto postfix_op; \ + } while (0) + +#define APPEND_NODE_1(_op) do { \ + op = _op; \ + goto append_node_1; \ + } while (0) + +#define APPEND_STR(_op) do { \ + op = _op; \ + goto append_str; \ + } while (0) + +#define APPEND_DEFAULT_VALUE(n) do { \ + p = n; \ + goto append_default_value; \ + } while (0) + +static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int indent) +{ + zval *zv; + zend_ast_decl *decl; + int p, pl, pr; + const char *op; + +tail_call: + if (!ast) { + return; + } + switch (ast->kind) { + /* special nodes */ + case ZEND_AST_ZVAL: + zend_ast_export_zval(str, zend_ast_get_zval(ast), priority, indent); + break; +//??? case ZEND_AST_ZNODE: + + /* declaration nodes */ + case ZEND_AST_FUNC_DECL: + case ZEND_AST_CLOSURE: + case ZEND_AST_METHOD: + decl = (zend_ast_decl *) ast; + if (decl->flags & ZEND_ACC_PUBLIC) { + smart_str_appends(str, "public "); + } else if (decl->flags & ZEND_ACC_PROTECTED) { + smart_str_appends(str, "protected "); + } else if (decl->flags & ZEND_ACC_PRIVATE) { + smart_str_appends(str, "private "); + } + if (decl->flags & ZEND_ACC_STATIC) { + smart_str_appends(str, "static "); + } + if (decl->flags & ZEND_ACC_ABSTRACT) { + smart_str_appends(str, "abstract "); + } + if (decl->flags & ZEND_ACC_FINAL) { + smart_str_appends(str, "final "); + } + smart_str_appends(str, "function "); + if (decl->flags & ZEND_ACC_RETURN_REFERENCE) { + smart_str_appendc(str, '&'); + } + if (ast->kind != ZEND_AST_CLOSURE) { + smart_str_appendl(str, decl->name->val, decl->name->len); + } + smart_str_appendc(str, '('); + zend_ast_export_ex(str, decl->child[0], 0, indent); + smart_str_appendc(str, ')'); + zend_ast_export_ex(str, decl->child[1], 0, indent); + if (decl->child[3]) { + smart_str_appends(str, ": "); + zend_ast_export_name(str, decl->child[3], 0, indent); + } + if (decl->child[2]) { + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, decl->child[2], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + if (ast->kind != ZEND_AST_CLOSURE) { + smart_str_appendc(str, '\n'); + } + } else { + smart_str_appends(str, ";\n"); + } + break; + case ZEND_AST_CLASS: + decl = (zend_ast_decl *) ast; + if (decl->flags & ZEND_ACC_INTERFACE) { + smart_str_appends(str, "interface "); + } else if (decl->flags & ZEND_ACC_TRAIT) { + smart_str_appends(str, "trait "); + } else { + if (decl->flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) { + smart_str_appends(str, "abstract "); + } + if (decl->flags & ZEND_ACC_FINAL) { + smart_str_appends(str, "final "); + } + smart_str_appends(str, "class "); + } + smart_str_appendl(str, decl->name->val, decl->name->len); + if (decl->child[0]) { + smart_str_appends(str, " extends "); + zend_ast_export_name(str, decl->child[0], 0, indent); + } + if (decl->child[1]) { + smart_str_appends(str, " implements "); + zend_ast_export_ex(str, decl->child[1], 0, indent); + } + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, decl->child[2], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}\n"); + break; + + /* list nodes */ + case ZEND_AST_ARG_LIST: + case ZEND_AST_EXPR_LIST: + case ZEND_AST_PARAM_LIST: +simple_list: + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + break; + case ZEND_AST_LIST: + smart_str_appends(str, "list("); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_ARRAY: + smart_str_appendc(str, '['); + zend_ast_export_list(str, (zend_ast_list*)ast, 1, 20, indent); + smart_str_appendc(str, ']'); + break; + case ZEND_AST_ENCAPS_LIST: + smart_str_appendc(str, '"'); + zend_ast_export_encaps_list(str, (zend_ast_list*)ast, indent); + smart_str_appendc(str, '"'); + break; + case ZEND_AST_STMT_LIST: + case ZEND_AST_TRAIT_ADAPTATIONS: + zend_ast_export_stmt(str, (zend_ast_list*)ast, indent); + break; + case ZEND_AST_IF: + case ZEND_AST_SWITCH_LIST: + case ZEND_AST_CATCH_LIST: + zend_ast_export_list(str, (zend_ast_list*)ast, 0, 0, indent); + break; + case ZEND_AST_CLOSURE_USES: + smart_str_appends(str, " use("); + zend_ast_export_var_list(str, (zend_ast_list*)ast, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_PROP_DECL: + if (ast->attr & ZEND_ACC_PUBLIC) { + smart_str_appends(str, "public "); + } else if (ast->attr & ZEND_ACC_PROTECTED) { + smart_str_appends(str, "protected "); + } else if (ast->attr & ZEND_ACC_PRIVATE) { + smart_str_appends(str, "private "); + } + if (ast->attr & ZEND_ACC_STATIC) { + smart_str_appends(str, "static "); + } + goto simple_list; + case ZEND_AST_CONST_DECL: + case ZEND_AST_CLASS_CONST_DECL: + smart_str_appends(str, "const "); + goto simple_list; + case ZEND_AST_NAME_LIST: + zend_ast_export_name_list(str, (zend_ast_list*)ast, indent); + break; + case ZEND_AST_USE: + smart_str_appends(str, "use "); + goto simple_list; + + /* 0 child nodes */ + case ZEND_AST_MAGIC_CONST: + switch (ast->attr) { + case T_LINE: APPEND_STR("__LINE__"); + case T_FILE: APPEND_STR("__FILE__"); + case T_DIR: APPEND_STR("__DIR__"); + case T_TRAIT_C: APPEND_STR("__TRAIT__"); + case T_METHOD_C: APPEND_STR("__METHOD__"); + case T_FUNC_C: APPEND_STR("__FUNCTION__"); + case T_NS_C: APPEND_STR("__NAMESPACE__"); + case T_CLASS_C: APPEND_STR("__CLASS__"); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_TYPE: + switch (ast->attr) { + case IS_ARRAY: APPEND_STR("array"); + case IS_CALLABLE: APPEND_STR("callable"); + default: ZEND_ASSERT(0); + } + break; + + /* 1 child node */ + case ZEND_AST_VAR: + smart_str_appendc(str, '$'); + zend_ast_export_var(str, ast->child[0], 0, indent); + break; + case ZEND_AST_CONST: + zv = zend_ast_get_zval(ast->child[0]); + smart_str_appendl(str, Z_STRVAL_P(zv), Z_STRLEN_P(zv)); + break; + case ZEND_AST_RESOLVE_CLASS_NAME: + zend_ast_export_name(str, ast->child[0], 0, indent); + APPEND_STR("::class"); + case ZEND_AST_UNPACK: + APPEND_NODE_1("..."); + case ZEND_AST_UNARY_PLUS: PREFIX_OP("+", 240, 241); + case ZEND_AST_UNARY_MINUS: PREFIX_OP("-", 240, 241); + case ZEND_AST_CAST: + switch (ast->attr) { + case IS_NULL: PREFIX_OP("(unset)", 240, 241); + case _IS_BOOL: PREFIX_OP("(bool)", 240, 241); + case IS_LONG: PREFIX_OP("(int)", 240, 241); + case IS_DOUBLE: PREFIX_OP("(double)", 240, 241); + case IS_STRING: PREFIX_OP("(string)", 240, 241); + case IS_ARRAY: PREFIX_OP("(array)", 240, 241); + case IS_OBJECT: PREFIX_OP("(object)", 240, 241); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_EMPTY: + FUNC_OP("empty"); + case ZEND_AST_ISSET: + FUNC_OP("isset"); + case ZEND_AST_SILENCE: + PREFIX_OP("@", 240, 241); + case ZEND_AST_SHELL_EXEC: + smart_str_appendc(str, '`'); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appendc(str, '`'); + break; + case ZEND_AST_CLONE: + PREFIX_OP("clone ", 270, 271); + case ZEND_AST_EXIT: + if (ast->child[0]) { + FUNC_OP("exit"); + } else { + APPEND_STR("exit"); + } + break; + case ZEND_AST_PRINT: + PREFIX_OP("print ", 60, 61); + case ZEND_AST_INCLUDE_OR_EVAL: + switch (ast->attr) { + case ZEND_INCLUDE_ONCE: FUNC_OP("include_once"); + case ZEND_INCLUDE: FUNC_OP("include"); + case ZEND_REQUIRE_ONCE: FUNC_OP("require_once"); + case ZEND_REQUIRE: FUNC_OP("require"); + case ZEND_EVAL: FUNC_OP("eval"); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_UNARY_OP: + switch (ast->attr) { + case ZEND_BW_NOT: PREFIX_OP("~", 240, 241); + case ZEND_BOOL_NOT: PREFIX_OP("!", 240, 241); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_PRE_INC: + PREFIX_OP("++", 240, 241); + case ZEND_AST_PRE_DEC: + PREFIX_OP("--", 240, 241); + case ZEND_AST_POST_INC: + POSTFIX_OP("++", 240, 241); + case ZEND_AST_POST_DEC: + POSTFIX_OP("--", 240, 241); + + case ZEND_AST_GLOBAL: + APPEND_NODE_1("global "); + case ZEND_AST_UNSET: + FUNC_OP("unset"); + case ZEND_AST_RETURN: + APPEND_NODE_1("return"); + case ZEND_AST_LABEL: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, ':'); + break; + case ZEND_AST_REF: + APPEND_NODE_1("&"); + case ZEND_AST_HALT_COMPILER: + APPEND_STR("__HALT_COMPILER()"); + case ZEND_AST_ECHO: + APPEND_NODE_1("echo "); + case ZEND_AST_THROW: + APPEND_NODE_1("throw "); + case ZEND_AST_GOTO: + smart_str_appends(str, "goto "); + zend_ast_export_name(str, ast->child[0], 0, indent); + break; + case ZEND_AST_BREAK: + APPEND_NODE_1("break"); + case ZEND_AST_CONTINUE: + APPEND_NODE_1("continue"); + + /* 2 child nodes */ + case ZEND_AST_DIM: + zend_ast_export_ex(str, ast->child[0], 260, indent); + smart_str_appendc(str, '['); + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 0, indent); + } + smart_str_appendc(str, ']'); + break; + case ZEND_AST_PROP: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, "->"); + zend_ast_export_var(str, ast->child[1], 0, indent); + break; + case ZEND_AST_STATIC_PROP: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::$"); + zend_ast_export_var(str, ast->child[1], 0, indent); + break; + case ZEND_AST_CALL: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_CLASS_CONST: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + zend_ast_export_name(str, ast->child[1], 0, indent); + break; + case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90); + case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90); + case ZEND_AST_ASSIGN_OP: + switch (ast->attr) { + case ZEND_ASSIGN_ADD: BINARY_OP(" += ", 90, 91, 90); + case ZEND_ASSIGN_SUB: BINARY_OP(" -= ", 90, 91, 90); + case ZEND_ASSIGN_MUL: BINARY_OP(" *= ", 90, 91, 90); + case ZEND_ASSIGN_DIV: BINARY_OP(" /= ", 90, 91, 90); + case ZEND_ASSIGN_MOD: BINARY_OP(" %= ", 90, 91, 90); + case ZEND_ASSIGN_SL: BINARY_OP(" <<= ", 90, 91, 90); + case ZEND_ASSIGN_SR: BINARY_OP(" >>= ", 90, 91, 90); + case ZEND_ASSIGN_CONCAT: BINARY_OP(" .= ", 90, 91, 90); + case ZEND_ASSIGN_BW_OR: BINARY_OP(" |= ", 90, 91, 90); + case ZEND_ASSIGN_BW_AND: BINARY_OP(" &= ", 90, 91, 90); + case ZEND_ASSIGN_BW_XOR: BINARY_OP(" ^= ", 90, 91, 90); + case ZEND_POW: BINARY_OP(" **= ", 90, 91, 90); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_BINARY_OP: + switch (ast->attr) { + case ZEND_ADD: BINARY_OP(" + ", 200, 200, 201); + case ZEND_SUB: BINARY_OP(" - ", 200, 200, 201); + case ZEND_MUL: BINARY_OP(" * ", 210, 210, 211); + case ZEND_DIV: BINARY_OP(" / ", 210, 210, 211); + case ZEND_MOD: BINARY_OP(" % ", 210, 210, 211); + case ZEND_SL: BINARY_OP(" << ", 190, 190, 191); + case ZEND_SR: BINARY_OP(" >> ", 190, 190, 191); + case ZEND_CONCAT: BINARY_OP(" . ", 200, 200, 201); + case ZEND_BW_OR: BINARY_OP(" | ", 140, 140, 141); + case ZEND_BW_AND: BINARY_OP(" & ", 160, 160, 161); + case ZEND_BW_XOR: BINARY_OP(" ^ ", 150, 150, 151); + case ZEND_IS_IDENTICAL: BINARY_OP(" === ", 170, 171, 171); + case ZEND_IS_NOT_IDENTICAL: BINARY_OP(" !== ", 170, 171, 171); + case ZEND_IS_EQUAL: BINARY_OP(" == ", 170, 171, 171); + case ZEND_IS_NOT_EQUAL: BINARY_OP(" != ", 170, 171, 171); + case ZEND_IS_SMALLER: BINARY_OP(" < ", 180, 181, 181); + case ZEND_IS_SMALLER_OR_EQUAL: BINARY_OP(" <= ", 180, 181, 181); + case ZEND_POW: BINARY_OP(" ** ", 250, 251, 250); + case ZEND_BOOL_XOR: BINARY_OP(" xor ", 40, 40, 41); + default: ZEND_ASSERT(0); + } + break; + case ZEND_AST_GREATER: BINARY_OP(" > ", 180, 181, 181); + case ZEND_AST_GREATER_EQUAL: BINARY_OP(" >= ", 180, 181, 181); + case ZEND_AST_AND: BINARY_OP(" && ", 130, 130, 131); + case ZEND_AST_OR: BINARY_OP(" || ", 120, 120, 121); + case ZEND_AST_ARRAY_ELEM: + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 80, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[0], 80, indent); + break; + case ZEND_AST_NEW: + smart_str_appends(str, "new "); + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_INSTANCEOF: BINARY_OP(" instanceof ", 230, 231, 231); + case ZEND_AST_YIELD: + if (priority > 70) smart_str_appendc(str, '('); + smart_str_appends(str, "yield "); + if (ast->child[0]) { + if (ast->child[1]) { + zend_ast_export_ex(str, ast->child[1], 70, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[0], 70, indent); + } + if (priority > 70) smart_str_appendc(str, ')'); + break; + case ZEND_AST_COALESCE: BINARY_OP(" ?? ", 110, 111, 110); + case ZEND_AST_STATIC: + smart_str_appends(str, "static $"); + zend_ast_export_name(str, ast->child[0], 0, indent); + APPEND_DEFAULT_VALUE(1); + case ZEND_AST_WHILE: + smart_str_appends(str, "while ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_DO_WHILE: + smart_str_appends(str, "do {\n"); + zend_ast_export_stmt(str, ast->child[0], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} while ("); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_IF_ELEM: + if (ast->child[0]) { + smart_str_appends(str, "if ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") "); + } else { + smart_str_appends(str, " else "); + } + if (ast->child[1]->kind == ZEND_AST_IF) { + zend_ast_export_list(str, (zend_ast_list*)ast->child[1], 0, 0, indent); + } else { + smart_str_appends(str, "{\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + } + break; + case ZEND_AST_SWITCH: + smart_str_appends(str, "switch ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_ex(str, ast->child[1], 0, indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_SWITCH_CASE: + zend_ast_export_indent(str, indent); + if (ast->child[0]) { + smart_str_appends(str, "case "); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, ":\n"); + } else { + smart_str_appends(str, "default:\n"); + } + zend_ast_export_stmt(str, ast->child[1], indent + 1); + break; + case ZEND_AST_DECLARE: + smart_str_appends(str, "declare("); + if (ast->child[0]->kind == ZEND_AST_CONST_DECL) { + zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent); + } else { + zend_ast_export_ex(str, ast->child[0], 0, indent); + } + if (ast->child[1]) { + smart_str_appends(str, " = "); + zend_ast_export_ex(str, ast->child[1], 0, indent); + } + smart_str_appendc(str, ')'); + break; + case ZEND_AST_PROP_ELEM: + smart_str_appendc(str, '$'); + case ZEND_AST_CONST_ELEM: + zend_ast_export_name(str, ast->child[0], 0, indent); + APPEND_DEFAULT_VALUE(1); + case ZEND_AST_USE_TRAIT: + smart_str_appends(str, "use "); + zend_ast_export_ex(str, ast->child[0], 0, indent); + if (ast->child[1]) { + smart_str_appends(str, " {\n"); + zend_ast_export_ex(str, ast->child[1], 0, indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}"); + } else { + smart_str_appends(str, ";"); + } + break; + case ZEND_AST_TRAIT_PRECEDENCE: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, " insteadof "); + zend_ast_export_ex(str, ast->child[1], 0, indent); + break; + case ZEND_AST_METHOD_REFERENCE: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + zend_ast_export_name(str, ast->child[1], 0, indent); + break; + case ZEND_AST_NAMESPACE: + smart_str_appends(str, "namespace"); + if (ast->child[0]) { + smart_str_appendc(str, ' '); + zend_ast_export_name(str, ast->child[0], 0, indent); + } + if (ast->child[1]) { + smart_str_appends(str, " {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appends(str, "}\n"); + } else { + smart_str_appendc(str, ';'); + } + break; + case ZEND_AST_USE_ELEM: + case ZEND_AST_TRAIT_ALIAS: + zend_ast_export_name(str, ast->child[0], 0, indent); + if (ast->child[1]) { + smart_str_appends(str, " as "); + zend_ast_export_name(str, ast->child[1], 0, indent); + } + break; + + /* 3 child nodes */ + case ZEND_AST_METHOD_CALL: + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, "->"); + zend_ast_export_var(str, ast->child[1], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_STATIC_CALL: + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::"); + zend_ast_export_var(str, ast->child[1], 0, indent); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appendc(str, ')'); + break; + case ZEND_AST_CONDITIONAL: + if (priority > 100) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], 100, indent); + if (ast->child[1]) { + smart_str_appends(str, " ? "); + zend_ast_export_ex(str, ast->child[1], 101, indent); + smart_str_appends(str, " : "); + } else { + smart_str_appends(str, " ?: "); + } + zend_ast_export_ex(str, ast->child[2], 101, indent); + if (priority > 100) smart_str_appendc(str, ')'); + break; + + case ZEND_AST_TRY: + smart_str_appends(str, "try {\n"); + zend_ast_export_stmt(str, ast->child[0], indent + 1); + zend_ast_export_indent(str, indent); + zend_ast_export_ex(str, ast->child[1], 0, indent); + if (ast->child[2]) { + smart_str_appends(str, "} finally {\n"); + zend_ast_export_stmt(str, ast->child[2], indent + 1); + zend_ast_export_indent(str, indent); + } + smart_str_appendc(str, '}'); + break; + case ZEND_AST_CATCH: + smart_str_appends(str, "} catch ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[2], indent + 1); + zend_ast_export_indent(str, indent); + break; + case ZEND_AST_PARAM: + if (ast->child[0]) { + zend_ast_export_name(str, ast->child[0], 0, indent); + smart_str_appendc(str, ' '); + } + if (ast->attr) { + smart_str_appendc(str, '&'); + } + smart_str_appendc(str, '$'); + zend_ast_export_name(str, ast->child[1], 0, indent); + APPEND_DEFAULT_VALUE(2); + + /* 4 child nodes */ + case ZEND_AST_FOR: + smart_str_appends(str, "for ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, "; "); + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appends(str, "; "); + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[3], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + case ZEND_AST_FOREACH: + smart_str_appends(str, "foreach ("); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appends(str, " as "); + if (ast->child[2]) { + zend_ast_export_ex(str, ast->child[2], 0, indent); + smart_str_appends(str, " => "); + } + zend_ast_export_ex(str, ast->child[1], 0, indent); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[3], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + break; + default: + ZEND_ASSERT(0); + } + return; + +binary_op: + if (priority > p) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], pl, indent); + smart_str_appends(str, op); + zend_ast_export_ex(str, ast->child[1], pr, indent); + if (priority > p) smart_str_appendc(str, ')'); + return; + +prefix_op: + if (priority > p) smart_str_appendc(str, '('); + smart_str_appends(str, op); + zend_ast_export_ex(str, ast->child[0], pl, indent); + if (priority > p) smart_str_appendc(str, ')'); + return; + +postfix_op: + if (priority > p) smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], pl, indent); + smart_str_appends(str, op); + if (priority > p) smart_str_appendc(str, ')'); + return; + +func_op: + smart_str_appends(str, op); + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast->child[0], 0, indent); + smart_str_appendc(str, ')'); + return; + +append_node_1: + smart_str_appends(str, op); + if (ast->child[0]) { + smart_str_appendc(str, ' '); + ast = ast->child[0]; + goto tail_call; + } + return; + +append_str: + smart_str_appends(str, op); + return; + +append_default_value: + if (ast->child[p]) { + smart_str_appends(str, " = "); + ast = ast->child[p]; + goto tail_call; + } + return; +} + +ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix) +{ + smart_str str = {0}; + + smart_str_appends(&str, prefix); + zend_ast_export_ex(&str, ast, 0, 0); + smart_str_appends(&str, suffix); + smart_str_0(&str); + return str.s; +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index a9d58428ec9..bc26ea2168a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -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); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e80c7ccc657..68dfe05ddac 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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)); } diff --git a/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt b/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt index e4d0b08a85e..7e36c6571e4 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_properties_no_conn.phpt @@ -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)