]> granicus.if.org Git - php/commitdiff
Statically bind static method call arguments
authorNikita Popov <nikic@php.net>
Sun, 13 Mar 2016 17:01:51 +0000 (18:01 +0100)
committerNikita Popov <nikic@php.net>
Mon, 14 Mar 2016 16:50:56 +0000 (17:50 +0100)
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.

Zend/zend_compile.c
ext/intl/tests/timezone_getCanonicalID_error.phpt
ext/opcache/Optimizer/optimize_func_calls.c

index 7cf00a0ebe0999145dd82bfc1fc82abf93da4a53..af3427b5b8149bfc4ae8a8a207a42807fa6097bc 100644 (file)
@@ -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);
 }
 /* }}} */
 
index e268e216a81d90c369475509af11fd33b52fc2aa..b29ca677017a97a04edb62782545750a9b05ec7e 100644 (file)
@@ -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
index a9fb259428d06becef72b90592fb684660461e2c..22829ad20d1de71ae5591bebdf34fe17b2097347 100644 (file)
@@ -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);
                                        }