From: Nikita Popov Date: Sun, 13 Mar 2016 17:01:51 +0000 (+0100) Subject: Statically bind static method call arguments X-Git-Tag: php-7.1.0alpha1~485 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=64dae1ea76c2c226fc375bf93858f9b2f5dae27f;p=php Statically bind static method call arguments If we know what method will be called, use ct-bound send opcodes. The intl test is changed because a runtime error changed to a compile-time error. --- diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7cf00a0ebe..af3427b5b8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3079,6 +3079,7 @@ ZEND_API zend_uchar zend_get_call_op(zend_uchar init_op, zend_function *fbc) /* } } else { if (zend_execute_ex == execute_ex && + !fbc->common.scope && !(fbc->common.fn_flags & ZEND_ACC_GENERATOR)) { return ZEND_DO_UCALL; } @@ -3646,6 +3647,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ znode class_node, method_node; zend_op *opline; + zend_function *fbc = NULL; zend_compile_class_ref_ex(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); @@ -3680,7 +3682,27 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{ } zend_check_live_ranges(opline); - zend_compile_call_common(result, args_ast, NULL); + /* Check if we already know which method we're calling */ + if (opline->op2_type == IS_CONST) { + zend_class_entry *ce = NULL; + if (opline->op1_type == IS_CONST) { + zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1); + ce = zend_hash_find_ptr(CG(class_table), lcname); + if (!ce && CG(active_class_entry) + && zend_string_equals_ci(CG(active_class_entry)->name, lcname)) { + ce = CG(active_class_entry); + } + } else if (opline->op1_type == IS_UNUSED && (opline->op1.num & ZEND_FETCH_CLASS_SELF) + && zend_is_scope_known()) { + ce = CG(active_class_entry); + } + if (ce) { + zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op2) + 1); + fbc = zend_hash_find_ptr(&ce->function_table, lcname); + } + } + + zend_compile_call_common(result, args_ast, fbc); } /* }}} */ diff --git a/ext/intl/tests/timezone_getCanonicalID_error.phpt b/ext/intl/tests/timezone_getCanonicalID_error.phpt index e268e216a8..b29ca67701 100644 --- a/ext/intl/tests/timezone_getCanonicalID_error.phpt +++ b/ext/intl/tests/timezone_getCanonicalID_error.phpt @@ -11,7 +11,6 @@ ini_set("intl.error_level", E_WARNING); var_dump(IntlTimeZone::getCanonicalID()); var_dump(IntlTimeZone::getCanonicalID(array())); var_dump(IntlTimeZone::getCanonicalID("foo\x81")); -var_dump(IntlTimeZone::getCanonicalID('foobar', null)); --EXPECTF-- @@ -28,8 +27,3 @@ bool(false) Warning: IntlTimeZone::getCanonicalID(): intltz_get_canonical_id: could not convert time zone id to UTF-16 in %s on line %d bool(false) - -Fatal error: Uncaught Error: Cannot pass parameter 2 by reference in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c index a9fb259428..22829ad20d 100644 --- a/ext/opcache/Optimizer/optimize_func_calls.c +++ b/ext/opcache/Optimizer/optimize_func_calls.c @@ -29,6 +29,9 @@ #include "zend_execute.h" #include "zend_vm.h" +#define ZEND_OP1_IS_CONST_STRING(opline) \ + (ZEND_OP1_TYPE(opline) == IS_CONST && \ + Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING) #define ZEND_OP2_IS_CONST_STRING(opline) \ (ZEND_OP2_TYPE(opline) == IS_CONST && \ Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING) @@ -58,17 +61,34 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_INIT_NS_FCALL_BY_NAME: if (ZEND_OP2_IS_CONST_STRING(opline)) { zend_function *func; - zval *function_name = &op_array->literals[opline->op2.constant + 1]; + zval *function_name = &ZEND_OP2_LITERAL(opline) + 1; if ((func = zend_hash_find_ptr(&ctx->script->function_table, Z_STR_P(function_name))) != NULL) { call_stack[call].func = func; } } + call_stack[call].opline = opline; + call++; + break; + case ZEND_INIT_STATIC_METHOD_CALL: + if (ZEND_OP2_IS_CONST_STRING(opline)) { + zend_class_entry *ce = NULL; + if (ZEND_OP1_IS_CONST_STRING(opline)) { + zend_string *class_name = Z_STR_P(&ZEND_OP1_LITERAL(opline) + 1); + ce = zend_hash_find_ptr(&ctx->script->class_table, class_name); + } else if (opline->op1_type == IS_UNUSED && op_array->scope + && (opline->op1.num & ZEND_FETCH_CLASS_SELF)) { + ce = op_array->scope; + } + if (ce) { + zend_string *func_name = Z_STR_P(&ZEND_OP2_LITERAL(opline) + 1); + call_stack[call].func = zend_hash_find_ptr(&ce->function_table, func_name); + } + } /* break missing intentionally */ case ZEND_NEW: case ZEND_INIT_DYNAMIC_CALL: case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_INIT_FCALL: case ZEND_INIT_USER_CALL: call_stack[call].opline = opline; @@ -97,6 +117,8 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) literal_dtor(&op_array->literals[fcall->op2.constant + 2]); fcall->op2.constant = fcall->op2.constant + 1; opline->opcode = zend_get_call_op(ZEND_INIT_FCALL, call_stack[call].func); + } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL) { + /* We don't have specialized opcodes for this, do nothing */ } else { ZEND_ASSERT(0); }