From b40afb415c1ff40a7d3d6e8206b1d6bc3628760b Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 15 Apr 2016 19:01:36 +0100 Subject: [PATCH 1/2] functional interfaces --- Zend/tests/functional_interfaces/001.phpt | 15 ++ Zend/tests/functional_interfaces/002.phpt | 12 ++ Zend/tests/functional_interfaces/003.phpt | 16 ++ Zend/tests/functional_interfaces/004.phpt | 15 ++ Zend/tests/functional_interfaces/005.phpt | 14 ++ Zend/tests/functional_interfaces/006.phpt | 19 ++ Zend/tests/functional_interfaces/007.phpt | 7 + Zend/tests/functional_interfaces/008.phpt | 7 + Zend/tests/functional_interfaces/009.phpt | 7 + Zend/tests/functional_interfaces/010.phpt | 30 +++ Zend/tests/functional_interfaces/011.phpt | 42 ++++ Zend/tests/functional_interfaces/012.phpt | 25 +++ Zend/tests/functional_interfaces/013.phpt | 70 +++++++ Zend/tests/functional_interfaces/014.phpt | 12 ++ Zend/tests/functional_interfaces/015.phpt | 12 ++ Zend/zend_ast.c | 3 +- Zend/zend_ast.h | 4 +- Zend/zend_closures.c | 64 ++++-- Zend/zend_closures.h | 4 +- Zend/zend_compile.c | 45 ++++- Zend/zend_inheritance.c | 139 ++++++++++++- Zend/zend_inheritance.h | 2 +- Zend/zend_interfaces.c | 12 +- Zend/zend_language_parser.y | 29 +-- Zend/zend_vm_def.h | 14 +- Zend/zend_vm_execute.h | 226 ++++++++++------------ Zend/zend_vm_opcodes.c | 2 +- 27 files changed, 687 insertions(+), 160 deletions(-) create mode 100644 Zend/tests/functional_interfaces/001.phpt create mode 100644 Zend/tests/functional_interfaces/002.phpt create mode 100644 Zend/tests/functional_interfaces/003.phpt create mode 100644 Zend/tests/functional_interfaces/004.phpt create mode 100644 Zend/tests/functional_interfaces/005.phpt create mode 100644 Zend/tests/functional_interfaces/006.phpt create mode 100644 Zend/tests/functional_interfaces/007.phpt create mode 100644 Zend/tests/functional_interfaces/008.phpt create mode 100644 Zend/tests/functional_interfaces/009.phpt create mode 100644 Zend/tests/functional_interfaces/010.phpt create mode 100644 Zend/tests/functional_interfaces/011.phpt create mode 100644 Zend/tests/functional_interfaces/012.phpt create mode 100644 Zend/tests/functional_interfaces/013.phpt create mode 100644 Zend/tests/functional_interfaces/014.phpt create mode 100644 Zend/tests/functional_interfaces/015.phpt diff --git a/Zend/tests/functional_interfaces/001.phpt b/Zend/tests/functional_interfaces/001.phpt new file mode 100644 index 0000000000000..de867c9f1c1ba --- /dev/null +++ b/Zend/tests/functional_interfaces/001.phpt @@ -0,0 +1,15 @@ +--TEST-- +functional interfaces: basic test +--FILE-- +method("turtles ?"); +--EXPECTF-- +string(9) "turtles ?" + + + + diff --git a/Zend/tests/functional_interfaces/007.phpt b/Zend/tests/functional_interfaces/007.phpt new file mode 100644 index 0000000000000..a0fbc34e6b4db --- /dev/null +++ b/Zend/tests/functional_interfaces/007.phpt @@ -0,0 +1,7 @@ +--TEST-- +functional interfaces: cannot implement self +--FILE-- +stream = fopen("php://stdout", "w"); + } + + public function getLogger() : ILog { + return function (string $message, ... $args) implements ILog { + fprintf($this->stream, $message, ... $args); + }; + } + + protected $stream; +} + +$foo = new Foo(); +$logger = + $foo->getLogger(); +$logger->log("php7 baby\n"); +?> +--EXPECTF-- +php7 baby diff --git a/Zend/tests/functional_interfaces/011.phpt b/Zend/tests/functional_interfaces/011.phpt new file mode 100644 index 0000000000000..39321a3ef41af --- /dev/null +++ b/Zend/tests/functional_interfaces/011.phpt @@ -0,0 +1,42 @@ +--TEST-- +functional interfaces: call method handling +--FILE-- +bar = $bar; + } + + public function getShufflingIterator() : Traversable { + + return function() implements IteratorAggregate : Traversable { + shuffle($this->bar); + + return new ArrayIterator($this->bar); + }; + } +} + +$foo = new Foo([ + "hello", + "world" +]); + +$shuffler = + $foo->getShufflingIterator(); + +foreach ($shuffler as $key => $value) { + var_dump($value); +} + +foreach ($shuffler as $key => $value) { + var_dump($value); +} +?> +--EXPECTF-- +string(5) "%s" +string(5) "%s" +string(5) "%s" +string(5) "%s" diff --git a/Zend/tests/functional_interfaces/012.phpt b/Zend/tests/functional_interfaces/012.phpt new file mode 100644 index 0000000000000..ba52ed05b94a4 --- /dev/null +++ b/Zend/tests/functional_interfaces/012.phpt @@ -0,0 +1,25 @@ +--TEST-- +functional interfaces: binding +--FILE-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/Zend/tests/functional_interfaces/013.phpt b/Zend/tests/functional_interfaces/013.phpt new file mode 100644 index 0000000000000..080ee05c85318 --- /dev/null +++ b/Zend/tests/functional_interfaces/013.phpt @@ -0,0 +1,70 @@ +--TEST-- +functional interfaces: call method without ce and proxy +--FILE-- +bar[] = mt_rand($i, $limit); + } + } + + public function getEvenCounter() : Countable { + return function () implements Countable { + $counter = 0; + foreach ($this->bar as $value) { + if ($value % 2 === 0) + $counter++; + } + return $counter; + }; + } + + public function getOddCounter() : Countable { + return function () implements Countable { + $counter = 0; + foreach ($this->bar as $value) { + if ($value % 2 !== 0) { + $counter++; + } + } + return $counter; + }; + } +} + +$foo = new Foo(); + +$even = $foo->getEvenCounter(); +$odd = $foo->getOddCounter(); + +$it = 0; + +while (++$it<10) { + $foo->fill(50); + var_dump( + count($even), + count($odd)); +} +?> +--EXPECTF-- +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) diff --git a/Zend/tests/functional_interfaces/014.phpt b/Zend/tests/functional_interfaces/014.phpt new file mode 100644 index 0000000000000..242aef884e8c1 --- /dev/null +++ b/Zend/tests/functional_interfaces/014.phpt @@ -0,0 +1,12 @@ +--TEST-- +functional interfaces: non static implementation of static interface +--FILE-- +child[1] = child1; ast->child[2] = child2; ast->child[3] = child3; + ast->child[4] = child4; return (zend_ast *) ast; } diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index ec771003c0c9d..5cde073036cc1 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -184,7 +184,7 @@ typedef struct _zend_ast_decl { unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; - zend_ast *child[4]; + zend_ast *child[5]; } zend_ast_decl; typedef void (*zend_ast_process_t)(zend_ast *ast); @@ -197,7 +197,7 @@ ZEND_API zend_ast *zend_ast_create(zend_ast_kind kind, ...); ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 ); ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind kind, ...); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index fd0738f32f95d..d4c964c6e63a4 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -25,6 +25,7 @@ #include "zend_closures.h" #include "zend_exceptions.h" #include "zend_interfaces.h" +#include "zend_inheritance.h" #include "zend_objects.h" #include "zend_objects_API.h" #include "zend_globals.h" @@ -42,6 +43,7 @@ typedef struct _zend_closure { zend_function func; zval this_ptr; zend_class_entry *called_scope; + zend_class_entry *interface; void (*orig_internal_handler)(INTERNAL_FUNCTION_PARAMETERS); } zend_closure; @@ -155,7 +157,7 @@ ZEND_METHOD(Closure, call) if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; - zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis); + zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis, closure->interface); closure = (zend_closure *) Z_OBJ(new_closure); fci_cache.function_handler = &closure->func; } else { @@ -228,7 +230,7 @@ ZEND_METHOD(Closure, bind) called_scope = ce; } - zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); + zend_create_closure(return_value, &closure->func, ce, called_scope, newthis, closure->interface); new_closure = (zend_closure *) Z_OBJ_P(return_value); /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ @@ -276,12 +278,34 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* { } invoke->internal_function.handler = ZEND_MN(Closure___invoke); invoke->internal_function.module = 0; - invoke->internal_function.scope = zend_ce_closure; + invoke->internal_function.scope = object->ce; invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0); return invoke; } /* }}} */ +ZEND_API zend_function *zend_get_closure_interface_method(zend_string *name, zend_function *func, zend_class_entry *interface) /* {{{ */ +{ + zend_function *invoke = (zend_function*) emalloc(sizeof(zend_function)); + const uint32_t keep_flags = + ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE; + + invoke->common = func->common; + invoke->type = ZEND_INTERNAL_FUNCTION; + invoke->internal_function.fn_flags = + ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (func->common.fn_flags & keep_flags); + if (func->type != ZEND_INTERNAL_FUNCTION || (func->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { + invoke->internal_function.fn_flags |= + ZEND_ACC_USER_ARG_INFO; + } + invoke->internal_function.handler = ZEND_MN(Closure___invoke); + invoke->internal_function.module = 0; + invoke->internal_function.scope = interface; + invoke->internal_function.function_name = zend_string_copy(name); + return invoke; +} +/* }}} */ + ZEND_API const zend_function *zend_get_closure_method_def(zval *obj) /* {{{ */ { zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); @@ -296,13 +320,21 @@ ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */ } /* }}} */ -static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ +zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ { + zend_function *func; + if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) { return zend_get_closure_invoke_method(*object); } - return std_object_handlers.get_method(object, method, key); + func = std_object_handlers.get_method(object, method, key); + + if (func && func->common.prototype) { + return zend_get_closure_interface_method(method, func, (*object)->ce); + } + + return func; } /* }}} */ @@ -380,8 +412,8 @@ static zend_object *zend_closure_clone(zval *zobject) /* {{{ */ zend_closure *closure = (zend_closure *)Z_OBJ_P(zobject); zval result; - zend_create_closure(&result, &closure->func, - closure->func.common.scope, closure->called_scope, &closure->this_ptr); + zend_create_closure(&result, &closure->func,closure->func.common.scope, closure->called_scope, &closure->this_ptr, closure->interface); + return Z_OBJ(result); } /* }}} */ @@ -548,20 +580,26 @@ static void zend_closure_internal_handler(INTERNAL_FUNCTION_PARAMETERS) /* {{{ * } /* }}} */ -ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ -{ +ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, zend_class_entry *interface) { /* {{{ */ zend_closure *closure; - object_init_ex(res, zend_ce_closure); + object_init_ex(res, interface ? interface : zend_ce_closure); closure = (zend_closure *)Z_OBJ_P(res); if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) { /* use dummy scope if we're binding an object without specifying a scope */ /* maybe it would be better to create one for this purpose */ - scope = zend_ce_closure; + scope = Z_OBJCE_P(res); + } else { + /* we use the interfaces scope if not rebinding */ + if ((scope == NULL ) && interface) { + scope = Z_OBJCE_P(res); + } } + closure->interface = interface; + if (func->type == ZEND_USER_FUNCTION) { memcpy(&closure->func, func, sizeof(zend_op_array)); closure->func.common.prototype = (zend_function*)closure; @@ -585,7 +623,7 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) { /* avoid infinity recursion, by taking handler from nested closure */ zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func)); - ZEND_ASSERT(nested->std.ce == zend_ce_closure); + ZEND_ASSERT(instanceof_function(nested->std.ce, zend_ce_closure)); closure->orig_internal_handler = nested->orig_internal_handler; } else { closure->orig_internal_handler = closure->func.internal_function.handler; @@ -616,7 +654,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas { zend_closure *closure; - zend_create_closure(res, func, scope, called_scope, this_ptr); + zend_create_closure(res, func, scope, called_scope, this_ptr, NULL); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 7f8bac430c98f..d60a14992b91d 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -26,12 +26,14 @@ BEGIN_EXTERN_C() void zend_register_closure_ce(void); void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var); +zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key); extern ZEND_API zend_class_entry *zend_ce_closure; -ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); +ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, zend_class_entry *interface); ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); +ZEND_API zend_function *zend_get_closure_interface_method(zend_string *name, zend_function *func, zend_class_entry *type); ZEND_API const zend_function *zend_get_closure_method_def(zval *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index da2c83c55ecd3..88b85faee3ce7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5265,7 +5265,7 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo } /* }}} */ -static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl) /* {{{ */ +static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, zend_ast *interface_ast) /* {{{ */ { zend_ast *params_ast = decl->child[0]; zend_string *name = decl->name, *lcname, *key; @@ -5294,9 +5294,48 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as zend_hash_update_ptr(CG(function_table), key, op_array); if (op_array->fn_flags & ZEND_ACC_CLOSURE) { + znode inode; + + inode.op_type = IS_UNUSED; + if (interface_ast) { + zend_string *interface_name = zend_ast_get_str(interface_ast); + uint32_t fetch_type = zend_get_class_fetch_type(interface_name); + + if (fetch_type != ZEND_FETCH_CLASS_DEFAULT) { + switch (fetch_type) { + case ZEND_FETCH_CLASS_PARENT: + zend_error_noreturn(E_COMPILE_ERROR, "functional interface cannot implement parent"); + break; + + case ZEND_FETCH_CLASS_STATIC: + zend_error_noreturn(E_COMPILE_ERROR, "functional interface cannot implement static"); + break; + + case ZEND_FETCH_CLASS_SELF: + zend_error_noreturn(E_COMPILE_ERROR, "functional interface cannot implement self"); + break; + } + } + + opline = zend_emit_op(&inode, ZEND_FETCH_CLASS, NULL, NULL); + opline->extended_value = fetch_type | ZEND_FETCH_CLASS_EXCEPTION; + opline->op2_type = IS_CONST; + opline->op2.constant = + zend_add_class_name_literal( + CG(active_op_array), + zend_resolve_class_name( + interface_name, interface_ast->attr)); + zend_string_release(interface_name); + } + opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); opline->op1_type = IS_CONST; LITERAL_STR(opline->op1, key); + if (inode.op_type != IS_UNUSED) { + SET_NODE(opline->op2, &inode); + } else { + SET_UNUSED(opline->op2); + } } else { opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_DECLARE_FUNCTION; @@ -5317,6 +5356,8 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ zend_ast *uses_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; zend_ast *return_type_ast = decl->child[3]; + zend_ast *interface_ast = decl->child[4]; + zend_bool is_method = decl->kind == ZEND_AST_METHOD; zend_op_array *orig_op_array = CG(active_op_array); @@ -5340,7 +5381,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */ zend_bool has_body = stmt_ast != NULL; zend_begin_method_decl(op_array, decl->name, has_body); } else { - zend_begin_func_decl(result, op_array, decl); + zend_begin_func_decl(result, op_array, decl, interface_ast); if (uses_ast) { zend_compile_closure_binding(result, uses_ast); } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 8e48a3446cc79..d503507e96964 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -24,6 +24,7 @@ #include "zend_inheritance.h" #include "zend_smart_str.h" #include "zend_inheritance.h" +#include "zend_closures.h" static void overriden_ptr_dtor(zval *zv) /* {{{ */ { @@ -88,6 +89,45 @@ static zend_function *zend_duplicate_function(zend_function *func, zend_class_en } /* }}} */ +static zend_function *zend_duplicate_function_ex(zend_function *func, zend_class_entry *ce, zend_function *prototype) /* {{{ */ +{ + zend_function *new_function; + + if (UNEXPECTED(func->type == ZEND_INTERNAL_FUNCTION)) { + if (UNEXPECTED(ce->type & ZEND_INTERNAL_CLASS)) { + new_function = pemalloc(sizeof(zend_internal_function), 1); + memcpy(new_function, func, sizeof(zend_internal_function)); + } else { + new_function = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function)); + memcpy(new_function, func, sizeof(zend_internal_function)); + new_function->common.fn_flags |= ZEND_ACC_ARENA_ALLOCATED; + } + if (EXPECTED(new_function->common.function_name)) { + zend_string_addref(new_function->common.function_name); + } + } else { + if (func->op_array.refcount) { + (*func->op_array.refcount)++; + } + if (func->op_array.static_variables && + !(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { + GC_REFCOUNT(func->op_array.static_variables)++; + } + new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); + memcpy(new_function, func, sizeof(zend_op_array)); + } + + zend_string_release(new_function->common.function_name); + new_function->common.function_name = + zend_string_copy(prototype->common.function_name); + + new_function->common.scope = ce; + new_function->common.prototype = prototype; + + return new_function; +} +/* }}} */ + static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ { ZEND_ASSERT(ce->parent != NULL); @@ -759,7 +799,7 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa } /* }}} */ -ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ +void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool ignore_final_class) /* {{{ */ { zend_property_info *property_info; zend_function *func; @@ -779,7 +819,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } /* Class must not extend a final class */ - if (parent_ce->ce_flags & ZEND_ACC_FINAL) { + if (parent_ce->ce_flags & ZEND_ACC_FINAL && !ignore_final_class) { zend_error_noreturn(E_COMPILE_ERROR, "Class %s may not inherit from final class (%s)", ZSTR_VAL(ce->name), ZSTR_VAL(parent_ce->name)); } } @@ -937,6 +977,12 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } /* }}} */ +ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */ +{ + zend_do_inheritance_ex(ce, parent_ce, 0); +} +/* }}} */ + static zend_bool do_inherit_constant_check(HashTable *child_constants_table, zend_class_constant *parent_constant, zend_string *name, const zend_class_entry *iface) /* {{{ */ { zend_class_constant *old_constant; @@ -1694,6 +1740,95 @@ void zend_check_deprecated_constructor(const zend_class_entry *ce) /* {{{ */ } /* }}} */ +static inline zend_string* zend_get_functional_interface_name(zend_string *name, zend_class_entry *interface) { /* {{{ */ + zend_string *type_name = zend_string_alloc( + ZSTR_LEN(interface->name) + ZSTR_LEN(name) + sizeof("{}"), 0); + + memcpy(&ZSTR_VAL(type_name)[0], "{", sizeof("{")-1); + memcpy(&ZSTR_VAL(type_name)[(sizeof("{")-1)], ZSTR_VAL(interface->name), ZSTR_LEN(interface->name)); + memcpy(&ZSTR_VAL(type_name)[(sizeof("{")-1) + ZSTR_LEN(interface->name)], "}", sizeof("}") - 1); + memcpy(&ZSTR_VAL(type_name)[(sizeof("{")-1) + ZSTR_LEN(interface->name) + (sizeof("}")-1)], ZSTR_VAL(name), ZSTR_LEN(name)); + + return type_name; +} /* }}} */ + +static inline uint32_t zend_get_functional_interface_method(zend_string **key, zend_function **prototype, zend_class_entry *interface) { /* {{{ */ + zend_string *_key = NULL; + zend_function *_prototype = NULL; + uint32_t methods = 0; + + ZEND_HASH_FOREACH_STR_KEY_PTR(&interface->function_table, _key, _prototype) { + *prototype = _prototype; + *key = _key; + + if (++methods > 1) { + break; + } + } ZEND_HASH_FOREACH_END(); + + return methods; +} /* }}} */ + +ZEND_API zend_class_entry* zend_get_functional_interface(zend_string *name, zend_function *func, zend_class_entry *interface) { /* {{{ */ + zend_class_entry *type; + zend_string *type_name; + zend_string *key = NULL; + zend_function *prototype = NULL; + + if (!interface) { + return zend_ce_closure; + } + + type_name = zend_get_functional_interface_name(name, interface); + + if ((type = zend_hash_find_ptr(CG(class_table), type_name))) { + zend_string_release(type_name); + return type; + } + + if (zend_get_functional_interface_method(&key, &prototype, interface) != 1) { + zend_error_noreturn(E_COMPILE_ERROR, + "cannot implement non functional interface %s", + ZSTR_VAL(interface->name)); + } + + if (!(interface->ce_flags & ZEND_ACC_INTERFACE)) { + zend_error_noreturn(E_COMPILE_ERROR, + "cannot implement non interface %s", + ZSTR_VAL(interface->name)); + } + + if (((prototype->common.fn_flags & ZEND_ACC_STATIC) && !(func->common.fn_flags & ZEND_ACC_STATIC))) { + zend_error_noreturn(E_COMPILE_ERROR, + "cannot create non static implementation of static functional interface %s", + ZSTR_VAL(interface->name)); + } + + if (((func->common.fn_flags & ZEND_ACC_STATIC) && !(prototype->common.fn_flags & ZEND_ACC_STATIC))) { + zend_error_noreturn(E_COMPILE_ERROR, + "cannot create static implementation of non static functional interface %s", + ZSTR_VAL(interface->name)); + } + + type = (zend_class_entry*) zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); + + zend_initialize_class_data(type, 1); + + type->name = type_name; + type->type = ZEND_USER_CLASS; + type->refcount = 1; + type->ce_flags |= ZEND_ACC_FINAL; + + func = zend_duplicate_function_ex(func, type, prototype); + func->common.fn_flags &= ~ZEND_ACC_CLOSURE; + zend_hash_update_ptr( + &type->function_table, key, func); + zend_do_inheritance_ex(type, zend_ce_closure, 1); + zend_class_implements(type, 1, interface); + + return (zend_class_entry*) zend_hash_add_ptr(CG(class_table), type->name, type); +} /* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index bbe43b0ccda8b..a63c7bf18bb9b 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -34,7 +34,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent void zend_do_early_binding(void); void zend_check_deprecated_constructor(const zend_class_entry *ce); - +ZEND_API zend_class_entry* zend_get_functional_interface(zend_string *name, zend_function *func, zend_class_entry *interface); END_EXTERN_C() #endif diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index c7d225704f066..89b449e5d697e 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -22,6 +22,7 @@ #include "zend_API.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_closures.h" ZEND_API zend_class_entry *zend_ce_traversable; ZEND_API zend_class_entry *zend_ce_aggregate; @@ -56,7 +57,7 @@ ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_fun fci.params = params; fci.no_separation = 1; - if (!fn_proxy && !obj_ce) { + if (!fn_proxy && !obj_ce && fci.object && Z_OBJ_HT_P(object)->get_method != zend_closure_get_method) { /* no interest in caching and no information already present that is * needed later inside zend_call_function. */ fci.function_table = !object ? EG(function_table) : NULL; @@ -75,11 +76,16 @@ ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_fun function_table = EG(function_table); } if (!fn_proxy || !*fn_proxy) { - if ((fcic.function_handler = zend_hash_find_ptr(function_table, Z_STR(fci.function_name))) == NULL) { + if (object && Z_TYPE_P(object) == IS_OBJECT && Z_OBJ_HT_P(object)->get_method == zend_closure_get_method) { + fcic.function_handler = Z_OBJ_HT_P(object)->get_method(&Z_OBJ_P(object), Z_STR(fci.function_name), NULL); + } else fcic.function_handler = zend_hash_find_ptr(function_table, Z_STR(fci.function_name)); + + if (fcic.function_handler == NULL) { /* error at c-level */ zend_error_noreturn(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? ZSTR_VAL(obj_ce->name) : "", obj_ce ? "::" : "", function_name); } - if (fn_proxy) { + + if (fn_proxy && (!object || Z_TYPE_P(object) != IS_OBJECT || Z_OBJ_HT_P(object)->get_method != zend_closure_get_method)) { *fn_proxy = fcic.function_handler; } } else { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 53b2f3f50bea1..70f66b9b44bbe 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -225,7 +225,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); /* Token used to force a parse error from the lexer */ %token T_ERROR -%type top_statement namespace_name name statement function_declaration_statement +%type top_statement namespace_name name statement function_declaration_statement closure_interface %type class_declaration_statement trait_declaration_statement %type interface_declaration_statement interface_extends_list %type group_use_declaration inline_use_declarations inline_use_declaration @@ -478,7 +478,7 @@ function_declaration_statement: function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type '{' inner_statement_list '}' { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2, $1, $4, - zend_ast_get_str($3), $6, NULL, $10, $8); } + zend_ast_get_str($3), $6, NULL, $10, $8, NULL); } ; is_reference: @@ -494,10 +494,10 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } | T_CLASS { $$ = CG(zend_lineno); } T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } ; class_modifiers: @@ -513,13 +513,13 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } ; extends_from: @@ -710,7 +710,7 @@ class_statement: | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' return_type method_body { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1, $2, $5, - zend_ast_get_str($4), $7, NULL, $10, $9); } + zend_ast_get_str($4), $7, NULL, $10, $9, NULL); } ; name_list: @@ -842,7 +842,7 @@ anonymous_class: extends_from implements_list backup_doc_comment '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, NULL, - $4, $5, $8, NULL); + $4, $5, $8, NULL, NULL); $$ = zend_ast_create(ZEND_AST_NEW, decl, $3); } ; @@ -854,6 +854,11 @@ new_expr: { $$ = $2; } ; +closure_interface: + /* empty */ { $$ = NULL; } + | T_IMPLEMENTS class_name { $$ = $2; } +; + expr_without_variable: T_LIST '(' assignment_list ')' '=' expr { $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); } @@ -961,16 +966,16 @@ expr_without_variable: | T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); } | T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); } | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); } - | function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type + | function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars closure_interface return_type '{' inner_statement_list '}' { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $5, $7, $10, $8); } + $5, $7, $11, $9, $8); } | T_STATIC function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars - return_type '{' inner_statement_list '}' + closure_interface return_type '{' inner_statement_list '}' { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $3 | ZEND_ACC_STATIC, $2, $4, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $6, $8, $11, $9); } + $6, $8, $12, $10, $9); } ; function: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b96ce22c0ddec..b388921ce3a43 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7397,12 +7397,13 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED) +ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, ANY) { USE_OPLINE zval *zfunc; zval *object; zend_class_entry *called_scope; + zend_class_entry *interface = NULL; SAVE_OPLINE(); @@ -7421,8 +7422,15 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED) called_scope = Z_CE(EX(This)); object = NULL; } - zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), - EG(scope), called_scope, object); + + if (OP2_TYPE != IS_UNUSED) { + interface = zend_get_functional_interface( + Z_STR_P(EX_CONSTANT(opline->op1)), + Z_FUNC_P(zfunc), Z_CE_P(EX_VAR(opline->op2.var))); + } + + zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), + EG(scope), called_scope, object, interface); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b43ea8f159fd4..386ffc26fcadd 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4064,6 +4064,44 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(Z ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *zfunc; + zval *object; + zend_class_entry *called_scope; + zend_class_entry *interface = NULL; + + SAVE_OPLINE(); + + zfunc = zend_hash_find(EG(function_table), Z_STR_P(EX_CONSTANT(opline->op1))); + ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION); + + if (Z_TYPE(EX(This)) == IS_OBJECT) { + called_scope = Z_OBJCE(EX(This)); + if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) || + (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { + object = NULL; + } else { + object = &EX(This); + } + } else { + called_scope = Z_CE(EX(This)); + object = NULL; + } + + if (opline->op2_type != IS_UNUSED) { + interface = zend_get_functional_interface( + Z_STR_P(EX_CONSTANT(opline->op1)), + Z_FUNC_P(zfunc), Z_CE_P(EX_VAR(opline->op2.var))); + } + + zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), + EG(scope), called_scope, object, interface); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8088,36 +8126,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zval *zfunc; - zval *object; - zend_class_entry *called_scope; - - SAVE_OPLINE(); - - zfunc = zend_hash_find(EG(function_table), Z_STR_P(EX_CONSTANT(opline->op1))); - ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION); - - if (Z_TYPE(EX(This)) == IS_OBJECT) { - called_scope = Z_OBJCE(EX(This)); - if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) || - (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { - object = NULL; - } else { - object = &EX(This); - } - } else { - called_scope = Z_CE(EX(This)); - object = NULL; - } - zend_create_closure(EX_VAR(opline->result.var), Z_FUNC_P(zfunc), - EG(scope), called_scope, object); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -58645,27 +58653,7 @@ void zend_init_opcodes_handlers(void) ZEND_JMP_SET_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_JMP_SET_SPEC_CV_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_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_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -60089,7 +60077,7 @@ void zend_init_opcodes_handlers(void) 776 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_RETVAL, 826 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 851 | SPEC_RULE_OP1, - 3845, + 3825, 856, 857 | SPEC_RULE_OP1, 862 | SPEC_RULE_OP1, @@ -60097,9 +60085,9 @@ void zend_init_opcodes_handlers(void) 872 | SPEC_RULE_OP1, 877 | SPEC_RULE_OP1, 882 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 3845, - 3845, - 3845, + 3825, + 3825, + 3825, 907 | SPEC_RULE_OP1, 912 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 937 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -60148,7 +60136,7 @@ void zend_init_opcodes_handlers(void) 1646 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1671 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1696 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 3845, + 3825, 1721, 1722, 1723, @@ -60201,38 +60189,38 @@ void zend_init_opcodes_handlers(void) 2438, 2439, 2440 | SPEC_RULE_OP1, - 2445 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2470, - 2471, - 2472 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2497, - 2498, - 2499, - 2500 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2525 | SPEC_RULE_OP1, - 2530, - 2531, - 2532, - 2533, - 2534 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2584 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2609 | SPEC_RULE_OP1, - 2614 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2639, - 2640 | SPEC_RULE_OP2, - 2645 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2670 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2695 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2720 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2745 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2770 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2795 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2820 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2845 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2870 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 2895 | SPEC_RULE_OP1 | SPEC_RULE_OP2, - 3845 + 2445 | SPEC_RULE_OP1, + 2450, + 2451, + 2452 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2477, + 2478, + 2479, + 2480 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2505 | SPEC_RULE_OP1, + 2510, + 2511, + 2512, + 2513, + 2514 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2539 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2564 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2589 | SPEC_RULE_OP1, + 2594 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2619, + 2620 | SPEC_RULE_OP2, + 2625 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2650 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2675 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2700 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2725 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2750 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2775 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2800 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2825 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2850 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 2875 | SPEC_RULE_OP1 | SPEC_RULE_OP2, + 3825 }; zend_opcode_handlers = labels; zend_handlers_count = sizeof(labels) / sizeof(void*); @@ -60331,7 +60319,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2920 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2900 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60339,7 +60327,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2945 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2925 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60347,7 +60335,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2970 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2950 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60358,17 +60346,17 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2995 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2975 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if ((op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG)) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3020 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3000 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3045 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3025 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -60376,7 +60364,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3070 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3050 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60384,7 +60372,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3095 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3075 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60392,7 +60380,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3120 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3100 | SPEC_RULE_OP1 | SPEC_RULE_OP2; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60403,7 +60391,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3145 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3125 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60411,7 +60399,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3220 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3200 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60422,7 +60410,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3295 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3275 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60430,7 +60418,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3370 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3350 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; if (op->op1_type > op->op2_type) { zend_swap_operands(op); } @@ -60441,12 +60429,12 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3445 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3425 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3520 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3500 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -60454,55 +60442,55 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3595 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3575 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if ((op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE)) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3670 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3650 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if ((op1_info == MAY_BE_DOUBLE)) { - spec = 3835 | SPEC_RULE_OP1; + spec = 3815 | SPEC_RULE_OP1; } else if ((!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3840 | SPEC_RULE_OP1; + spec = 3820 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) { - spec = 3745 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3725 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if ((op1_info == MAY_BE_LONG)) { - spec = 3755 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3735 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) { - spec = 3765 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3745 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) { - spec = 3775 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3755 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if ((op1_info == MAY_BE_LONG)) { - spec = 3785 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3765 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) { - spec = 3795 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; + spec = 3775 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) { - spec = 3805 | SPEC_RULE_OP1; + spec = 3785 | SPEC_RULE_OP1; } else if ((op1_info == MAY_BE_LONG)) { - spec = 3810 | SPEC_RULE_OP1; + spec = 3790 | SPEC_RULE_OP1; } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) { - spec = 3815 | SPEC_RULE_OP1; + spec = 3795 | SPEC_RULE_OP1; } break; case ZEND_POST_DEC: if ((res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG)) { - spec = 3820 | SPEC_RULE_OP1; + spec = 3800 | SPEC_RULE_OP1; } else if ((op1_info == MAY_BE_LONG)) { - spec = 3825 | SPEC_RULE_OP1; + spec = 3805 | SPEC_RULE_OP1; } else if ((op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE))) { - spec = 3830 | SPEC_RULE_OP1; + spec = 3810 | SPEC_RULE_OP1; } break; default: diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index d4c76ad2657a1..63531ef54543f 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -362,7 +362,7 @@ static uint32_t zend_vm_opcodes_flags[184] = { 0x00000000, 0x00002000, 0x00002003, - 0x00000103, + 0x00000003, 0x00000000, 0x00000000, 0x00000101, From 5fe7d18adec368b1e69947c5e9a9e9b32cb57f1e Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Tue, 19 Apr 2016 10:02:56 +0100 Subject: [PATCH 2/2] fix ast destroy --- Zend/zend_ast.c | 3 +++ Zend/zend_compile.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index c38252fe5d142..8f70ccd36366d 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -473,6 +473,9 @@ static void zend_ast_destroy_ex(zend_ast *ast, zend_bool free) { zend_ast_destroy_ex(decl->child[1], free); zend_ast_destroy_ex(decl->child[2], free); zend_ast_destroy_ex(decl->child[3], free); + if (decl->child[4]) { + zend_ast_destroy_ex(decl->child[4], free); + } break; } default: diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 88b85faee3ce7..d0e657b5cc0f1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5325,7 +5325,6 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as CG(active_op_array), zend_resolve_class_name( interface_name, interface_ast->attr)); - zend_string_release(interface_name); } opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);