summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c590
1 files changed, 235 insertions, 355 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index f018d10152..e791500fa4 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -247,7 +247,6 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
CG(context).vars_size = 0;
CG(context).literals_size = 0;
- CG(context).backpatch_count = 0;
CG(context).fast_call_var = -1;
CG(context).try_catch_offset = -1;
CG(context).current_brk_cont = -1;
@@ -1031,197 +1030,100 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */
}
/* }}} */
-ZEND_API int do_bind_function(const zend_op_array *op_array, const zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
+static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zend_string *lcname, zend_op_array *op_array, zend_bool compile_time) /* {{{ */
{
- zend_function *function, *new_function;
- zval *lcname, *rtd_key, *zv;
+ zval *zv = zend_hash_find_ex(compile_time ? CG(function_table) : EG(function_table), lcname, 1);
+ int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
+ zend_function *old_function;
- if (compile_time) {
- lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
- rtd_key = lcname + 1;
+ ZEND_ASSERT(zv != NULL);
+ old_function = (zend_function*)Z_PTR_P(zv);
+ if (old_function->type == ZEND_USER_FUNCTION
+ && old_function->op_array.last > 0) {
+ zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
+ op_array ? ZSTR_VAL(op_array->function_name) : ZSTR_VAL(old_function->common.function_name),
+ ZSTR_VAL(old_function->op_array.filename),
+ old_function->op_array.opcodes[0].lineno);
} else {
- lcname = RT_CONSTANT(opline, opline->op1);
- rtd_key = lcname + 1;
- }
-
- zv = zend_hash_find_ex(function_table, Z_STR_P(rtd_key), 1);
- function = (zend_function*)Z_PTR_P(zv);
- new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
- memcpy(new_function, function, sizeof(zend_op_array));
- if (zend_hash_add_ptr(function_table, Z_STR_P(lcname), new_function) == NULL) {
- int error_level = compile_time ? E_COMPILE_ERROR : E_ERROR;
- zend_function *old_function;
-
- zv = zend_hash_find_ex(function_table, Z_STR_P(lcname), 1);
- ZEND_ASSERT(zv != NULL);
- old_function = (zend_function*)Z_PTR_P(zv);
- if (old_function->type == ZEND_USER_FUNCTION
- && old_function->op_array.last > 0) {
- zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)",
- ZSTR_VAL(function->common.function_name),
- ZSTR_VAL(old_function->op_array.filename),
- old_function->op_array.opcodes[0].lineno);
- } else {
- zend_error_noreturn(error_level, "Cannot redeclare %s()", ZSTR_VAL(function->common.function_name));
- }
- return FAILURE;
- } else {
- if (function->op_array.refcount) {
- (*function->op_array.refcount)++;
- }
- if (!(function->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) {
- function->op_array.static_variables = NULL; /* NULL out the unbound function */
- }
- return SUCCESS;
+ zend_error_noreturn(error_level, "Cannot redeclare %s()",
+ op_array ? ZSTR_VAL(op_array->function_name) : ZSTR_VAL(old_function->common.function_name));
}
}
-/* }}} */
-ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time) /* {{{ */
+ZEND_API int do_bind_function(zval *lcname) /* {{{ */
{
- zend_class_entry *ce;
- zval *lcname, *rtd_key, *zv;
+ zend_function *function;
+ zval *rtd_key, *zv;
- if (compile_time) {
- lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
- rtd_key = lcname + 1;
- } else {
- lcname = RT_CONSTANT(opline, opline->op1);
- rtd_key = lcname + 1;
+ rtd_key = lcname + 1;
+ zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1);
+ if (UNEXPECTED(!zv)) {
+ do_bind_function_error(Z_STR_P(lcname), NULL, 0);
+ return FAILURE;
}
- zv = zend_hash_find_ex(class_table, Z_STR_P(rtd_key), 1);
- ZEND_ASSERT(zv);
- ce = (zend_class_entry*)Z_PTR_P(zv);
- ce->refcount++;
- if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
- ce->refcount--;
- if (!compile_time) {
- /* If we're in compile time, in practice, it's quite possible
- * that we'll never reach this class declaration at runtime,
- * so we shut up about it. This allows the if (!defined('FOO')) { return; }
- * approach to work.
- */
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
- }
- return NULL;
- } else {
- if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
- zend_verify_abstract_class(ce);
- }
- return ce;
+ function = (zend_function*)Z_PTR_P(zv);
+ zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname));
+ if (UNEXPECTED(!zv)) {
+ do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0);
+ return FAILURE;
}
+ return SUCCESS;
}
/* }}} */
-ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time) /* {{{ */
+ZEND_API int do_bind_class(zval *lcname) /* {{{ */
{
zend_class_entry *ce;
- zval *lcname, *rtd_key, *zv;
-
- if (compile_time) {
- lcname = CT_CONSTANT_EX(op_array, opline->op1.constant);
- rtd_key = lcname + 1;
- } else {
- lcname = RT_CONSTANT(opline, opline->op1);
- rtd_key = lcname + 1;
- }
-
- zv = zend_hash_find_ex(class_table, Z_STR_P(rtd_key), 1);
+ zval *rtd_key, *zv;
- if (!zv) {
- if (!compile_time) {
- /* If we're in compile time, in practice, it's quite possible
- * that we'll never reach this class declaration at runtime,
- * so we shut up about it. This allows the if (!defined('FOO')) { return; }
- * approach to work.
- */
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", zend_get_object_type(Z_OBJCE_P(lcname)));
- }
- return NULL;
+ rtd_key = lcname + 1;
+ zv = zend_hash_find_ex(EG(class_table), Z_STR_P(rtd_key), 1);
+ if (UNEXPECTED(!zv)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", Z_STRVAL_P(lcname));
+ return FAILURE;
}
ce = (zend_class_entry*)Z_PTR_P(zv);
-
- if (zend_hash_exists(class_table, Z_STR_P(lcname))) {
+ zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname));
+ if (UNEXPECTED(!zv)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
+ return FAILURE;
}
- zend_do_inheritance(ce, parent_ce);
-
- ce->refcount++;
-
- /* Register the derived class */
- if (zend_hash_add_ptr(class_table, Z_STR_P(lcname), ce) == NULL) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
- }
- return ce;
+ zend_do_link_class(ce, NULL);
+ return SUCCESS;
}
/* }}} */
-void zend_do_early_binding(void) /* {{{ */
+ZEND_API int do_bind_inherited_class(zval *lcname, zend_class_entry *parent_ce) /* {{{ */
{
- zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
- HashTable *table;
+ zend_class_entry *ce;
+ zval *rtd_key, *zv;
- while (opline->opcode == ZEND_TICKS && opline > CG(active_op_array)->opcodes) {
- opline--;
- }
+ rtd_key = lcname + 1;
- switch (opline->opcode) {
- case ZEND_DECLARE_FUNCTION:
- if (do_bind_function(CG(active_op_array), opline, CG(function_table), 1) == FAILURE) {
- return;
- }
- table = CG(function_table);
- break;
- case ZEND_DECLARE_CLASS:
- if (do_bind_class(CG(active_op_array), opline, CG(class_table), 1) == NULL) {
- return;
- }
- table = CG(class_table);
- break;
- case ZEND_DECLARE_INHERITED_CLASS:
- {
- zval *parent_name;
- zend_class_entry *ce;
-
- parent_name = CT_CONSTANT(opline->op2);
- if (((ce = zend_lookup_class_ex(Z_STR_P(parent_name), parent_name + 1, 0)) == NULL) ||
- ((CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES) &&
- (ce->type == ZEND_INTERNAL_CLASS))) {
- if (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) {
- CG(active_op_array)->fn_flags |= ZEND_ACC_EARLY_BINDING;
- opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;
- opline->result_type = IS_UNUSED;
- opline->result.opline_num = -1;
- }
- return;
- }
- if (do_bind_inherited_class(CG(active_op_array), opline, CG(class_table), ce, 1) == NULL) {
- return;
- }
- zend_del_literal(CG(active_op_array), opline->op2.constant+1);
- zend_del_literal(CG(active_op_array), opline->op2.constant);
+ zv = zend_hash_find_ex(EG(class_table), Z_STR_P(rtd_key), 1);
- table = CG(class_table);
- break;
- }
- case ZEND_VERIFY_ABSTRACT_CLASS:
- case ZEND_ADD_INTERFACE:
- case ZEND_ADD_TRAIT:
- case ZEND_BIND_TRAITS:
- /* We currently don't early-bind classes that implement interfaces */
- /* Classes with traits are handled exactly the same, no early-bind here */
- return;
- default:
- zend_error_noreturn(E_COMPILE_ERROR, "Invalid binding type");
- return;
+ if (UNEXPECTED(!zv)) {
+ /* If we're in compile time, in practice, it's quite possible
+ * that we'll never reach this class declaration at runtime,
+ * so we shut up about it. This allows the if (!defined('FOO')) { return; }
+ * approach to work.
+ */
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", Z_STRVAL_P(lcname));
+ return FAILURE;
+ }
+
+ /* Register the derived class */
+ ce = (zend_class_entry*)Z_PTR_P(zv);
+ zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname));
+ if (UNEXPECTED(!zv)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
+ return FAILURE;
}
- zend_hash_del(table, Z_STR_P(CT_CONSTANT(opline->op1)+1));
- zend_del_literal(CG(active_op_array), opline->op1.constant+1);
- zend_del_literal(CG(active_op_array), opline->op1.constant);
- MAKE_NOP(opline);
+ zend_do_link_class(ce, parent_ce);
+ return SUCCESS;
}
/* }}} */
@@ -1287,8 +1189,8 @@ ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array, uint3
while (opline_num != (uint32_t)-1) {
const zend_op *opline = &op_array->opcodes[opline_num];
zval *parent_name = RT_CONSTANT(opline, opline->op2);
- if ((ce = zend_lookup_class_ex(Z_STR_P(parent_name), parent_name + 1, 0)) != NULL) {
- do_bind_inherited_class(op_array, &op_array->opcodes[opline_num], EG(class_table), ce, 0);
+ if ((ce = zend_lookup_class_ex(Z_STR_P(parent_name), Z_STR_P(parent_name + 1), 0)) != NULL) {
+ do_bind_inherited_class(RT_CONSTANT(&op_array->opcodes[opline_num], op_array->opcodes[opline_num].op1), ce);
}
opline_num = op_array->opcodes[opline_num].result.opline_num;
}
@@ -1712,19 +1614,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0);
if (ce->type == ZEND_INTERNAL_CLASS) {
-#ifdef ZTS
- int n = zend_hash_num_elements(CG(class_table));
-
- if (CG(static_members_table) && n >= CG(last_static_member)) {
- /* Support for run-time declaration: dl() */
- CG(last_static_member) = n+1;
- CG(static_members_table) = realloc(CG(static_members_table), (n+1)*sizeof(zval*));
- CG(static_members_table)[n] = NULL;
- }
- ce->static_members_table = (zval*)(zend_intptr_t)n;
-#else
ce->static_members_table = NULL;
-#endif
} else {
ce->static_members_table = ce->default_static_members_table;
ce->info.user.doc_comment = NULL;
@@ -1752,7 +1642,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
ce->num_interfaces = 0;
ce->interfaces = NULL;
ce->num_traits = 0;
- ce->traits = NULL;
+ ce->trait_names = NULL;
ce->trait_aliases = NULL;
ce->trait_precedences = NULL;
ce->serialize = NULL;
@@ -1954,12 +1844,9 @@ static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var)
def->opcode == ZEND_JMPNZ_EX ||
def->opcode == ZEND_BOOL ||
def->opcode == ZEND_BOOL_NOT) {
- /* result IS_BOOL, it does't have to be destroyed */
+ /* result IS_BOOL, it doesn't have to be destroyed */
break;
- } else if (def->opcode == ZEND_DECLARE_CLASS ||
- def->opcode == ZEND_DECLARE_INHERITED_CLASS ||
- def->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
- def->opcode == ZEND_DECLARE_ANON_CLASS ||
+ } else if (def->opcode == ZEND_DECLARE_ANON_CLASS ||
def->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS) {
/* classes don't have to be destroyed */
break;
@@ -2048,11 +1935,7 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */
}
} else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
opline->opcode == ZEND_NEW ||
- opline->opcode == ZEND_FETCH_CLASS_CONSTANT ||
- opline->opcode == ZEND_ADD_INTERFACE ||
- opline->opcode == ZEND_ADD_TRAIT ||
- opline->opcode == ZEND_BIND_TRAITS ||
- opline->opcode == ZEND_VERIFY_ABSTRACT_CLASS) {
+ opline->opcode == ZEND_FETCH_CLASS_CONSTANT) {
/* classes don't have to be destroyed */
} else if (opline->opcode == ZEND_FAST_RET) {
/* fast_calls don't have to be destroyed */
@@ -3380,7 +3263,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
}
/* }}} */
-static zend_bool zend_args_contain_unpack(zend_ast_list *args) /* {{{ */
+static inline zend_bool zend_args_contain_unpack(zend_ast_list *args) /* {{{ */
{
uint32_t i;
for (i = 0; i < args->children; ++i) {
@@ -3397,7 +3280,7 @@ int zend_compile_func_strlen(znode *result, zend_ast_list *args) /* {{{ */
znode arg_node;
if ((CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)
- || args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK
+ || args->children != 1
) {
return FAILURE;
}
@@ -3419,7 +3302,7 @@ int zend_compile_func_typecheck(znode *result, zend_ast_list *args, uint32_t typ
znode arg_node;
zend_op *opline;
- if (args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK) {
+ if (args->children != 1) {
return FAILURE;
}
@@ -3439,7 +3322,7 @@ int zend_compile_func_cast(znode *result, zend_ast_list *args, uint32_t type) /*
znode arg_node;
zend_op *opline;
- if (args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK) {
+ if (args->children != 1) {
return FAILURE;
}
@@ -3538,8 +3421,11 @@ static int zend_try_compile_ct_bound_init_user_func(zend_ast *name_ast, uint32_t
fbc = zend_hash_find_ptr(CG(function_table), lcname);
if (!fbc
+ /* Don't use INIT_FCALL for recursive calls */
+ || (fbc == (zend_function*)CG(active_op_array))
|| (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS))
|| (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS))
+ || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) && fbc->op_array.filename != CG(active_op_array)->filename)
) {
zend_string_release_ex(lcname, 0);
return FAILURE;
@@ -3579,7 +3465,7 @@ int zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcna
{
znode arg_node;
- if (args->children != 2 || zend_args_contain_unpack(args)) {
+ if (args->children != 2) {
return FAILURE;
}
@@ -3628,7 +3514,7 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam
{
uint32_t i;
- if (args->children < 1 || zend_args_contain_unpack(args)) {
+ if (args->children < 1) {
return FAILURE;
}
@@ -3659,7 +3545,8 @@ static int zend_compile_assert(znode *result, zend_ast_list *args, zend_string *
zend_emit_op(NULL, ZEND_ASSERT_CHECK, NULL, NULL);
- if (fbc) {
+ /* Don't use INIT_FCALL for recursive calls */
+ if (fbc && fbc != (zend_function*)CG(active_op_array)) {
name_node.op_type = IS_CONST;
ZVAL_STR_COPY(&name_node.u.constant, name);
@@ -3787,7 +3674,7 @@ int zend_compile_func_count(znode *result, zend_ast_list *args) /* {{{ */
{
znode arg_node;
- if (args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK) {
+ if (args->children != 1) {
return FAILURE;
}
@@ -3804,7 +3691,7 @@ int zend_compile_func_get_class(znode *result, zend_ast_list *args) /* {{{ */
} else {
znode arg_node;
- if (args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK) {
+ if (args->children != 1) {
return FAILURE;
}
@@ -3830,7 +3717,7 @@ int zend_compile_func_gettype(znode *result, zend_ast_list *args) /* {{{ */
{
znode arg_node;
- if (args->children != 1 || args->child[0]->kind == ZEND_AST_UNPACK) {
+ if (args->children != 1) {
return FAILURE;
}
@@ -3909,6 +3796,10 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l
return FAILURE;
}
+ if (zend_args_contain_unpack(args)) {
+ return FAILURE;
+ }
+
if (zend_string_equals_literal(lcname, "strlen")) {
return zend_compile_func_strlen(result, args);
} else if (zend_string_equals_literal(lcname, "is_null")) {
@@ -4010,8 +3901,11 @@ void zend_compile_call(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
fbc = zend_hash_find_ptr(CG(function_table), lcname);
if (!fbc
+ /* Don't use INIT_FCALL for recursive calls */
+ || (fbc == (zend_function*)CG(active_op_array))
|| (fbc->type == ZEND_INTERNAL_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS))
|| (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS))
+ || (fbc->type == ZEND_USER_FUNCTION && (CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) && fbc->op_array.filename != CG(active_op_array)->filename)
) {
zend_string_release_ex(lcname, 0);
zend_compile_dynamic_call(result, &name_node, args_ast);
@@ -4159,7 +4053,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
/* }}} */
-void zend_compile_class_decl(zend_ast *ast);
+void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel);
void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
{
@@ -4171,7 +4065,7 @@ void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */
if (class_ast->kind == ZEND_AST_CLASS) {
uint32_t dcl_opnum = get_next_op_number(CG(active_op_array));
- zend_compile_class_decl(class_ast);
+ zend_compile_class_decl(class_ast, 0);
/* jump over anon class declaration */
opline = &CG(active_op_array)->opcodes[dcl_opnum];
if (opline->opcode == ZEND_FETCH_CLASS) {
@@ -4249,17 +4143,8 @@ void zend_compile_global_var(zend_ast *ast) /* {{{ */
static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, uint32_t by_ref) /* {{{ */
{
- znode var_node;
zend_op *opline;
- zend_string *var_name;
-
- if (var_ast->kind == ZEND_AST_ZVAL) {
- var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
- zend_compile_expr(&var_node, var_ast);
- } else {
- zend_compile_expr(&var_node, var_ast);
- var_name = zval_make_interned_string(&var_node.u.constant);
- }
+ zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
if (!CG(active_op_array)->static_variables) {
if (CG(active_op_array)->scope) {
@@ -4268,19 +4153,13 @@ static void zend_compile_static_var_common(zend_ast *var_ast, zval *value, uint3
CG(active_op_array)->static_variables = zend_new_array(8);
}
- if (GC_REFCOUNT(CG(active_op_array)->static_variables) > 1) {
- if (!(GC_FLAGS(CG(active_op_array)->static_variables) & IS_ARRAY_IMMUTABLE)) {
- GC_DELREF(CG(active_op_array)->static_variables);
- }
- CG(active_op_array)->static_variables = zend_array_dup(CG(active_op_array)->static_variables);
- }
value = zend_hash_update(CG(active_op_array)->static_variables, var_name, value);
if (zend_string_equals_literal(var_name, "this")) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as static variable");
}
- opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, &var_node);
+ opline = zend_emit_op(NULL, ZEND_BIND_STATIC, NULL, NULL);
opline->op1_type = IS_CV;
opline->op1.var = lookup_cv(CG(active_op_array), var_name);
opline->extended_value = (uint32_t)((char*)value - (char*)CG(active_op_array)->static_variables->arData) | by_ref;
@@ -5435,10 +5314,16 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
}
/* }}} */
-static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info, zend_bool allow_null) /* {{{ */
+static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null) /* {{{ */
{
+ zend_bool allow_null = force_allow_null;
+ if (ast->attr & ZEND_TYPE_NULLABLE) {
+ allow_null = 1;
+ ast->attr &= ~ZEND_TYPE_NULLABLE;
+ }
+
if (ast->kind == ZEND_AST_TYPE) {
- arg_info->type = ZEND_TYPE_ENCODE(ast->attr, allow_null);
+ return ZEND_TYPE_ENCODE(ast->attr, allow_null);
} else {
zend_string *class_name = zend_ast_get_str(ast);
zend_uchar type = zend_lookup_builtin_type_by_name(class_name);
@@ -5449,7 +5334,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info, zend_b
"Type declaration '%s' must be unqualified",
ZSTR_VAL(zend_string_tolower(class_name)));
}
- arg_info->type = ZEND_TYPE_ENCODE(type, allow_null);
+ return ZEND_TYPE_ENCODE(type, allow_null);
} else {
uint32_t fetch_type = zend_get_class_fetch_type_ast(ast);
if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) {
@@ -5460,7 +5345,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info, zend_b
zend_string_addref(class_name);
}
- arg_info->type = ZEND_TYPE_ENCODE_CLASS(class_name, allow_null);
+ return ZEND_TYPE_ENCODE_CLASS(class_name, allow_null);
}
}
}
@@ -5474,21 +5359,12 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
zend_arg_info *arg_infos;
if (return_type_ast) {
- zend_bool allow_null = 0;
-
/* Use op_array->arg_info[-1] for return type */
arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0);
arg_infos->name = NULL;
arg_infos->pass_by_reference = (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
arg_infos->is_variadic = 0;
- arg_infos->type = 0;
-
- if (return_type_ast->attr & ZEND_TYPE_NULLABLE) {
- allow_null = 1;
- return_type_ast->attr &= ~ZEND_TYPE_NULLABLE;
- }
-
- zend_compile_typename(return_type_ast, arg_infos, allow_null);
+ arg_infos->type = zend_compile_typename(return_type_ast, 0);
if (ZEND_TYPE_CODE(arg_infos->type) == IS_VOID && ZEND_TYPE_ALLOW_NULL(arg_infos->type)) {
zend_error_noreturn(E_COMPILE_ERROR, "Void type cannot be nullable");
@@ -5571,19 +5447,14 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
arg_info->type = ZEND_TYPE_ENCODE(0, 1);
if (type_ast) {
- zend_bool allow_null;
zend_bool has_null_default = default_ast
&& (Z_TYPE(default_node.u.constant) == IS_NULL
|| (Z_TYPE(default_node.u.constant) == IS_CONSTANT_AST
&& Z_ASTVAL(default_node.u.constant)->kind == ZEND_AST_CONSTANT
&& strcasecmp(ZSTR_VAL(zend_ast_get_constant_name(Z_ASTVAL(default_node.u.constant))), "NULL") == 0));
- zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE;
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
- allow_null = has_null_default || is_explicitly_nullable;
-
- type_ast->attr &= ~ZEND_TYPE_NULLABLE;
- zend_compile_typename(type_ast, arg_info, allow_null);
+ arg_info->type = zend_compile_typename(type_ast, has_null_default);
if (ZEND_TYPE_CODE(arg_info->type) == IS_VOID) {
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
@@ -5685,13 +5556,6 @@ static void zend_compile_closure_binding(znode *closure, zend_op_array *op_array
op_array->static_variables = zend_new_array(8);
}
- if (GC_REFCOUNT(op_array->static_variables) > 1) {
- if (!(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
- GC_DELREF(op_array->static_variables);
- }
- op_array->static_variables = zend_array_dup(op_array->static_variables);
- }
-
for (i = 0; i < list->children; ++i) {
zend_ast *var_name_ast = list->child[i];
zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_name_ast));
@@ -5925,7 +5789,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_bool toplevel) /* {{{ */
{
zend_ast *params_ast = decl->child[0];
zend_string *unqualified_name, *name, *lcname, *key;
@@ -5959,9 +5823,17 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
"as the function has special semantics");
}
+ zend_register_seen_symbol(lcname, ZEND_SYMBOL_FUNCTION);
+ if (toplevel) {
+ if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) {
+ do_bind_function_error(lcname, op_array, 1);
+ }
+ zend_string_release_ex(lcname, 0);
+ return;
+ }
+
key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
zend_hash_update_ptr(CG(function_table), key, op_array);
- zend_register_seen_symbol(lcname, ZEND_SYMBOL_FUNCTION);
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
@@ -5975,12 +5847,11 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
/* RTD key is placed after lcname literal in op1 */
zend_add_literal_string(CG(active_op_array), &key);
}
-
zend_string_release_ex(lcname, 0);
}
/* }}} */
-void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
+void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *params_ast = decl->child[0];
@@ -6010,7 +5881,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, toplevel);
if (uses_ast) {
zend_compile_closure_binding(result, op_array, uses_ast);
}
@@ -6221,9 +6092,11 @@ void zend_compile_use_trait(zend_ast *ast) /* {{{ */
zend_ast_list *traits = zend_ast_get_list(ast->child[0]);
zend_ast_list *adaptations = ast->child[1] ? zend_ast_get_list(ast->child[1]) : NULL;
zend_class_entry *ce = CG(active_class_entry);
- zend_op *opline;
uint32_t i;
+ ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS;
+ ce->trait_names = erealloc(ce->trait_names, sizeof(zend_class_name) * (ce->num_traits + traits->children));
+
for (i = 0; i < traits->children; ++i) {
zend_ast *trait_ast = traits->child[i];
zend_string *name = zend_ast_get_str(trait_ast);
@@ -6242,13 +6115,8 @@ void zend_compile_use_trait(zend_ast *ast) /* {{{ */
break;
}
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_ADD_TRAIT;
- SET_NODE(opline->op1, &FC(implementing_class));
- opline->op2_type = IS_CONST;
- opline->op2.constant = zend_add_class_name_literal(CG(active_op_array),
- zend_resolve_class_name_ast(trait_ast));
-
+ ce->trait_names[ce->num_traits].name = zend_resolve_class_name_ast(trait_ast);
+ ce->trait_names[ce->num_traits].lc_name = zend_string_tolower(ce->trait_names[ce->num_traits].name);
ce->num_traits++;
}
@@ -6271,28 +6139,32 @@ void zend_compile_use_trait(zend_ast *ast) /* {{{ */
}
/* }}} */
-void zend_compile_implements(znode *class_node, zend_ast *ast) /* {{{ */
+void zend_compile_implements(zend_ast *ast) /* {{{ */
{
zend_ast_list *list = zend_ast_get_list(ast);
+ zend_class_entry *ce = CG(active_class_entry);
+ zend_class_name *interface_names;
uint32_t i;
+
+ interface_names = emalloc(sizeof(zend_class_name) * list->children);
+
for (i = 0; i < list->children; ++i) {
zend_ast *class_ast = list->child[i];
zend_string *name = zend_ast_get_str(class_ast);
- zend_op *opline;
-
if (!zend_is_const_default_class_ref(class_ast)) {
+ efree(interface_names);
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use '%s' as interface name as it is reserved", ZSTR_VAL(name));
}
- opline = zend_emit_op(NULL, ZEND_ADD_INTERFACE, class_node, NULL);
- opline->op2_type = IS_CONST;
- opline->op2.constant = zend_add_class_name_literal(CG(active_op_array),
- zend_resolve_class_name_ast(class_ast));
-
- CG(active_class_entry)->num_interfaces++;
+ interface_names[i].name = zend_resolve_class_name_ast(class_ast);
+ interface_names[i].lc_name = zend_string_tolower(interface_names[i].name);
}
+
+ ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
+ ce->num_interfaces = list->children;
+ ce->interface_names = interface_names;
}
/* }}} */
@@ -6310,7 +6182,7 @@ static zend_string *zend_generate_anon_class_name(unsigned char *lex_pos) /* {{{
}
/* }}} */
-void zend_compile_class_decl(zend_ast *ast) /* {{{ */
+void zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
zend_ast *extends_ast = decl->child[0];
@@ -6319,11 +6191,8 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
zend_string *name, *lcname;
zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
zend_op *opline;
- znode declare_node;
- int extends_const;
zend_class_entry *original_ce = CG(active_class_entry);
- znode original_implementing_class = FC(implementing_class);
if (EXPECTED((decl->flags & ZEND_ACC_ANON_CLASS) == 0)) {
zend_string *unqualified_name = decl->name;
@@ -6387,57 +6256,12 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name");
}
extends_name = Z_STR(extends_node.u.constant);
- extends_const = zend_add_class_name_literal(CG(active_op_array),
- zend_resolve_class_name(extends_name,
- extends_ast->kind == ZEND_AST_ZVAL ? extends_ast->attr : ZEND_NAME_FQ));
+ ce->parent_name = zend_resolve_class_name(extends_name,
+ extends_ast->kind == ZEND_AST_ZVAL ? extends_ast->attr : ZEND_NAME_FQ);
zend_string_release_ex(extends_name, 0);
ce->ce_flags |= ZEND_ACC_INHERITED;
}
- opline = get_next_op(CG(active_op_array));
- zend_make_var_result(&declare_node, opline);
-
- GET_NODE(&FC(implementing_class), opline->result);
-
- opline->op1_type = IS_CONST;
- LITERAL_STR(opline->op1, lcname);
-
- if (decl->flags & ZEND_ACC_ANON_CLASS) {
- if (extends_ast) {
- opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
- opline->op2_type = IS_CONST;
- opline->op2.constant = extends_const;
- } else {
- opline->opcode = ZEND_DECLARE_ANON_CLASS;
- }
-
- if (!zend_hash_exists(CG(class_table), lcname)) {
- zend_hash_add_ptr(CG(class_table), lcname, ce);
- } else {
- /* this anonymous class has been included */
- zval zv;
- ZVAL_PTR(&zv, ce);
- destroy_zend_class(&zv);
- return;
- }
- } else {
- zend_string *key;
-
- if (extends_ast) {
- opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
- opline->op2_type = IS_CONST;
- opline->op2.constant = extends_const;
- } else {
- opline->opcode = ZEND_DECLARE_CLASS;
- }
-
- key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
- /* RTD key is placed after lcname literal in op1 */
- zend_add_literal_string(CG(active_op_array), &key);
-
- zend_hash_update_ptr(CG(class_table), key, ce);
- }
-
CG(active_class_entry) = ce;
zend_compile_stmt(stmt_ast);
@@ -6445,13 +6269,12 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
/* Reset lineno for final opcodes and errors */
CG(zend_lineno) = ast->lineno;
- if (ce->num_traits == 0) {
+ if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) {
/* For traits this check is delayed until after trait binding */
zend_check_deprecated_constructor(ce);
}
if (ce->constructor) {
- ce->constructor->common.fn_flags |= ZEND_ACC_CTOR;
if (ce->constructor->common.fn_flags & ZEND_ACC_STATIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Constructor %s::%s() cannot be static",
ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
@@ -6463,7 +6286,6 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
}
}
if (ce->destructor) {
- ce->destructor->common.fn_flags |= ZEND_ACC_DTOR;
if (ce->destructor->common.fn_flags & ZEND_ACC_STATIC) {
zend_error_noreturn(E_COMPILE_ERROR, "Destructor %s::%s() cannot be static",
ZSTR_VAL(ce->name), ZSTR_VAL(ce->destructor->common.function_name));
@@ -6484,42 +6306,95 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */
}
}
- /* Check for traits and proceed like with interfaces.
- * The only difference will be a combined handling of them in the end.
- * Thus, we need another opcode here. */
- if (ce->num_traits > 0) {
- ce->traits = NULL;
- ce->num_traits = 0;
- ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS;
-
- zend_emit_op(NULL, ZEND_BIND_TRAITS, &declare_node, NULL);
- }
-
if (implements_ast) {
- zend_compile_implements(&declare_node, implements_ast);
+ zend_compile_implements(implements_ast);
}
- if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
- && (extends_ast || implements_ast)
- ) {
+ if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
zend_verify_abstract_class(ce);
- if (implements_ast) {
- zend_emit_op(NULL, ZEND_VERIFY_ABSTRACT_CLASS, &declare_node, NULL);
- }
}
- /* Inherit interfaces; reset number to zero, we need it for above check and
- * will restore it during actual implementation.
- * The ZEND_ACC_IMPLEMENT_INTERFACES flag disables double call to
- * zend_verify_abstract_class() */
- if (ce->num_interfaces > 0) {
- ce->interfaces = NULL;
- ce->num_interfaces = 0;
- ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES;
+ CG(active_class_entry) = original_ce;
+
+ if (toplevel
+ /* We currently don't early-bind classes that implement interfaces or use traits */
+ && !(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
+ if (extends_ast) {
+ zend_class_entry *parent_ce = zend_lookup_class_ex(ce->parent_name, NULL, 0);
+
+ if (parent_ce
+ && ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
+ && ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))
+ ) {
+ if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
+ CG(zend_lineno) = decl->end_lineno;
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ zend_do_inheritance(ce, parent_ce);
+ if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
+ zend_verify_abstract_class(ce);
+ }
+ CG(zend_lineno) = ast->lineno;
+ zend_string_release(lcname);
+ return;
+ }
+ }
+ } else {
+ if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
+ zend_string_release(lcname);
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ return;
+ }
+ }
}
- FC(implementing_class) = original_implementing_class;
- CG(active_class_entry) = original_ce;
+ opline = get_next_op(CG(active_op_array));
+
+ opline->op1_type = IS_CONST;
+ LITERAL_STR(opline->op1, lcname);
+
+ if (decl->flags & ZEND_ACC_ANON_CLASS) {
+ opline->result_type = IS_VAR;
+ opline->result.var = get_temporary_variable(CG(active_op_array));
+ if (extends_ast) {
+ opline->opcode = ZEND_DECLARE_ANON_INHERITED_CLASS;
+ opline->op2_type = IS_CONST;
+ opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), zend_string_copy(ce->parent_name));
+ } else {
+ opline->opcode = ZEND_DECLARE_ANON_CLASS;
+ }
+
+ if (!zend_hash_add_ptr(CG(class_table), lcname, ce)) {
+ /* this anonymous class has been included */
+ zval zv;
+ ZVAL_PTR(&zv, ce);
+ destroy_zend_class(&zv);
+ return;
+ }
+ } else {
+ zend_string *key = zend_build_runtime_definition_key(lcname, decl->lex_pos);
+
+ /* RTD key is placed after lcname literal in op1 */
+ zend_add_literal_string(CG(active_op_array), &key);
+ zend_hash_update_ptr(CG(class_table), key, ce);
+
+ if (extends_ast) {
+ if (toplevel
+ && (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING)
+ /* We currently don't early-bind classes that implement interfaces or use traits */
+ && !(ce->ce_flags & (ZEND_ACC_IMPLEMENT_INTERFACES|ZEND_ACC_IMPLEMENT_TRAITS))) {
+ CG(active_op_array)->fn_flags |= ZEND_ACC_EARLY_BINDING;
+ opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;
+ opline->result_type = IS_UNUSED;
+ opline->result.opline_num = -1;
+ } else {
+ opline->opcode = ZEND_DECLARE_INHERITED_CLASS;
+ }
+ opline->op2_type = IS_CONST;
+ opline->op2.constant = zend_add_class_name_literal(CG(active_op_array), zend_string_copy(ce->parent_name));
+ } else {
+ opline->opcode = ZEND_DECLARE_CLASS;
+ }
+ }
}
/* }}} */
@@ -8139,15 +8014,20 @@ void zend_compile_top_stmt(zend_ast *ast) /* {{{ */
return;
}
- zend_compile_stmt(ast);
-
+ if (ast->kind == ZEND_AST_FUNC_DECL) {
+ CG(zend_lineno) = ast->lineno;
+ zend_compile_func_decl(NULL, ast, 1);
+ CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
+ } else if (ast->kind == ZEND_AST_CLASS) {
+ CG(zend_lineno) = ast->lineno;
+ zend_compile_class_decl(ast, 1);
+ CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
+ } else {
+ zend_compile_stmt(ast);
+ }
if (ast->kind != ZEND_AST_NAMESPACE && ast->kind != ZEND_AST_HALT_COMPILER) {
zend_verify_namespace();
}
- if (ast->kind == ZEND_AST_FUNC_DECL || ast->kind == ZEND_AST_CLASS) {
- CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno;
- zend_do_early_binding();
- }
}
/* }}} */
@@ -8221,7 +8101,7 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
break;
case ZEND_AST_FUNC_DECL:
case ZEND_AST_METHOD:
- zend_compile_func_decl(NULL, ast);
+ zend_compile_func_decl(NULL, ast, 0);
break;
case ZEND_AST_PROP_DECL:
zend_compile_prop_decl(ast);
@@ -8233,7 +8113,7 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */
zend_compile_use_trait(ast);
break;
case ZEND_AST_CLASS:
- zend_compile_class_decl(ast);
+ zend_compile_class_decl(ast, 0);
break;
case ZEND_AST_GROUP_USE:
zend_compile_group_use(ast);
@@ -8380,7 +8260,7 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
zend_compile_magic_const(result, ast);
return;
case ZEND_AST_CLOSURE:
- zend_compile_func_decl(result, ast);
+ zend_compile_func_decl(result, ast, 0);
return;
default:
ZEND_ASSERT(0 /* not supported */);