summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-04-27 17:18:35 +0300
committerDmitry Stogov <dmitry@zend.com>2015-04-27 17:18:35 +0300
commit5af7743cf58056543886f9b2af183be2acac4e78 (patch)
tree842321edba852f4f03e2700f1502e724c7eee998 /Zend
parentffe804a50c687673e2c46a0d4a43ab5eb3604281 (diff)
parent8acd7f49dafa9b9743d2921b3f3e49503d9fe342 (diff)
downloadphp-git-5af7743cf58056543886f9b2af183be2acac4e78.tar.gz
Merge branch 'anon'
* anon: Pass class_entry through IS_VAR to avoid hash lookup in ZEND_NEW. Use "safe" anonymous class names. Don't show the mangled names through var_dump(). Refactored using specialized opcodes Fix implementing_class handling More cleanup (mainly retab) Don't issue FETCH_CLASS for anon classes More retab Simply anon class name generation Retab Revert unrelated changes / rebase fixup Rebase Joe's anon classes implementation
Diffstat (limited to 'Zend')
-rw-r--r--Zend/tests/anon/001.phpt10
-rw-r--r--Zend/tests/anon/002.phpt21
-rw-r--r--Zend/tests/anon/003.phpt54
-rw-r--r--Zend/tests/anon/004.phpt30
-rw-r--r--Zend/tests/anon/005.phpt36
-rw-r--r--Zend/tests/anon/006.phpt15
-rw-r--r--Zend/tests/anon/007.phpt23
-rw-r--r--Zend/tests/anon/008.phpt23
-rw-r--r--Zend/tests/anon/009.phpt18
-rw-r--r--Zend/tests/anon/010.phpt23
-rw-r--r--Zend/zend_ast.c4
-rw-r--r--Zend/zend_compile.c86
-rw-r--r--Zend/zend_compile.h2
-rw-r--r--Zend/zend_language_parser.y15
-rw-r--r--Zend/zend_opcode.c4
-rw-r--r--Zend/zend_vm_def.h42
-rw-r--r--Zend/zend_vm_execute.h92
-rw-r--r--Zend/zend_vm_opcodes.c4
-rw-r--r--Zend/zend_vm_opcodes.h2
19 files changed, 489 insertions, 15 deletions
diff --git a/Zend/tests/anon/001.phpt b/Zend/tests/anon/001.phpt
new file mode 100644
index 0000000000..75589550a8
--- /dev/null
+++ b/Zend/tests/anon/001.phpt
@@ -0,0 +1,10 @@
+--TEST--
+declare bare anonymous class
+--FILE--
+<?php
+var_dump(new class{});
+--EXPECTF--
+object(class@%s)#%d (0) {
+}
+
+
diff --git a/Zend/tests/anon/002.phpt b/Zend/tests/anon/002.phpt
new file mode 100644
index 0000000000..1cbaf81a9f
--- /dev/null
+++ b/Zend/tests/anon/002.phpt
@@ -0,0 +1,21 @@
+--TEST--
+declare anonymous class extending another
+--FILE--
+<?php
+class A{}
+
+interface B{
+ public function method();
+}
+
+$a = new class extends A implements B {
+ public function method(){
+ return true;
+ }
+};
+
+var_dump($a instanceof A, $a instanceof B);
+--EXPECTF--
+bool(true)
+bool(true)
+
diff --git a/Zend/tests/anon/003.phpt b/Zend/tests/anon/003.phpt
new file mode 100644
index 0000000000..ac023f43a5
--- /dev/null
+++ b/Zend/tests/anon/003.phpt
@@ -0,0 +1,54 @@
+--TEST--
+reusing anonymous classes
+--FILE--
+<?php
+while (@$i++<10) {
+ var_dump(new class($i) {
+
+ public function __construct($i) {
+ $this->i = $i;
+ }
+ });
+}
+--EXPECTF--
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(1)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(2)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(3)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(4)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(5)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(6)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(7)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(8)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(9)
+}
+object(class@%s)#1 (1) {
+ ["i"]=>
+ int(10)
+}
+
diff --git a/Zend/tests/anon/004.phpt b/Zend/tests/anon/004.phpt
new file mode 100644
index 0000000000..f72e7255de
--- /dev/null
+++ b/Zend/tests/anon/004.phpt
@@ -0,0 +1,30 @@
+--TEST--
+testing anonymous inheritance
+--FILE--
+<?php
+class Outer {
+ protected $data;
+
+ public function __construct($data) {
+ $this->data = $data;
+ }
+
+ public function getArrayAccess() {
+ /* create a proxy object implementing array access */
+ return new class($this->data) extends Outer implements ArrayAccess {
+ public function offsetGet($offset) { return $this->data[$offset]; }
+ public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); }
+ public function offsetUnset($offset) { unset($this->data[$offset]); }
+ public function offsetExists($offset) { return isset($this->data[$offset]); }
+ };
+ }
+}
+
+$outer = new Outer(array(
+ rand(1, 100)
+));
+
+/* not null because inheritance */
+var_dump($outer->getArrayAccess()[0]);
+--EXPECTF--
+int(%d)
diff --git a/Zend/tests/anon/005.phpt b/Zend/tests/anon/005.phpt
new file mode 100644
index 0000000000..7f1ff0755a
--- /dev/null
+++ b/Zend/tests/anon/005.phpt
@@ -0,0 +1,36 @@
+--TEST--
+testing reusing anons that implement an interface
+--FILE--
+<?php
+class Outer {
+ protected $data;
+
+ public function __construct(&$data) {
+ /* array access will be implemented by the time we get to here */
+ $this->data = &$data;
+ }
+
+ public function getArrayAccess() {
+ /* create a child object implementing array access */
+ /* this grants you access to protected methods and members */
+ return new class($this->data) implements ArrayAccess {
+ public function offsetGet($offset) { return $this->data[$offset]; }
+ public function offsetSet($offset, $data) { return ($this->data[$offset] = $data); }
+ public function offsetUnset($offset) { unset($this->data[$offset]); }
+ public function offsetExists($offset) { return isset($this->data[$offset]); }
+ };
+ }
+}
+
+$data = array(
+ rand(1, 100),
+ rand(2, 200)
+);
+
+$outer = new Outer($data);
+$proxy = $outer->getArrayAccess();
+
+/* null because no inheritance, so no access to protected member */
+var_dump(@$outer->getArrayAccess()[0]);
+--EXPECT--
+NULL
diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt
new file mode 100644
index 0000000000..e5dc1226d8
--- /dev/null
+++ b/Zend/tests/anon/006.phpt
@@ -0,0 +1,15 @@
+--TEST--
+testing anon classes inside namespaces
+--FILE--
+<?php
+namespace lone {
+ $hello = new class{} ;
+}
+
+namespace {
+ var_dump ($hello);
+}
+--EXPECTF--
+object(lone\class@%s)#1 (0) {
+}
+
diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt
new file mode 100644
index 0000000000..12f4da6653
--- /dev/null
+++ b/Zend/tests/anon/007.phpt
@@ -0,0 +1,23 @@
+--TEST--
+testing anon classes in functions outside of classes in namespaces
+--FILE--
+<?php
+namespace lone {
+ function my_factory() {
+ return new class{};
+ }
+
+ class Outer {
+
+ public function __construct() {
+ var_dump(
+ my_factory());
+ }
+ }
+
+ new Outer();
+}
+--EXPECTF--
+object(lone\class@%s)#2 (0) {
+}
+
diff --git a/Zend/tests/anon/008.phpt b/Zend/tests/anon/008.phpt
new file mode 100644
index 0000000000..315163ca5f
--- /dev/null
+++ b/Zend/tests/anon/008.phpt
@@ -0,0 +1,23 @@
+--TEST--
+testing static access for methods and properties in anon classes
+--FILE--
+<?php
+$anonClass = new class("cats", "dogs") {
+ public static $foo;
+ private static $bar;
+
+ public function __construct($foo, $bar) {
+ static::$foo = $foo;
+ static::$bar = $bar;
+ }
+
+ public static function getBar() {
+ return static::$bar;
+ }
+};
+
+var_dump($anonClass::$foo);
+var_dump($anonClass::getBar());
+--EXPECT--
+string(4) "cats"
+string(4) "dogs"
diff --git a/Zend/tests/anon/009.phpt b/Zend/tests/anon/009.phpt
new file mode 100644
index 0000000000..4a67cdd30d
--- /dev/null
+++ b/Zend/tests/anon/009.phpt
@@ -0,0 +1,18 @@
+--TEST--
+testing traits in anon classes
+--FILE--
+<?php
+
+trait Foo {
+ public function someMethod() {
+ return "bar";
+ }
+}
+
+$anonClass = new class {
+ use Foo;
+};
+
+var_dump($anonClass->someMethod());
+--EXPECT--
+string(3) "bar"
diff --git a/Zend/tests/anon/010.phpt b/Zend/tests/anon/010.phpt
new file mode 100644
index 0000000000..917989a7e2
--- /dev/null
+++ b/Zend/tests/anon/010.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Trait binding after anon class
+--FILE--
+<?php
+
+trait T {
+ public function m1() { return 42; }
+}
+
+class C {
+ public function m2() {
+ return new class {};
+ }
+
+ use T;
+}
+
+$c = new C;
+var_dump($c->m1());
+
+?>
+--EXPECT--
+int(42)
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 4eaad31a74..34a2e132f9 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -457,7 +457,9 @@ static void zend_ast_destroy_ex(zend_ast *ast, zend_bool free) {
case ZEND_AST_CLASS:
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
- zend_string_release(decl->name);
+ if (decl->name) {
+ zend_string_release(decl->name);
+ }
if (decl->doc_comment) {
zend_string_release(decl->doc_comment);
}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 65677f7d47..ddd1346d81 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -27,6 +27,7 @@
#include "zend_llist.h"
#include "zend_API.h"
#include "zend_exceptions.h"
+#include "zend_interfaces.h"
#include "zend_virtual_cwd.h"
#include "zend_multibyte.h"
#include "zend_language_scanner.h"
@@ -3228,6 +3229,8 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
/* }}} */
+zend_class_entry *zend_compile_class_decl(zend_ast *ast);
+
void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *class_ast = ast->child[0];
@@ -3240,6 +3243,17 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
if (zend_is_const_default_class_ref(class_ast)) {
class_node.op_type = IS_CONST;
ZVAL_STR(&class_node.u.constant, zend_resolve_class_name_ast(class_ast));
+ } else if (class_ast->kind == ZEND_AST_CLASS) {
+ uint32_t dcl_opnum = get_next_op_number(CG(active_op_array));
+ zend_class_entry *ce = zend_compile_class_decl(class_ast);
+ /* jump over anon class declaration */
+ opline = &CG(active_op_array)->opcodes[dcl_opnum];
+ if (opline->opcode == ZEND_FETCH_CLASS) {
+ opline++;
+ }
+ class_node.op_type = opline->result_type;
+ class_node.u.op.var = opline->result.var;
+ opline->op1.opline_num = get_next_op_number(CG(active_op_array));
} else {
zend_compile_class_ref(&class_node, class_ast, 1);
}
@@ -4884,7 +4898,28 @@ void zend_compile_implements(znode *class_node, zend_ast *ast) /* {{{ */
}
/* }}} */
-void zend_compile_class_decl(zend_ast *ast) /* {{{ */
+static zend_string *zend_generate_anon_class_name(unsigned char *lex_pos) /* {{{ */
+{
+ zend_string *result;
+ char char_pos_buf[32];
+ size_t filename_len, char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos);
+
+ const char *filename;
+ if (CG(active_op_array)->filename) {
+ filename = CG(active_op_array)->filename->val;
+ filename_len = CG(active_op_array)->filename->len;
+ } else {
+ filename = "-";
+ filename_len = sizeof("-") - 1;
+ }
+ /* NULL, name length, filename length, last accepting char position length */
+ result = zend_string_alloc(sizeof("class@anonymous") + filename_len + char_pos_len, 0);
+ sprintf(result->val, "class@anonymous%c%s%s", '\0', filename, char_pos_buf);
+ return zend_new_interned_string(result);
+}
+/* }}} */
+
+zend_class_entry *zend_compile_class_decl(zend_ast *ast) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *extends_ast = decl->child[0];
@@ -4896,9 +4931,20 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
zend_op *opline;
znode declare_node, extends_node;
- if (CG(active_class_entry)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested");
- return;
+ zend_class_entry *original_ce = CG(active_class_entry);
+ znode original_implementing_class = FC(implementing_class);
+
+ if (decl->flags & ZEND_ACC_ANON_CLASS) {
+ decl->name = name = zend_generate_anon_class_name(decl->lex_pos);
+
+ /* Serialization is not supported for anonymous classes */
+ ce->serialize = zend_class_serialize_deny;
+ ce->unserialize = zend_class_unserialize_deny;
+ }
+
+ if (CG(active_class_entry) && !(decl->flags & ZEND_ACC_ANON_CLASS)) {
+ zend_error(E_COMPILE_ERROR, "Class declarations may not be nested");
+ return NULL;
}
zend_assert_valid_class_name(name);
@@ -4957,15 +5003,28 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
opline->op2_type = IS_CONST;
LITERAL_STR(opline->op2, lcname);
- if (extends_ast) {
- opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
- opline->extended_value = extends_node.u.op.var;
+ if (decl->flags & ZEND_ACC_ANON_CLASS) {
+ if (extends_ast) {
+ opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
+ opline->extended_value = extends_node.u.op.var;
+ } else {
+ opline->opcode = ZEND_DECLARE_ANON_CLASS;
+ }
+
+ opline->op1_type = IS_UNUSED;
+
+ zend_hash_update_ptr(CG(class_table), lcname, ce);
} else {
- opline->opcode = ZEND_DECLARE_CLASS;
- }
+ zend_string *key;
- {
- zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
+ if (extends_ast) {
+ opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
+ opline->extended_value = extends_node.u.op.var;
+ } else {
+ opline->opcode = ZEND_DECLARE_CLASS;
+ }
+
+ key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
opline->op1_type = IS_CONST;
LITERAL_STR(opline->op1, key);
@@ -5051,7 +5110,10 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
}
- CG(active_class_entry) = NULL;
+ FC(implementing_class) = original_implementing_class;
+ CG(active_class_entry) = original_ce;
+
+ return ce;
}
/* }}} */
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index efc2f820e1..575dbe0803 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -206,6 +206,8 @@ typedef struct _zend_try_catch_element {
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20
#define ZEND_ACC_INTERFACE 0x40
#define ZEND_ACC_TRAIT 0x80
+#define ZEND_ACC_ANON_CLASS 0x100
+#define ZEND_ACC_ANON_BOUND 0x200
/* method flags (visibility) */
/* The order of those must be kept - public < protected < private */
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 0aba081218..2541c9f571 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -241,7 +241,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> extends_from parameter optional_type argument expr_without_variable global_var
%type <ast> static_var class_statement trait_adaptation trait_precedence trait_alias
%type <ast> absolute_trait_method_reference trait_method_reference property echo_expr
-%type <ast> new_expr class_name class_name_reference simple_variable internal_functions_in_yacc
+%type <ast> new_expr anonymous_class class_name class_name_reference simple_variable
+%type <ast> internal_functions_in_yacc
%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name
%type <ast> variable_class_name dereferencable_scalar class_name_scalar constant dereferencable
%type <ast> callable_expr callable_variable static_member new_variable
@@ -798,9 +799,21 @@ non_empty_for_exprs:
| expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
;
+anonymous_class:
+ T_CLASS { $<num>$ = CG(zend_lineno); } ctor_arguments
+ extends_from implements_list backup_doc_comment '{' class_statement_list '}' {
+ zend_ast *decl = zend_ast_create_decl(
+ ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $<num>2, $6, NULL,
+ $4, $5, $8, NULL);
+ $$ = zend_ast_create(ZEND_AST_NEW, decl, $3);
+ }
+;
+
new_expr:
T_NEW class_name_reference ctor_arguments
{ $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); }
+ | T_NEW anonymous_class
+ { $$ = $2; }
;
expr_without_variable:
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 8bdaded38f..98734dc533 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -761,6 +761,9 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->result.var);
}
switch (opline->opcode) {
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
+ /* break omitted intentionally */
case ZEND_DECLARE_INHERITED_CLASS:
case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
opline->extended_value = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->extended_value);
@@ -772,6 +775,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
/* break omitted intentionally */
case ZEND_JMP:
case ZEND_FAST_CALL:
+ case ZEND_DECLARE_ANON_CLASS:
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
break;
case ZEND_JMPZNZ:
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index e16cbd5529..6565324c4c 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -6752,6 +6752,48 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_INHERITED_CLASS_DELAYED, ANY, ANY)
ZEND_VM_NEXT_OPCODE();
}
+ZEND_VM_HANDLER(171, ZEND_DECLARE_ANON_CLASS, ANY, ANY)
+{
+ zend_class_entry *ce;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ Z_CE_P(EX_VAR(opline->result.var)) = ce;
+ ZEND_ASSERT(ce != NULL);
+
+ if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
+ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ }
+
+ if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
+ zend_verify_abstract_class(ce);
+ }
+ ce->ce_flags |= ZEND_ACC_ANON_BOUND;
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
+ZEND_VM_HANDLER(172, ZEND_DECLARE_ANON_INHERITED_CLASS, ANY, ANY)
+{
+ zend_class_entry *ce;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ Z_CE_P(EX_VAR(opline->result.var)) = ce;
+ ZEND_ASSERT(ce != NULL);
+
+ if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
+ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ }
+
+ zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->extended_value)));
+ ce->ce_flags |= ZEND_ACC_ANON_BOUND;
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY)
{
USE_OPLINE
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 2802c6015c..18f4e8584a 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1334,6 +1334,48 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_INHERITED_CLASS_DELAYE
ZEND_VM_NEXT_OPCODE();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zend_class_entry *ce;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ Z_CE_P(EX_VAR(opline->result.var)) = ce;
+ ZEND_ASSERT(ce != NULL);
+
+ if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
+ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ }
+
+ if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
+ zend_verify_abstract_class(ce);
+ }
+ ce->ce_flags |= ZEND_ACC_ANON_BOUND;
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
+static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ zend_class_entry *ce;
+ USE_OPLINE
+
+ SAVE_OPLINE();
+ ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(EX_CONSTANT(opline->op2)));
+ Z_CE_P(EX_VAR(opline->result.var)) = ce;
+ ZEND_ASSERT(ce != NULL);
+
+ if (ce->ce_flags & ZEND_ACC_ANON_BOUND) {
+ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
+ }
+
+ zend_do_inheritance(ce, Z_CE_P(EX_VAR(opline->extended_value)));
+ ce->ce_flags |= ZEND_ACC_ANON_BOUND;
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -48760,6 +48802,56 @@ void zend_init_opcodes_handlers(void)
ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SPACESHIP_SPEC_CV_CV_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
+ ZEND_DECLARE_ANON_INHERITED_CLASS_SPEC_HANDLER,
ZEND_NULL_HANDLER
};
zend_opcode_handlers = labels;
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index e8337404bb..95b8b859eb 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -21,7 +21,7 @@
#include <stdio.h>
#include <zend.h>
-const char *zend_vm_opcodes_map[171] = {
+const char *zend_vm_opcodes_map[173] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@@ -193,6 +193,8 @@ const char *zend_vm_opcodes_map[171] = {
"ZEND_BIND_GLOBAL",
"ZEND_COALESCE",
"ZEND_SPACESHIP",
+ "ZEND_DECLARE_ANON_CLASS",
+ "ZEND_DECLARE_ANON_INHERITED_CLASS",
};
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 a5620370bb..89a0d3139b 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -203,5 +203,7 @@ END_EXTERN_C()
#define ZEND_BIND_GLOBAL 168
#define ZEND_COALESCE 169
#define ZEND_SPACESHIP 170
+#define ZEND_DECLARE_ANON_CLASS 171
+#define ZEND_DECLARE_ANON_INHERITED_CLASS 172
#endif