From 3892eba2bf56a7699453855c995404106322718d Mon Sep 17 00:00:00 2001 From: krakjoe Date: Sun, 2 Feb 2014 12:49:35 +0000 Subject: [PATCH 1/7] import expect --- Zend/tests/assert/expect_001.phpt | 12 +++ Zend/tests/assert/expect_002.phpt | 16 ++++ Zend/tests/assert/expect_003.phpt | 15 ++++ Zend/tests/assert/expect_004.phpt | 15 ++++ Zend/tests/assert/expect_005.phpt | 17 ++++ Zend/tests/assert/expect_006.phpt | 14 +++ Zend/tests/assert/expect_007.phpt | 22 +++++ Zend/tests/assert/expect_008.phpt | 34 +++++++ Zend/tests/assert/expect_009.phpt | 26 ++++++ Zend/tests/assert/expect_010.phpt | 23 +++++ Zend/tests/assert/expect_011.phpt | 30 +++++++ Zend/tests/assert/expect_012.phpt | 21 +++++ Zend/tests/assert/expect_013.phpt | 11 +++ Zend/tests/assert/expect_014.phpt | 12 +++ Zend/zend.c | 1 + Zend/zend_compile.c | 88 ++++++++++++++++++- Zend/zend_execute_API.c | 1 + Zend/zend_globals.h | 1 + Zend/zend_opcode.c | 1 + Zend/zend_vm_def.h | 20 +++++ Zend/zend_vm_execute.h | 45 ++++++++++ Zend/zend_vm_opcodes.c | 3 +- Zend/zend_vm_opcodes.h | 1 + ext/opcache/Optimizer/block_pass.c | 12 +++ ext/opcache/Optimizer/nop_removal.c | 3 + ext/opcache/Optimizer/pass1_5.c | 3 + ext/opcache/Optimizer/zend_optimizer.c | 6 ++ ext/opcache/zend_persist.c | 3 + ...flectionExtension_getClassNames_basic.phpt | 4 +- ext/standard/assert.c | 79 ++++++++++++----- ext/standard/tests/assert/assert04.phpt | 4 +- ext/standard/tests/assert/assert_basic2.phpt | 4 +- ext/standard/tests/assert/assert_basic3.phpt | 2 +- ext/standard/tests/assert/assert_basic5.phpt | 2 +- .../tests/assert/assert_closures.phpt | 2 +- ext/standard/tests/assert/assert_error2.phpt | 5 +- ext/standard/tests/assert/assert_error3.phpt | 7 +- 37 files changed, 526 insertions(+), 39 deletions(-) create mode 100644 Zend/tests/assert/expect_001.phpt create mode 100644 Zend/tests/assert/expect_002.phpt create mode 100644 Zend/tests/assert/expect_003.phpt create mode 100644 Zend/tests/assert/expect_004.phpt create mode 100644 Zend/tests/assert/expect_005.phpt create mode 100644 Zend/tests/assert/expect_006.phpt create mode 100644 Zend/tests/assert/expect_007.phpt create mode 100644 Zend/tests/assert/expect_008.phpt create mode 100644 Zend/tests/assert/expect_009.phpt create mode 100644 Zend/tests/assert/expect_010.phpt create mode 100644 Zend/tests/assert/expect_011.phpt create mode 100644 Zend/tests/assert/expect_012.phpt create mode 100644 Zend/tests/assert/expect_013.phpt create mode 100644 Zend/tests/assert/expect_014.phpt diff --git a/Zend/tests/assert/expect_001.phpt b/Zend/tests/assert/expect_001.phpt new file mode 100644 index 0000000000000..6c78a0bd755db --- /dev/null +++ b/Zend/tests/assert/expect_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +test passing assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/Zend/tests/assert/expect_002.phpt b/Zend/tests/assert/expect_002.phpt new file mode 100644 index 0000000000000..bdb5e1969ae9a --- /dev/null +++ b/Zend/tests/assert/expect_002.phpt @@ -0,0 +1,16 @@ +--TEST-- +test failing assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_002.php:%d +Stack trace: +#0 %sexpect_002.php(%d): assert(false, 'assert(false)') +#1 {main} + thrown in %sexpect_002.php on line %d diff --git a/Zend/tests/assert/expect_003.phpt b/Zend/tests/assert/expect_003.phpt new file mode 100644 index 0000000000000..e2c84edb11748 --- /dev/null +++ b/Zend/tests/assert/expect_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +test catching failed assertion +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +string(13) "assert(false)" diff --git a/Zend/tests/assert/expect_004.phpt b/Zend/tests/assert/expect_004.phpt new file mode 100644 index 0000000000000..1f16adb3c68c5 --- /dev/null +++ b/Zend/tests/assert/expect_004.phpt @@ -0,0 +1,15 @@ +--TEST-- +test providing reason (fail) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +string(25) "I require this to succeed" diff --git a/Zend/tests/assert/expect_005.phpt b/Zend/tests/assert/expect_005.phpt new file mode 100644 index 0000000000000..fab983b0e6dc6 --- /dev/null +++ b/Zend/tests/assert/expect_005.phpt @@ -0,0 +1,17 @@ +--TEST-- +test providing reason (pass) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage()); +} +var_dump(true); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_006.phpt b/Zend/tests/assert/expect_006.phpt new file mode 100644 index 0000000000000..8c88aeb923e3c --- /dev/null +++ b/Zend/tests/assert/expect_006.phpt @@ -0,0 +1,14 @@ +--TEST-- +test looping assert (pass) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_007.phpt b/Zend/tests/assert/expect_007.phpt new file mode 100644 index 0000000000000..c9c7b5fc98c06 --- /dev/null +++ b/Zend/tests/assert/expect_007.phpt @@ -0,0 +1,22 @@ +--TEST-- +test compiled reason +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + "X-HTTP ", + "value" => "testing" +); + +class HeaderMalfunctionException extends AssertionException {} + +assert (preg_match("~^([a-zA-Z0-9-]+)$~", $data["key"]), new HeaderMalfunctionException("malformed key found at {$next} \"{$data["key"]}\"")); +?> +--EXPECTF-- +Fatal error: Uncaught exception 'HeaderMalfunctionException' with message 'malformed key found at 1 "X-HTTP "' in %sexpect_007.php:10 +Stack trace: +#0 {main} + thrown in %sexpect_007.php on line 10 diff --git a/Zend/tests/assert/expect_008.phpt b/Zend/tests/assert/expect_008.phpt new file mode 100644 index 0000000000000..67779bec7f39c --- /dev/null +++ b/Zend/tests/assert/expect_008.phpt @@ -0,0 +1,34 @@ +--TEST-- +test disabled expectations have no ill side effects +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) diff --git a/Zend/tests/assert/expect_009.phpt b/Zend/tests/assert/expect_009.phpt new file mode 100644 index 0000000000000..3d9f679e6f95b --- /dev/null +++ b/Zend/tests/assert/expect_009.phpt @@ -0,0 +1,26 @@ +--TEST-- +test stack trace is correct from failed exception in extended class +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_009.php:%d +Stack trace: +#0 %sexpect_009.php(%d): assert(false, 'assert(false)') +#1 %sexpect_009.php(%d): Two->__construct() +#2 {main} + thrown in %sexpect_009.php on line %d diff --git a/Zend/tests/assert/expect_010.phpt b/Zend/tests/assert/expect_010.phpt new file mode 100644 index 0000000000000..f942f6d6f01b5 --- /dev/null +++ b/Zend/tests/assert/expect_010.phpt @@ -0,0 +1,23 @@ +--TEST-- +test stack trace is correct from failed exception in extended class (parent implementing constructor) +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message 'assert(false)' in %sexpect_010.php:%d +Stack trace: +#0 %sexpect_010.php(%d): assert(false, 'assert(false)') +#1 %sexpect_010.php(%d): One->__construct() +#2 {main} + thrown in %sexpect_010.php on line %d diff --git a/Zend/tests/assert/expect_011.phpt b/Zend/tests/assert/expect_011.phpt new file mode 100644 index 0000000000000..23c21aaa686dc --- /dev/null +++ b/Zend/tests/assert/expect_011.phpt @@ -0,0 +1,30 @@ +--TEST-- +test overloaded __toString on custom exception +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'AssertionException' with message '[Message]: MyExpectations' in %sexpect_011.php:%d +Stack trace: +#0 %sexpect_011.php(%d): assert(false, '[Message]: MyEx...') +#1 %sexpect_011.php(%d): One->__construct() +#2 {main} + thrown in %sexpect_011.php on line %d diff --git a/Zend/tests/assert/expect_012.phpt b/Zend/tests/assert/expect_012.phpt new file mode 100644 index 0000000000000..c1ae817f62d3e --- /dev/null +++ b/Zend/tests/assert/expect_012.phpt @@ -0,0 +1,21 @@ +--TEST-- +test enable/disable assertions at runtime +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- + +--EXPECT-- +int(1) +int(0) +int(1) +bool(true) diff --git a/Zend/tests/assert/expect_013.phpt b/Zend/tests/assert/expect_013.phpt new file mode 100644 index 0000000000000..cf8fa1cd02bd0 --- /dev/null +++ b/Zend/tests/assert/expect_013.phpt @@ -0,0 +1,11 @@ +--TEST-- +test failing assertion when disabled (with return value) +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/Zend/tests/assert/expect_014.phpt b/Zend/tests/assert/expect_014.phpt new file mode 100644 index 0000000000000..7cf4fb193112e --- /dev/null +++ b/Zend/tests/assert/expect_014.phpt @@ -0,0 +1,12 @@ +--TEST-- +test failing assertion when disabled +--INI-- +zend.assertions=0 +assert.exception=1 +--FILE-- + +--EXPECT-- +bool(true) \ No newline at end of file diff --git a/Zend/zend.c b/Zend/zend.c index acbbcbebc7c1e..e5f43243363dc 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -101,6 +101,7 @@ static ZEND_INI_MH(OnUpdateScriptEncoding) /* {{{ */ ZEND_INI_BEGIN() ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting) + STD_ZEND_INI_BOOLEAN("zend.assertions", "1", ZEND_INI_SYSTEM, OnUpdateBool, assertions, zend_executor_globals, executor_globals) STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals) STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals) ZEND_INI_ENTRY("zend.script_encoding", NULL, ZEND_INI_ALL, OnUpdateScriptEncoding) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f789e3397f25c..01579f637d3d9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1969,6 +1969,22 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace } lcname = zend_str_tolower_dup(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name->u.constant)); + + if (Z_STRLEN(function_name->u.constant) == sizeof("assert")-1 && + memcmp(lcname, "assert", sizeof("assert")-1) == 0) { + + int op_number = get_next_op_number(CG(active_op_array)); + zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_ASSERT_CHECK; + opline->extended_value = 0; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); + opline->op2.ptr = (void*)LANG_SCNG(yy_text); + + function_name->EA = op_number; + } + if ((zend_hash_find(CG(function_table), lcname, Z_STRLEN(function_name->u.constant)+1, (void **) &function)==FAILURE) || ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS) && (function->type == ZEND_INTERNAL_FUNCTION))) { @@ -2538,19 +2554,85 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na } /* }}} */ +static inline void zend_copy_assertion_text(zval *target, const char *start_statement, const char *end_statement TSRMLS_DC) /* {{{ */ +{ + char *str; + const char *statement = start_statement; + size_t statement_length = end_statement - start_statement; + + while (statement && isspace(*statement)) { + statement_length--; + statement++; + } + + while (end_statement && isspace(*end_statement)) { + statement_length--; + end_statement--; + } + + str = emalloc(sizeof("assert") + statement_length + 1); + memcpy(str, "assert", sizeof("assert") - 1); + memcpy(str + sizeof("assert") - 1, statement, statement_length + 1); + str[sizeof("assert") + statement_length] = 0; + ZVAL_STRINGL(target, str, sizeof("assert") + statement_length, 0); +} +/* }}} */ + void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */ { zend_op *opline; + long arg_num = Z_LVAL(argument_list->u.constant); if (is_method && function_name && function_name->op_type == IS_UNUSED) { /* clone */ - if (Z_LVAL(argument_list->u.constant) != 0) { + if (arg_num != 0) { zend_error(E_WARNING, "Clone method does not require arguments"); } opline = &CG(active_op_array)->opcodes[Z_LVAL(function_name->u.constant)]; } else { zend_function **function_ptr_ptr; zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr); + + if (!is_method && + function_name->op_type==IS_CONST && + Z_STRLEN(function_name->u.constant) == sizeof("assert")-1 && + memcmp(Z_STRVAL(function_name->u.constant), "assert", sizeof("assert")-1) == 0) { + + int assert_op_number = function_name->EA; + + if (arg_num == 1) { + int last_op_number = get_next_op_number(CG(active_op_array)); + zend_op *last_op = &CG(active_op_array)->opcodes[last_op_number-1]; + + if (last_op->opcode != ZEND_SEND_VAL || + last_op->op1_type != IS_CONST || + Z_TYPE(CONSTANT(last_op->op1.constant)) != IS_STRING) { + + zval message; + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_SEND_VAL; + + zend_copy_assertion_text( + &message, + (const char*)CG(active_op_array)->opcodes[assert_op_number].op2.ptr, + (const char*)LANG_SCNG(yy_text) TSRMLS_CC); + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_literal(CG(active_op_array), &message TSRMLS_CC); + CALCULATE_LITERAL_HASH(opline->op1.constant); + opline->op2.opline_num = 2; + opline->extended_value = !is_dynamic_fcall ? ZEND_DO_FCALL : ZEND_DO_FCALL_BY_NAME; + SET_UNUSED(opline->op2); + arg_num = 2; + CG(context).used_stack++; + } + } + + CG(active_op_array)->opcodes[assert_op_number].op1.opline_num = + get_next_op_number(CG(active_op_array)) + 1; + CG(active_op_array)->opcodes[assert_op_number].op2.ptr = + NULL; + } opline = get_next_op(CG(active_op_array) TSRMLS_CC); if (*function_ptr_ptr) { @@ -2580,12 +2662,12 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode GET_NODE(result, opline->result); zend_stack_del_top(&CG(function_call_stack)); - opline->extended_value = Z_LVAL(argument_list->u.constant); + opline->extended_value = arg_num; if (CG(context).used_stack + 1 > CG(active_op_array)->used_stack) { CG(active_op_array)->used_stack = CG(context).used_stack + 1; } - CG(context).used_stack -= Z_LVAL(argument_list->u.constant); + CG(context).used_stack -= arg_num; } /* }}} */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 3988074779bc1..3c7a30b93568e 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1299,6 +1299,7 @@ void execute_new_code(TSRMLS_D) /* {{{ */ } /* break omitted intentionally */ case ZEND_JMP: + case ZEND_ASSERT_CHECK: opline->op1.jmp_addr = &CG(active_op_array)->opcodes[opline->op1.opline_num]; break; case ZEND_JMPZ: diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 31dd9af619750..aa2bf4f413d94 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -256,6 +256,7 @@ struct _zend_executor_globals { zend_property_info std_property_info; zend_bool active; + zend_bool assertions; zend_op *start_op; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index af64e5a7f983e..61db501ab1e49 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -699,6 +699,7 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC) } /* break omitted intentionally */ case ZEND_JMP: + case ZEND_ASSERT_CHECK: case ZEND_FAST_CALL: opline->op1.jmp_addr = &op_array->opcodes[opline->op1.opline_num]; break; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b2cecf229e56e..9bd34722cd765 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5659,4 +5659,24 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY) } } +ZEND_VM_HANDLER(166, ZEND_ASSERT_CHECK, ANY, ANY) +{ + USE_OPLINE + + if (!EG(assertions)) { + if (RETURN_VALUE_USED((opline->op1.jmp_addr-1))) { + zval *ret; + + MAKE_STD_ZVAL(ret); + Z_SET_REFCOUNT_P(ret, 0); + ZVAL_BOOL(ret, 1); + PZVAL_LOCK(ret); + AI_SET_PTR(&EX_T((opline->op1.jmp_addr-1)->result.var), ret); + } + ZEND_VM_JMP(opline->op1.jmp_addr); + } else { + ZEND_VM_NEXT_OPCODE(); + } +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 49d04a2bae793..eb2dd21a32be2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1383,6 +1383,26 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } } +static int ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + if (!EG(assertions)) { + if (RETURN_VALUE_USED((opline->op1.jmp_addr-1))) { + zval *ret; + + MAKE_STD_ZVAL(ret); + Z_SET_REFCOUNT_P(ret, 0); + ZVAL_BOOL(ret, 1); + PZVAL_LOCK(ret); + AI_SET_PTR(&EX_T((opline->op1.jmp_addr-1)->result.var), ret); + } + ZEND_VM_JMP(opline->op1.jmp_addr); + } else { + ZEND_VM_NEXT_OPCODE(); + } +} + static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -45078,6 +45098,31 @@ void zend_init_opcodes_handlers(void) ZEND_SEND_UNPACK_SPEC_HANDLER, ZEND_SEND_UNPACK_SPEC_HANDLER, ZEND_SEND_UNPACK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index d6f51cce5c3f8..89bb0f77a0920 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -const char *zend_vm_opcodes_map[166] = { +const char *zend_vm_opcodes_map[167] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -188,6 +188,7 @@ const char *zend_vm_opcodes_map[166] = { "ZEND_FAST_RET", "ZEND_RECV_VARIADIC", "ZEND_SEND_UNPACK", + "ZEND_ASSERT_CHECK", }; ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 0f75196ffb205..fb27cbc4c1aaf 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -171,5 +171,6 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode); #define ZEND_FAST_RET 163 #define ZEND_RECV_VARIADIC 164 #define ZEND_SEND_UNPACK 165 +#define ZEND_ASSERT_CHECK 166 #endif diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index a6114733e3bf6..4ab921b60f619 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -118,6 +118,12 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg) } START_BLOCK_OP(opno + 1); break; +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + START_BLOCK_OP(opno + 1); + break; #endif case ZEND_JMP: START_BLOCK_OP(ZEND_OP1(opline).opline_num); @@ -268,6 +274,12 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg) cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; } break; +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: + cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; + cur_block->follow_to = &blocks[opno]; + break; #endif case ZEND_JMP: cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index b2fb667ed5557..026b583ad55a7 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -57,6 +57,9 @@ static void nop_removal(zend_op_array *op_array) /* update JMPs */ for (opline = op_array->opcodes; oplineopcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO case ZEND_GOTO: diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 577d1b66098e4..40f272941f19e 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -485,6 +485,9 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO case ZEND_FAST_CALL: case ZEND_FAST_RET: +#endif +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: #endif case ZEND_JMP: case ZEND_JMPZNZ: diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 973ba3aaacb07..5f5d9b00ac3dc 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -474,6 +474,9 @@ static void zend_accel_optimize(zend_op_array *op_array, } #endif switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO case ZEND_GOTO: @@ -515,6 +518,9 @@ static void zend_accel_optimize(zend_op_array *op_array, } #endif switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: #if ZEND_EXTENSION_API_NO > PHP_5_2_X_API_NO case ZEND_GOTO: diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 47f8f88312796..728ecd888b70e 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -345,6 +345,9 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc if (ZEND_DONE_PASS_TWO(op_array)) { /* fix jumps to point to new array */ switch (opline->opcode) { +#ifdef ZEND_ASSERT_CHECK + case ZEND_ASSERT_CHECK: +#endif case ZEND_JMP: case ZEND_GOTO: #if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index dfec951f79903..0728d505c317d 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -9,12 +9,14 @@ var_dump($standard->getClassNames()); ?> ==DONE== --EXPECTF-- -array(3) { +array(4) { [0]=> %s(22) "__PHP_Incomplete_Class" [1]=> %s(15) "php_user_filter" [2]=> %s(9) "Directory" + [3]=> + %s(18) "AssertionException" } ==DONE== diff --git a/ext/standard/assert.c b/ext/standard/assert.c index a9567f3b0a7cd..a09ff3e1368f3 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -22,6 +22,7 @@ #include "php.h" #include "php_assert.h" #include "php_ini.h" +#include "zend_exceptions.h" /* }}} */ ZEND_BEGIN_MODULE_GLOBALS(assert) @@ -29,12 +30,15 @@ ZEND_BEGIN_MODULE_GLOBALS(assert) long bail; long warning; long quiet_eval; + long exception; zval *callback; char *cb; ZEND_END_MODULE_GLOBALS(assert) ZEND_DECLARE_MODULE_GLOBALS(assert) +static zend_class_entry *assertion_exception_ce; + #ifdef ZTS #define ASSERTG(v) TSRMG(assert_globals_id, zend_assert_globals *, v) #else @@ -48,7 +52,8 @@ enum { ASSERT_CALLBACK, ASSERT_BAIL, ASSERT_WARNING, - ASSERT_QUIET_EVAL + ASSERT_QUIET_EVAL, + ASSERT_EXCEPTION }; static PHP_INI_MH(OnChangeCallback) /* {{{ */ @@ -84,6 +89,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("assert.warning", "1", PHP_INI_ALL, OnUpdateLong, warning, zend_assert_globals, assert_globals) PHP_INI_ENTRY("assert.callback", NULL, PHP_INI_ALL, OnChangeCallback) STD_PHP_INI_ENTRY("assert.quiet_eval", "0", PHP_INI_ALL, OnUpdateLong, quiet_eval, zend_assert_globals, assert_globals) + STD_PHP_INI_ENTRY("assert.exception", "0", PHP_INI_ALL, OnUpdateLong, exception, zend_assert_globals, assert_globals) PHP_INI_END() static void php_assert_init_globals(zend_assert_globals *assert_globals_p TSRMLS_DC) /* {{{ */ @@ -95,6 +101,8 @@ static void php_assert_init_globals(zend_assert_globals *assert_globals_p TSRMLS PHP_MINIT_FUNCTION(assert) /* {{{ */ { + zend_class_entry ce; + ZEND_INIT_MODULE_GLOBALS(assert, php_assert_init_globals, NULL); REGISTER_INI_ENTRIES(); @@ -104,6 +112,10 @@ PHP_MINIT_FUNCTION(assert) /* {{{ */ REGISTER_LONG_CONSTANT("ASSERT_BAIL", ASSERT_BAIL, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ASSERT_WARNING", ASSERT_WARNING, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("ASSERT_QUIET_EVAL", ASSERT_QUIET_EVAL, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ASSERT_EXCEPTION", ASSERT_EXCEPTION, CONST_CS|CONST_PERSISTENT); + + INIT_CLASS_ENTRY(ce, "AssertionException", NULL); + assertion_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); return SUCCESS; } @@ -140,24 +152,25 @@ PHP_MINFO_FUNCTION(assert) /* {{{ */ Checks if assertion is false */ PHP_FUNCTION(assert) { - zval **assertion; - int val, description_len = 0; + zval *assertion; + zval *description = NULL; + int val; char *myeval = NULL; - char *compiled_string_description, *description = NULL; + char *compiled_string_description; if (! ASSERTG(active)) { RETURN_TRUE; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|s", &assertion, &description, &description_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &assertion, &description) == FAILURE) { return; } - if (Z_TYPE_PP(assertion) == IS_STRING) { + if (Z_TYPE_P(assertion) == IS_STRING) { zval retval; int old_error_reporting = 0; /* shut up gcc! */ - myeval = Z_STRVAL_PP(assertion); + myeval = Z_STRVAL_P(assertion); if (ASSERTG(quiet_eval)) { old_error_reporting = EG(error_reporting); @@ -165,12 +178,13 @@ PHP_FUNCTION(assert) } compiled_string_description = zend_make_compiled_string_description("assert code" TSRMLS_CC); - if (zend_eval_stringl(myeval, Z_STRLEN_PP(assertion), &retval, compiled_string_description TSRMLS_CC) == FAILURE) { + if (zend_eval_stringl(myeval, Z_STRLEN_P(assertion), &retval, compiled_string_description TSRMLS_CC) == FAILURE) { efree(compiled_string_description); - if (description_len == 0) { + if (!description) { php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s", PHP_EOL, myeval); } else { - php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, description, myeval); + convert_to_string(description); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Failure evaluating code: %s%s:\"%s\"", PHP_EOL, Z_STRVAL_P(description), myeval); } if (ASSERTG(bail)) { zend_bailout(); @@ -186,8 +200,11 @@ PHP_FUNCTION(assert) convert_to_boolean(&retval); val = Z_LVAL(retval); } else { - convert_to_boolean_ex(assertion); - val = Z_LVAL_PP(assertion); +#if PHP_VERSION_ID >= 50700 + val = zend_is_true(assertion TSRMLS_CC); +#else + val = zend_is_true(assertion); +#endif } if (val) { @@ -200,7 +217,7 @@ PHP_FUNCTION(assert) } if (ASSERTG(callback)) { - zval **args = safe_emalloc(description_len == 0 ? 3 : 4, sizeof(zval *), 0); + zval **args = safe_emalloc(!description ? 3 : 4, sizeof(zval *), 0); zval *retval; int i; uint lineno = zend_get_executed_lineno(TSRMLS_C); @@ -218,14 +235,15 @@ PHP_FUNCTION(assert) ZVAL_FALSE(retval); /* XXX do we want to check for error here? */ - if (description_len == 0) { + if (!description) { call_user_function(CG(function_table), NULL, ASSERTG(callback), retval, 3, args TSRMLS_CC); for (i = 0; i <= 2; i++) { zval_ptr_dtor(&(args[i])); } } else { - MAKE_STD_ZVAL(args[3]); - ZVAL_STRINGL(args[3], SAFE_STRING(description), description_len, 1); + convert_to_string(description); + Z_ADDREF_P(description); + args[3] = description; call_user_function(CG(function_table), NULL, ASSERTG(callback), retval, 4, args TSRMLS_CC); for (i = 0; i <= 3; i++) { @@ -237,18 +255,30 @@ PHP_FUNCTION(assert) zval_ptr_dtor(&retval); } - if (ASSERTG(warning)) { - if (description_len == 0) { + if (ASSERTG(exception)) { + if (!description) { + zend_throw_exception(assertion_exception_ce, NULL, E_ERROR TSRMLS_CC); + } else if (Z_TYPE_P(description) == IS_OBJECT && + instanceof_function(Z_OBJCE_P(description), assertion_exception_ce TSRMLS_CC)) { + Z_ADDREF_P(description); + zend_throw_exception_object(description TSRMLS_CC); + } else { + convert_to_string(description); + zend_throw_exception(assertion_exception_ce, Z_STRVAL_P(description), E_ERROR TSRMLS_CC); + } + } else if (ASSERTG(warning)) { + if (!description) { if (myeval) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Assertion \"%s\" failed", myeval); } else { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Assertion failed"); } } else { + convert_to_string(description); if (myeval) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: \"%s\" failed", description, myeval); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s: \"%s\" failed", Z_STRVAL_P(description), myeval); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s failed", description); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s failed", Z_STRVAL_P(description)); } } } @@ -327,6 +357,15 @@ PHP_FUNCTION(assert_options) return; break; + case ASSERT_EXCEPTION: + oldint = ASSERTG(exception); + if (ac == 2) { + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.exception", sizeof("assert.exception"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); + } + RETURN_LONG(oldint); + break; + default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown value %ld", what); break; diff --git a/ext/standard/tests/assert/assert04.phpt b/ext/standard/tests/assert/assert04.phpt index bffadcb97c6e7..dce2c805e3662 100644 --- a/ext/standard/tests/assert/assert04.phpt +++ b/ext/standard/tests/assert/assert04.phpt @@ -42,7 +42,7 @@ Warning: assert_options() expects at most 2 parameters, 3 given in %s on line %d Warning: assert_options() expects parameter 1 to be long, %unicode_string_optional% given in %s on line %d -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d diff --git a/ext/standard/tests/assert/assert_basic2.phpt b/ext/standard/tests/assert/assert_basic2.phpt index 277e5cad187e9..42c59b1b9a65a 100644 --- a/ext/standard/tests/assert/assert_basic2.phpt +++ b/ext/standard/tests/assert/assert_basic2.phpt @@ -29,9 +29,9 @@ assert(0); string(2) "f1" f1 called -Warning: assert(): Assertion failed in %s on line 13 +Warning: assert(): assert(0) failed in %s on line 13 string(2) "f1" string(2) "f2" f2 called -Warning: assert(): Assertion failed in %s on line 17 +Warning: assert(): assert(0) failed in %s on line 17 diff --git a/ext/standard/tests/assert/assert_basic3.phpt b/ext/standard/tests/assert/assert_basic3.phpt index 0ce326606fbf5..9fcbbd5c3c292 100644 --- a/ext/standard/tests/assert/assert_basic3.phpt +++ b/ext/standard/tests/assert/assert_basic3.phpt @@ -22,4 +22,4 @@ echo "If this is printed BAIL hasn't worked"; int(0) f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): assert($sa): "0 != 0" failed in %s on line 10 diff --git a/ext/standard/tests/assert/assert_basic5.phpt b/ext/standard/tests/assert/assert_basic5.phpt index 737f902d64048..d67d879262371 100644 --- a/ext/standard/tests/assert/assert_basic5.phpt +++ b/ext/standard/tests/assert/assert_basic5.phpt @@ -26,7 +26,7 @@ var_dump($rao=assert_options(ASSERT_WARNING, 0)); int(0) f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): assert($sa): "0 != 0" failed in %s on line 10 NULL bool(true) int(1) diff --git a/ext/standard/tests/assert/assert_closures.phpt b/ext/standard/tests/assert/assert_closures.phpt index e01c11ace989f..4a013283a8d7e 100644 --- a/ext/standard/tests/assert/assert_closures.phpt +++ b/ext/standard/tests/assert/assert_closures.phpt @@ -13,4 +13,4 @@ assert(0); --EXPECTF-- Hello World! -Warning: assert(): Assertion failed in %s on line %d +Warning: assert(): assert(0) failed in %s on line %d diff --git a/ext/standard/tests/assert/assert_error2.phpt b/ext/standard/tests/assert/assert_error2.phpt index da7c3d9e0bbbc..41a7197535e26 100644 --- a/ext/standard/tests/assert/assert_error2.phpt +++ b/ext/standard/tests/assert/assert_error2.phpt @@ -17,8 +17,7 @@ function f1($script, $line, $message, $user_message) //bail out on error var_dump($rao = assert_options(ASSERT_BAIL, 1)); -$sa = "0 != 0"; -var_dump($r2 = assert($sa)); +var_dump($r2 = assert("0 != 0")); echo "If this is printed BAIL hasn't worked"; --EXPECTF-- int(0) @@ -26,5 +25,5 @@ int(0) Warning: Missing argument 4 for f1() in %s on line 2 f1 called -Warning: assert(): Assertion "0 != 0" failed in %s on line 10 +Warning: assert(): Assertion "0 != 0" failed in %s on line 9 diff --git a/ext/standard/tests/assert/assert_error3.phpt b/ext/standard/tests/assert/assert_error3.phpt index 54b91edd3d93e..201a4544fa09a 100644 --- a/ext/standard/tests/assert/assert_error3.phpt +++ b/ext/standard/tests/assert/assert_error3.phpt @@ -10,12 +10,11 @@ error_reporting = -1 display_errors = 1 --FILE-- Date: Tue, 17 Feb 2015 22:45:10 +0300 Subject: [PATCH 2/7] Implemented AST pretty-printer to capture expression passed to assert() --- Zend/tests/assert/expect_015.phpt | 240 ++++ Zend/zend_ast.c | 1011 +++++++++++++++++ Zend/zend_ast.h | 1 + Zend/zend_compile.c | 11 +- ...ysqli_class_mysqli_properties_no_conn.phpt | 8 +- 5 files changed, 1264 insertions(+), 7 deletions(-) create mode 100644 Zend/tests/assert/expect_015.phpt diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt new file mode 100644 index 0000000000000..a2fa4861a623e --- /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 242c99ff7f528..8a4c9cf9959b9 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 a9d58428ec923..bc26ea2168a6a 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 e80c7ccc6575b..68dfe05ddac95 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 e4d0b08a85e3e..7e36c6571e4cf 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) From 96de5ffc8d604df9797d0141ae5ad9c15e1d6c32 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 18 Feb 2015 00:28:39 +0300 Subject: [PATCH 3/7] Fixed assert() in namesapaces --- Zend/tests/assert/expect_016.phpt | 24 ++++++++++++++++++++++ Zend/tests/assert/expect_017.phpt | 19 ++++++++++++++++++ Zend/tests/assert/expect_018.phpt | 33 +++++++++++++++++++++++++++++++ Zend/tests/assert/expect_019.phpt | 19 ++++++++++++++++++ Zend/zend.c | 1 + Zend/zend_compile.c | 29 +++++++++++++++++++++------ 6 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/assert/expect_016.phpt create mode 100644 Zend/tests/assert/expect_017.phpt create mode 100644 Zend/tests/assert/expect_018.phpt create mode 100644 Zend/tests/assert/expect_019.phpt diff --git a/Zend/tests/assert/expect_016.phpt b/Zend/tests/assert/expect_016.phpt new file mode 100644 index 0000000000000..84e71ff47bab1 --- /dev/null +++ b/Zend/tests/assert/expect_016.phpt @@ -0,0 +1,24 @@ +--TEST-- +test enable/disable assertions at runtime (assertions not completely disabled) +--INI-- +zend.assertions=0 +assert.exception=0 +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) + +Warning: assert(): assert(false) failed in %sexpect_016.php on line 6 +NULL +bool(true) + +Warning: zend.assertions may be completely enabled or disabled only in php.ini in %sexpect_016.php on line 8 diff --git a/Zend/tests/assert/expect_017.phpt b/Zend/tests/assert/expect_017.phpt new file mode 100644 index 0000000000000..bc2c4d41a7ac5 --- /dev/null +++ b/Zend/tests/assert/expect_017.phpt @@ -0,0 +1,19 @@ +--TEST-- +test enable/disable assertions at runtime (assertions completely disabled) +--INI-- +zend.assertions=-1 +assert.exception=0 +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) + +Warning: zend.assertions may be completely enabled or disabled only in php.ini in %sexpect_017.php on line 4 + +Warning: zend.assertions may be completely enabled or disabled only in php.ini in %sexpect_017.php on line 5 diff --git a/Zend/tests/assert/expect_018.phpt b/Zend/tests/assert/expect_018.phpt new file mode 100644 index 0000000000000..0c9b9fe40f537 --- /dev/null +++ b/Zend/tests/assert/expect_018.phpt @@ -0,0 +1,33 @@ +--TEST-- +test assertions in namespace +--INI-- +zend.assertions=1 +assert.exception=0 +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: assert(): assert(false) failed in %sexpect_018.php on line 10 +NULL +bool(true) + +Warning: assert(): assert(false) failed in %sexpect_018.php on line 12 +NULL +bool(true) diff --git a/Zend/tests/assert/expect_019.phpt b/Zend/tests/assert/expect_019.phpt new file mode 100644 index 0000000000000..38e9a39c338cd --- /dev/null +++ b/Zend/tests/assert/expect_019.phpt @@ -0,0 +1,19 @@ +--TEST-- +test assertions in namespace (assertions completely disabled) +--INI-- +zend.assertions=-1 +assert.exception=0 +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/Zend/zend.c b/Zend/zend.c index 5ee4b7c1b32ed..a99b07ada9bb9 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -116,6 +116,7 @@ static ZEND_INI_MH(OnUpdateAssertions) /* {{{ */ if (stage != ZEND_INI_STAGE_STARTUP && stage != ZEND_INI_STAGE_SHUTDOWN && + *p != val && (*p < 0 || val < 0)) { zend_error(E_WARNING, "zend.assertions may be completely enabled or disabled only in php.ini"); return FAILURE; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 68dfe05ddac95..248bbb05d5ea2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2817,19 +2817,26 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam -static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *lcname, zend_function *fbc) /* {{{ */ +static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *name, zend_function *fbc) /* {{{ */ { if (EG(assertions) >= 0) { - znode name_node; + znode name_node; zend_op *opline; uint32_t check_op_number = get_next_op_number(CG(active_op_array)); zend_emit_op(NULL, ZEND_ASSERT_CHECK, NULL, NULL); - name_node.op_type = IS_CONST; - ZVAL_STR_COPY(&name_node.u.constant, lcname); + if (fbc) { + name_node.op_type = IS_CONST; + ZVAL_STR_COPY(&name_node.u.constant, name); - opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node); + opline = zend_emit_op(NULL, ZEND_INIT_FCALL, NULL, &name_node); + } else { + opline = zend_emit_op(NULL, ZEND_INIT_NS_FCALL_BY_NAME, NULL, NULL); + opline->op2_type = IS_CONST; + opline->op2.constant = zend_add_ns_func_name_literal( + CG(active_op_array), name); + } zend_alloc_cache_slot(opline->op2.constant); if (args->children == 1 && @@ -2844,6 +2851,12 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string * 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)); + } else { + if (!fbc) { + zend_string_release(name); + } + result->op_type = IS_CONST; + ZVAL_TRUE(&result->u.constant); } return SUCCESS; @@ -2906,7 +2919,11 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ { zend_bool runtime_resolution = zend_compile_function_name(&name_node, name_ast); if (runtime_resolution) { - zend_compile_ns_call(result, &name_node, args_ast); + if (zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert")) { + zend_compile_assert(result, args_ast, Z_STR(name_node.u.constant), NULL); + } else { + zend_compile_ns_call(result, &name_node, args_ast); + } return; } } From 8473157fbb12d03fff8d5b602865a4b667522a4d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 18 Feb 2015 10:17:26 +0300 Subject: [PATCH 4/7] Fixed typo and white spaces --- Zend/tests/assert/expect_015.phpt | 2 +- Zend/zend_ast.c | 36 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index a2fa4861a623e..4c92e53ae79e9 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -27,7 +27,7 @@ assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X { public $a = 1, $b; protected $c; static private $d = null; - + abstract function foo(); static private function f1() { diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 8a4c9cf9959b9..753fe52a7dddc 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -450,13 +450,13 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn) { * ==================== * proirity associativity operatiers * ----------------------------------- - * 10 left inclue, include_once, eval, require, require_once + * 10 left include, include_once, eval, require, require_once * 20 left , * 30 left or * 40 left xor * 50 left and * 60 right print - * 70 right yield + * 70 right yield * 80 right => * 90 right = += -= *= /= .= %= &= |= ^= <<= >>= **= * 100 left ? : @@ -506,30 +506,30 @@ static void zend_ast_export_dstr(smart_str *str, zend_string *s) switch (c) { case '\n': smart_str_appends(str, "\\n"); - break; + break; case '\r': smart_str_appends(str, "\\r"); - break; + break; case '\t': smart_str_appends(str, "\\t"); - break; + break; case '\f': smart_str_appends(str, "\\f"); - break; + break; case '\v': smart_str_appends(str, "\\v"); - break; + break; #ifdef PHP_WIN32 case VK_ESCAPE: #else case '\e': #endif smart_str_appends(str, "\\e"); - break; + break; default: smart_str_appends(str, "\\0"); smart_str_appendc(str, '0' + (c / 8)); - smart_str_appendc(str, '0' + (c % 8)); + smart_str_appendc(str, '0' + (c % 8)); break; } } else { @@ -553,7 +553,7 @@ static void zend_ast_export_name(smart_str *str, zend_ast *ast, int priority, in { 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; @@ -601,7 +601,7 @@ static void zend_ast_export_encaps_list(smart_str *str, zend_ast_list *list, int 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 { @@ -642,7 +642,7 @@ static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int in if (list->child[i]->attr) { smart_str_appendc(str, '&'); } - smart_str_appendc(str, '$'); + smart_str_appendc(str, '$'); zend_ast_export_name(str, list->child[i], 20, indent); i++; } @@ -687,7 +687,7 @@ static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int indent) { - zend_long idx; + zend_long idx; zend_string *key; zval *val; int first; @@ -745,7 +745,7 @@ static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int ind break; default: ZEND_ASSERT(0); - } + } } #define BINARY_OP(_op, _p, _pl, _pr) do { \ @@ -1071,7 +1071,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int APPEND_NODE_1("continue"); /* 2 child nodes */ - case ZEND_AST_DIM: + case ZEND_AST_DIM: zend_ast_export_ex(str, ast->child[0], 260, indent); smart_str_appendc(str, '['); if (ast->child[1]) { @@ -1231,7 +1231,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int smart_str_appends(str, "default:\n"); } zend_ast_export_stmt(str, ast->child[1], indent + 1); - break; + break; case ZEND_AST_DECLARE: smart_str_appends(str, "declare("); if (ast->child[0]->kind == ZEND_AST_CONST_DECL) { @@ -1251,7 +1251,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_name(str, ast->child[0], 0, indent); APPEND_DEFAULT_VALUE(1); case ZEND_AST_USE_TRAIT: - smart_str_appends(str, "use "); + smart_str_appends(str, "use "); zend_ast_export_ex(str, ast->child[0], 0, indent); if (ast->child[1]) { smart_str_appends(str, " {\n"); @@ -1385,7 +1385,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_stmt(str, ast->child[3], indent + 1); zend_ast_export_indent(str, indent); smart_str_appendc(str, '}'); - break; + break; default: ZEND_ASSERT(0); } From 5a976c8d85078502b48379996ab066e57533a0c3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 18 Feb 2015 19:50:08 +0300 Subject: [PATCH 5/7] Fixed vaeious ptetty-printer issues --- Zend/tests/assert/expect_015.phpt | 251 ++++++++++++++++++------------ Zend/zend_ast.c | 229 +++++++++++++++++++-------- 2 files changed, 312 insertions(+), 168 deletions(-) diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index 4c92e53ae79e9..d4f3769de733f 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -96,12 +96,14 @@ assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X { use T1, T2 { T1::foo insteadof foo; T2::foo as bar; + baz as public; + ops as protected x; } use T3; } })); -assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X { +assert(0 && ($a = function &(array &...$a) { declare(A=1,B=2); try { $i++; @@ -113,128 +115,175 @@ assert(0 && ($a = function &(array &$a, X $b = null) use ($c,&$d) : X { echo 3; } })); + +assert(0 && ($a = function () { + declare(C=1) { echo 1; } + $x = '\'"`$a'; + $x = "'\"`$a"; + $x = `'"\`$a`; + $x = "{$a}b"; + $x = "${a}b"; + $x = " {$foo->bar} ${$foo->bar} "; + $x = " ${'---'} "; + foo(); + \foo(); + namespace\foo(); + $x = foo; + $x = \foo; + $x = namespace\foo; + $x = new foo(); + $x = new \foo(); + $x = new namespace\foo(); +})); + ?> --EXPECTF-- Warning: Unsupported declare 'A' in %sexpect_015.php on line %d Warning: Unsupported declare 'B' in %sexpect_015.php on line %d +Warning: Unsupported declare 'C' 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; + 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(); + 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'}; - } + 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; - } - } - } + 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; - } + class A { + use T1, T2 { + T1::foo insteadof foo; + T2::foo as bar; + baz as public; + ops as protected x; + } + 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; - } +Warning: assert(): assert(0 && ($a = function &(array &...$a) { + 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 + +Warning: assert(): assert(0 && ($a = function () { + declare(C = 1) { + echo 1; + } + $x = '\'"`$a'; + $x = "'\"`$a"; + $x = `'"\`$a`; + $x = "{$a}b"; + $x = "{$a}b"; + $x = " {$foo->bar} {${$foo->bar}} "; + $x = " ${---} "; + foo(); + \foo(); + namespace\foo(); + $x = foo; + $x = \foo; + $x = namespace\foo; + $x = new foo(); + $x = new \foo(); + $x = new namespace\foo(); })) failed in %sexpect_015.php on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 753fe52a7dddc..becb00b2269a2 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -448,8 +448,8 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn) { /* * Operator Precendence * ==================== - * proirity associativity operatiers - * ----------------------------------- + * priority associativity operators + * ---------------------------------- * 10 left include, include_once, eval, require, require_once * 20 left , * 30 left or @@ -496,7 +496,7 @@ static void zend_ast_export_str(smart_str *str, zend_string *s) } } -static void zend_ast_export_dstr(smart_str *str, zend_string *s) +static void zend_ast_export_qstr(smart_str *str, char quote, zend_string *s) { size_t i; @@ -533,7 +533,7 @@ static void zend_ast_export_dstr(smart_str *str, zend_string *s) break; } } else { - if (c == '"' || c == '`' || c == '$' || c == '\\') { + if (c == quote || c == '$' || c == '\\') { smart_str_appendc(str, '\\'); } smart_str_appendc(str, c); @@ -544,7 +544,7 @@ static void zend_ast_export_dstr(smart_str *str, zend_string *s) static void zend_ast_export_indent(smart_str *str, int indent) { while (indent > 0) { - smart_str_appendc(str, '\t'); + smart_str_appends(str, " "); indent--; } } @@ -555,19 +555,77 @@ static void zend_ast_export_name(smart_str *str, zend_ast *ast, int priority, in 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)); + smart_str_append(str, Z_STR_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) +static void zend_ast_export_ns_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)); + if (ast->attr == ZEND_NAME_FQ) { + smart_str_appendc(str, '\\'); + } else if (ast->attr == ZEND_NAME_RELATIVE) { + smart_str_appends(str, "namespace\\"); + } + smart_str_append(str, Z_STR_P(zv)); + return; + } + } + zend_ast_export_ex(str, ast, priority, indent); +} + +static int zend_ast_valid_var_char(char ch) +{ + unsigned char c = (unsigned char)ch; + + if (c != '_' && c < 127 && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + return 1; +} + +static int zend_ast_valid_var_name(const char *s, size_t len) +{ + unsigned char c; + size_t i; + + if (len == 0) { + return 0; + } + c = (unsigned char)s[0]; + if (c != '_' && c < 127 && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + for (i = 1; i < len; i++) { + c = (unsigned char)s[i]; + if (c != '_' && c < 127 && + (c < '0' || c > '9') && + (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) { + return 0; + } + } + return 1; +} + +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 && + zend_ast_valid_var_name(Z_STRVAL_P(zv), Z_STRLEN_P(zv))) { + smart_str_append(str, Z_STR_P(zv)); return; } } else if (ast->kind == ZEND_AST_VAR) { @@ -592,7 +650,7 @@ static void zend_ast_export_list(smart_str *str, zend_ast_list *list, int separa } } -static void zend_ast_export_encaps_list(smart_str *str, zend_ast_list *list, int indent) +static void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_list *list, int indent) { uint32_t i = 0; zend_ast *ast; @@ -602,15 +660,18 @@ static void zend_ast_export_encaps_list(smart_str *str, zend_ast_list *list, int 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_ASSERT(Z_TYPE_P(zv) == IS_STRING); + zend_ast_export_qstr(str, quote, Z_STR_P(zv)); + } else if (ast->kind == ZEND_AST_VAR && + ast->child[0]->kind == ZEND_AST_ZVAL && + (i + 1 == list->children || + list->child[i + 1]->kind != ZEND_AST_ZVAL || + !zend_ast_valid_var_char( + *Z_STRVAL_P( + zend_ast_get_zval(list->child[i + 1]))))) { zend_ast_export_ex(str, ast, 0, indent); } else { - smart_str_appends(str, "${"); + smart_str_appendc(str, '{'); zend_ast_export_ex(str, ast, 0, indent); smart_str_appendc(str, '}'); } @@ -676,6 +737,7 @@ static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) case ZEND_AST_CLASS: case ZEND_AST_USE_TRAIT: case ZEND_AST_NAMESPACE: + case ZEND_AST_DECLARE: break; default: smart_str_appendc(str, ';'); @@ -688,9 +750,9 @@ static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) 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; + zend_string *key; + zval *val; + int first; ZVAL_DEREF(zv); switch (Z_TYPE_P(zv)) { @@ -743,8 +805,7 @@ static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int ind case IS_CONSTANT_AST: zend_ast_export_ex(str, Z_ASTVAL_P(zv), priority, indent); break; - default: - ZEND_ASSERT(0); + EMPTY_SWITCH_DEFAULT_CASE(); } } @@ -842,7 +903,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + zend_ast_export_ns_name(str, decl->child[3], 0, indent); } if (decl->child[2]) { smart_str_appends(str, " {\n"); @@ -874,7 +935,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + zend_ast_export_ns_name(str, decl->child[0], 0, indent); } if (decl->child[1]) { smart_str_appends(str, " implements "); @@ -905,12 +966,12 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int break; case ZEND_AST_ENCAPS_LIST: smart_str_appendc(str, '"'); - zend_ast_export_encaps_list(str, (zend_ast_list*)ast, indent); + 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); + zend_ast_export_stmt(str, ast, indent); break; case ZEND_AST_IF: case ZEND_AST_SWITCH_LIST: @@ -943,6 +1004,11 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int break; case ZEND_AST_USE: smart_str_appends(str, "use "); + if (ast->attr == T_FUNCTION) { + smart_str_appends(str, "function "); + } else if (ast->attr == T_CONST) { + smart_str_appends(str, "const "); + } goto simple_list; /* 0 child nodes */ @@ -956,14 +1022,14 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + EMPTY_SWITCH_DEFAULT_CASE(); } break; case ZEND_AST_TYPE: switch (ast->attr) { case IS_ARRAY: APPEND_STR("array"); case IS_CALLABLE: APPEND_STR("callable"); - default: ZEND_ASSERT(0); + EMPTY_SWITCH_DEFAULT_CASE(); } break; @@ -973,14 +1039,15 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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)); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); break; case ZEND_AST_RESOLVE_CLASS_NAME: - zend_ast_export_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); APPEND_STR("::class"); case ZEND_AST_UNPACK: - APPEND_NODE_1("..."); + smart_str_appends(str, "..."); + ast = ast->child[0]; + goto tail_call; case ZEND_AST_UNARY_PLUS: PREFIX_OP("+", 240, 241); case ZEND_AST_UNARY_MINUS: PREFIX_OP("-", 240, 241); case ZEND_AST_CAST: @@ -992,7 +1059,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + EMPTY_SWITCH_DEFAULT_CASE(); } break; case ZEND_AST_EMPTY: @@ -1003,7 +1070,11 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int PREFIX_OP("@", 240, 241); case ZEND_AST_SHELL_EXEC: smart_str_appendc(str, '`'); - zend_ast_export_ex(str, ast->child[0], 0, indent); + if (ast->child[0]->kind == ZEND_AST_ENCAPS_LIST) { + zend_ast_export_encaps_list(str, '`', (zend_ast_list*)ast->child[0], indent); + } else { + zend_ast_export_ex(str, ast->child[0], 0, indent); + } smart_str_appendc(str, '`'); break; case ZEND_AST_CLONE: @@ -1024,14 +1095,14 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int case ZEND_REQUIRE_ONCE: FUNC_OP("require_once"); case ZEND_REQUIRE: FUNC_OP("require"); case ZEND_EVAL: FUNC_OP("eval"); - default: ZEND_ASSERT(0); + EMPTY_SWITCH_DEFAULT_CASE(); } 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); + EMPTY_SWITCH_DEFAULT_CASE(); } break; case ZEND_AST_PRE_INC: @@ -1044,7 +1115,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int POSTFIX_OP("--", 240, 241); case ZEND_AST_GLOBAL: - APPEND_NODE_1("global "); + APPEND_NODE_1("global"); case ZEND_AST_UNSET: FUNC_OP("unset"); case ZEND_AST_RETURN: @@ -1054,13 +1125,15 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int smart_str_appendc(str, ':'); break; case ZEND_AST_REF: - APPEND_NODE_1("&"); + smart_str_appendc(str, '&'); + ast = ast->child[0]; + goto tail_call; case ZEND_AST_HALT_COMPILER: APPEND_STR("__HALT_COMPILER()"); case ZEND_AST_ECHO: - APPEND_NODE_1("echo "); + APPEND_NODE_1("echo"); case ZEND_AST_THROW: - APPEND_NODE_1("throw "); + APPEND_NODE_1("throw"); case ZEND_AST_GOTO: smart_str_appends(str, "goto "); zend_ast_export_name(str, ast->child[0], 0, indent); @@ -1085,18 +1158,18 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + zend_ast_export_ns_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); + zend_ast_export_ns_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); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -1116,7 +1189,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + EMPTY_SWITCH_DEFAULT_CASE(); } break; case ZEND_AST_BINARY_OP: @@ -1140,7 +1213,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + EMPTY_SWITCH_DEFAULT_CASE(); } break; case ZEND_AST_GREATER: BINARY_OP(" > ", 180, 181, 181); @@ -1156,7 +1229,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int break; case ZEND_AST_NEW: smart_str_appends(str, "new "); - zend_ast_export_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_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, ')'); @@ -1234,19 +1307,21 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); - } + ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_CONST_DECL); + zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent); + smart_str_appendc(str, ')'); if (ast->child[1]) { - 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[1], indent + 1); + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); + } else { + smart_str_appendc(str, ';'); } - smart_str_appendc(str, ')'); break; case ZEND_AST_PROP_ELEM: smart_str_appendc(str, '$'); + /* break missing intentionally */ case ZEND_AST_CONST_ELEM: zend_ast_export_name(str, ast->child[0], 0, indent); APPEND_DEFAULT_VALUE(1); @@ -1268,8 +1343,10 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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, "::"); + if (ast->child[0]) { + 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: @@ -1290,8 +1367,17 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int case ZEND_AST_USE_ELEM: case ZEND_AST_TRAIT_ALIAS: zend_ast_export_name(str, ast->child[0], 0, indent); + if (ast->attr & ZEND_ACC_PUBLIC) { + smart_str_appends(str, " as public"); + } else if (ast->attr & ZEND_ACC_PROTECTED) { + smart_str_appends(str, " as protected"); + } else if (ast->attr & ZEND_ACC_PRIVATE) { + smart_str_appends(str, " as private"); + } else if (ast->child[1]) { + smart_str_appends(str, " as"); + } if (ast->child[1]) { - smart_str_appends(str, " as "); + smart_str_appendc(str, ' '); zend_ast_export_name(str, ast->child[1], 0, indent); } break; @@ -1306,7 +1392,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_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, '('); @@ -1341,20 +1427,24 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, " $"); + zend_ast_export_var(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); + zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appendc(str, ' '); } - if (ast->attr) { + if (ast->attr & ZEND_PARAM_REF) { smart_str_appendc(str, '&'); } + if (ast->attr & ZEND_PARAM_VARIADIC) { + smart_str_appends(str, "..."); + } smart_str_appendc(str, '$'); zend_ast_export_name(str, ast->child[1], 0, indent); APPEND_DEFAULT_VALUE(2); @@ -1363,10 +1453,16 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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_appendc(str, ';'); + if (ast->child[1]) { + smart_str_appendc(str, ' '); + zend_ast_export_ex(str, ast->child[1], 0, indent); + } + smart_str_appendc(str, ';'); + if (ast->child[2]) { + smart_str_appendc(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); @@ -1386,8 +1482,7 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_indent(str, indent); smart_str_appendc(str, '}'); break; - default: - ZEND_ASSERT(0); + EMPTY_SWITCH_DEFAULT_CASE(); } return; From 07b1f92ed662f6fa9309e679b83aff328362c98b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 18 Feb 2015 23:06:32 +0300 Subject: [PATCH 6/7] fixed pretty-printer (support for "elseif") --- Zend/tests/assert/expect_015.phpt | 6 ++++ Zend/zend_ast.c | 55 +++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index d4f3769de733f..942c480a7c32d 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -134,6 +134,9 @@ assert(0 && ($a = function () { $x = new foo(); $x = new \foo(); $x = new namespace\foo(); + if ($a) { + } elseif ($b) { + } })); ?> @@ -286,4 +289,7 @@ Warning: assert(): assert(0 && ($a = function () { $x = new foo(); $x = new \foo(); $x = new namespace\foo(); + if ($a) { + } elseif ($b) { + } })) failed in %sexpect_015.php on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index becb00b2269a2..b0b97c177663c 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -747,6 +747,43 @@ static void zend_ast_export_stmt(smart_str *str, zend_ast *ast, int indent) } } +static void zend_ast_export_if_stmt(smart_str *str, zend_ast_list *list, int indent) +{ + uint32_t i; + zend_ast *ast; + +tail_call: + i = 0; + while (i < list->children) { + ast = list->child[i]; + ZEND_ASSERT(ast->kind == ZEND_AST_IF_ELEM); + if (ast->child[0]) { + if (i == 0) { + smart_str_appends(str, "if ("); + } else { + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} elseif ("); + } + 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); + } else { + zend_ast_export_indent(str, indent); + smart_str_appends(str, "} else "); + if (ast->child[1]->kind == ZEND_AST_IF) { + list = (zend_ast_list*)ast->child[1]; + goto tail_call; + } else { + smart_str_appends(str, "{\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); + } + } + i++; + } + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); +} + static void zend_ast_export_zval(smart_str *str, zval *zv, int priority, int indent) { zend_long idx; @@ -974,6 +1011,8 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int zend_ast_export_stmt(str, ast, indent); break; case ZEND_AST_IF: + zend_ast_export_if_stmt(str, (zend_ast_list*)ast, indent); + break; case ZEND_AST_SWITCH_LIST: case ZEND_AST_CATCH_LIST: zend_ast_export_list(str, (zend_ast_list*)ast, 0, 0, indent); @@ -1268,23 +1307,19 @@ static void zend_ast_export_ex(smart_str *str, zend_ast *ast, int priority, int 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); + smart_str_appends(str, ") {\n"); + zend_ast_export_stmt(str, ast->child[1], indent + 1); } else { - smart_str_appends(str, "{\n"); + smart_str_appends(str, "else {\n"); zend_ast_export_stmt(str, ast->child[1], indent + 1); - zend_ast_export_indent(str, indent); - smart_str_appendc(str, '}'); - break; } + zend_ast_export_indent(str, indent); + smart_str_appendc(str, '}'); break; case ZEND_AST_SWITCH: smart_str_appends(str, "switch ("); From 3f3651a7870738e35ec541e53b53069152135b24 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 2 Mar 2015 11:56:33 +0300 Subject: [PATCH 7/7] opcode 137 is used for ZEND_OP_DATA and con't be reused for ZEND_ASSERT_CHECK --- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 100 ++++++++++++++++++++--------------------- Zend/zend_vm_opcodes.c | 4 +- Zend/zend_vm_opcodes.h | 2 +- 4 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 641316b61291c..2e9ffd6448f58 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6839,7 +6839,7 @@ ZEND_VM_HANDLER(122, ZEND_DEFINED, CONST, ANY) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(137, ZEND_ASSERT_CHECK, ANY, ANY) +ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, ANY) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ef46d6cf6dbe2..465ecc6dd45fc 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -40885,31 +40885,31 @@ void zend_init_opcodes_handlers(void) ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_HANDLER, ZEND_NULL_HANDLER, ZEND_ASSIGN_OBJ_SPEC_CV_CV_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, - ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -41235,31 +41235,31 @@ void zend_init_opcodes_handlers(void) ZEND_USER_OPCODE_SPEC_HANDLER, ZEND_USER_OPCODE_SPEC_HANDLER, ZEND_USER_OPCODE_SPEC_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, + ZEND_ASSERT_CHECK_SPEC_HANDLER, ZEND_JMP_SET_SPEC_CONST_HANDLER, ZEND_JMP_SET_SPEC_CONST_HANDLER, ZEND_JMP_SET_SPEC_CONST_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index da5f0acdf6a01..9bc531634d0c4 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -159,7 +159,7 @@ const char *zend_vm_opcodes_map[171] = { "ZEND_POST_INC_OBJ", "ZEND_POST_DEC_OBJ", "ZEND_ASSIGN_OBJ", - "ZEND_ASSERT_CHECK", + NULL, "ZEND_INSTANCEOF", "ZEND_DECLARE_CLASS", "ZEND_DECLARE_INHERITED_CLASS", @@ -173,7 +173,7 @@ const char *zend_vm_opcodes_map[171] = { "ZEND_ISSET_ISEMPTY_PROP_OBJ", "ZEND_HANDLE_EXCEPTION", "ZEND_USER_OPCODE", - NULL, + "ZEND_ASSERT_CHECK", "ZEND_JMP_SET", "ZEND_DECLARE_LAMBDA_FUNCTION", "ZEND_ADD_TRAIT", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 42fe03f298c97..e161ee0078a0a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -161,7 +161,6 @@ END_EXTERN_C() #define ZEND_POST_INC_OBJ 134 #define ZEND_POST_DEC_OBJ 135 #define ZEND_ASSIGN_OBJ 136 -#define ZEND_ASSERT_CHECK 137 #define ZEND_INSTANCEOF 138 #define ZEND_DECLARE_CLASS 139 #define ZEND_DECLARE_INHERITED_CLASS 140 @@ -174,6 +173,7 @@ END_EXTERN_C() #define ZEND_ISSET_ISEMPTY_PROP_OBJ 148 #define ZEND_HANDLE_EXCEPTION 149 #define ZEND_USER_OPCODE 150 +#define ZEND_ASSERT_CHECK 151 #define ZEND_JMP_SET 152 #define ZEND_DECLARE_LAMBDA_FUNCTION 153 #define ZEND_ADD_TRAIT 154