diff --git a/.github/workflows/remove-needs-feedback.yml b/.github/workflows/remove-needs-feedback.yml index 6f8e5a3362cb1..8f6dfc47f1664 100644 --- a/.github/workflows/remove-needs-feedback.yml +++ b/.github/workflows/remove-needs-feedback.yml @@ -13,3 +13,6 @@ jobs: - uses: actions-ecosystem/action-remove-labels@v1 with: labels: "Status: Needs Feedback" + - uses: actions-ecosystem/action-add-labels@v1 + with: + labels: "Status: Needs Triage" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a70511ad64ad7..bd1ed6f4792cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,7 +80,7 @@ repository. Mailing list subscription is explained on the [mailing lists page](https://www.php.net/mailing-lists.php). You may also want to read -[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process) +[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/post/the-mysterious-php-rfc-process-and-how-you-can-change-the-web) for additional notes on the best way to approach submitting an RFC. ## Writing tests diff --git a/NEWS b/NEWS index 3bdec63ad2290..0a53434c36a9f 100644 --- a/NEWS +++ b/NEWS @@ -13,10 +13,13 @@ PHP NEWS - OCI8: . Added oci8.prefetch_lob_size directive to tune LOB query performance + . Support for building against Oracle Client libraries 10.1 and 10.2 has been + dropped. Oracle Client libraries 11.2 or newer are now required. - Standard: . net_get_interfaces() also reports wireless network interfaces on Windows. (Yurun) + . Finished AVIF support in getimagesize(). (Yannis Guyon) - Zip: . add ZipArchive::clearError() method diff --git a/README.REDIST.BINS b/README.REDIST.BINS index 64123ab004953..19e6a02b18269 100644 --- a/README.REDIST.BINS +++ b/README.REDIST.BINS @@ -15,6 +15,7 @@ 15. ext/phar/zip.c portion extracted from libzip 16. libbcmath (ext/bcmath) see ext/bcmath/libbcmath/LICENSE 17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP +18. avifinfo (ext/standard/libavifinfo) see ext/standard/libavifinfo/LICENSE 3. pcre2lib (ext/pcre) @@ -591,7 +592,7 @@ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -16. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP +17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP The OpenLDAP Public License Version 2.8, 17 August 2003 diff --git a/UPGRADING b/UPGRADING index 3b02ec4bf3f2b..4d918a85bfc17 100644 --- a/UPGRADING +++ b/UPGRADING @@ -19,6 +19,14 @@ PHP 8.2 UPGRADE NOTES 1. Backward Incompatible Changes ======================================== +- Standard: + . strtolower() and strtoupper() are no longer locale-sensitive. They now + perform ASCII case conversion, as if the locale were "C". Use + mb_strtolower() if you want localized case conversion. Similarly, stristr, + stripos, strripos, lcfirst, ucfirst, ucwords, str_ireplace, + array_change_key_case and sorting with SORT_FLAG_CASE use ASCII case + conversion. + ======================================== 2. New Features ======================================== @@ -28,9 +36,10 @@ PHP 8.2 UPGRADE NOTES HTTP method in curl_getinfo() return value. - OCI8: - . Added an oci8.prefetch_lob_size directive to tune LOB query performance - by reducing the number of round-trips between PHP and Oracle Database when - fetching LOBS. This is usable with Oracle Database 12.2 or later. + . Added an oci8.prefetch_lob_size directive and oci_set_prefetch_lob() + function to tune LOB query performance by reducing the number of + round-trips between PHP and Oracle Database when fetching LOBS. This is + usable with Oracle Database 12.2 or later. - PCRE: . Added support for the "n" (NO_AUTO_CAPTURE) modifier, which makes simple @@ -117,6 +126,14 @@ PHP 8.2 UPGRADE NOTES 9. Other Changes to Extensions ======================================== +- OCI8: + . The minimum Oracle Client library version required is now 11.2. + +- Standard: + . getimagesize() now reports the actual image dimensions, bits and channels + of AVIF images. Previously, the dimensions have been reported as 0x0, and + bits and channels have not been reported at all. + - Zip: . extension updated to 1.20.0 with new methods: ZipArchive::clearError, getStreamName and getStreamIndex @@ -128,6 +145,9 @@ PHP 8.2 UPGRADE NOTES - COM_DOTNET: . LOCALE_NEUTRAL +- Sockets: + . SO_INCOMING_CPU + ======================================== 11. Changes to INI File Handling ======================================== @@ -140,6 +160,11 @@ PHP 8.2 UPGRADE NOTES . Windows specific error messages are no longer localized, but instead in English to better match PHP error messages. +- OCI8: + . Since building against Oracle Client 10g is no longer supported anyway, + the configuration option --with-oci8 has been dropped. --with-oci8-11g, + --with-oci8-12c and --with-oci8-19 are still supported. + ======================================== 13. Other Changes ======================================== diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 7feaee74ef705..7175f0b54b4db 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -6,6 +6,8 @@ PHP 8.2 INTERNALS UPGRADE NOTES 3. Module changes +4. OpCode changes + ======================== 1. Internal API changes ======================== @@ -24,3 +26,14 @@ PHP 8.2 INTERNALS UPGRADE NOTES 3. Module changes ======================== +======================== +4. OpCode changes +======================== + +* The ZEND_INIT_FCALL opcode now asserts that the function exists in the symbol + table as the function's existence is checked at compile time. + For extensions modifying the function symbol table, setting + CG(compiler_options) |= ZEND_COMPILE_IGNORE_USER_FUNCTIONS | ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS; + will produce ZEND_INIT_FCALL_BY_NAME opcodes instead which check for the + existence of the function at runtime. + diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c index 0902979ca55fa..087add4cbed66 100644 --- a/Zend/Optimizer/dce.c +++ b/Zend/Optimizer/dce.c @@ -620,7 +620,10 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_d while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) { zend_bitset_excl(ctx.instr_dead, i); add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1); - if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) { + if (i < op_array->last + && (op_array->opcodes[i+1].opcode == ZEND_OP_DATA + || (op_array->opcodes[i].opcode == ZEND_NEW + && op_array->opcodes[i+1].opcode == ZEND_DO_FCALL))) { zend_bitset_excl(ctx.instr_dead, i+1); add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1); } diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 406ff625dc249..1f846959bc009 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -308,7 +308,7 @@ static inline bool can_elide_return_type_check( } static bool opline_supports_assign_contraction( - zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) { + zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) { if (opline->opcode == ZEND_NEW) { /* see Zend/tests/generators/aborted_yield_during_new.phpt */ return 0; @@ -342,6 +342,13 @@ static bool opline_supports_assign_contraction( return opline->op1_type != IS_CV || opline->op1.var != cv_var; } + if (opline->opcode == ZEND_ASSIGN_OP + && opline->op1_type == IS_CV + && opline->op1.var == cv_var + && zend_may_throw(opline, &ssa->ops[ssa->vars[src_var].definition], op_array, ssa)) { + return 0; + } + return 1; } @@ -716,9 +723,13 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) uint32_t op_num; zend_op *opline; zend_ssa_op *ssa_op; + bool can_follow = 1; while (next_block_num < ssa->cfg.blocks_count && !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) { + if (ssa->cfg.blocks[next_block_num].flags & ZEND_BB_UNREACHABLE_FREE) { + can_follow = 0; + } next_block_num++; } @@ -730,7 +741,7 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) switch (opline->opcode) { case ZEND_JMP: optimize_jmp: - if (block->successors[0] == next_block_num) { + if (block->successors[0] == next_block_num && can_follow) { MAKE_NOP(opline); removed_ops++; goto optimize_nop; @@ -751,7 +762,7 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) goto optimize_jmp; } } else { - if (block->successors[0] == next_block_num) { + if (block->successors[0] == next_block_num && can_follow) { take_successor_0(ssa, block_num, block); if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { opline->opcode = ZEND_CHECK_VAR; @@ -782,7 +793,7 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) goto optimize_nop; } } else if (block->successors_count == 2) { - if (block->successors[0] == next_block_num) { + if (block->successors[0] == next_block_num && can_follow) { take_successor_0(ssa, block_num, block); if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { opline->opcode = ZEND_CHECK_VAR; @@ -816,7 +827,7 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa) } else if (block->successors_count == 2) { if (block->successors[0] == block->successors[1]) { take_successor_0(ssa, block_num, block); - if (block->successors[0] == next_block_num) { + if (block->successors[0] == next_block_num && can_follow) { if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) { opline->opcode = ZEND_CHECK_VAR; opline->op2.num = 0; @@ -1327,7 +1338,7 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx && !ssa->vars[src_var].phi_use_chain && !ssa->vars[src_var].sym_use_chain && opline_supports_assign_contraction( - ssa, &op_array->opcodes[ssa->vars[src_var].definition], + op_array, ssa, &op_array->opcodes[ssa->vars[src_var].definition], src_var, opline->result.var) && !variable_defined_or_used_in_range(ssa, EX_VAR_TO_NUM(opline->result.var), ssa->vars[src_var].definition+1, op_1) @@ -1486,7 +1497,7 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx && !ssa->vars[src_var].phi_use_chain && !ssa->vars[src_var].sym_use_chain && opline_supports_assign_contraction( - ssa, &op_array->opcodes[ssa->vars[src_var].definition], + op_array, ssa, &op_array->opcodes[ssa->vars[src_var].definition], src_var, opline->op1.var) && !variable_defined_or_used_in_range(ssa, EX_VAR_TO_NUM(opline->op1.var), ssa->vars[src_var].definition+1, op_1) diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c index 3579ea1c6bfc8..2877d1d30841a 100644 --- a/Zend/Optimizer/pass1.c +++ b/Zend/Optimizer/pass1.c @@ -103,33 +103,9 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; case ZEND_ASSIGN_OP: - if (opline->op2_type == IS_CONST) { - if (opline->extended_value == ZEND_ADD - || opline->extended_value == ZEND_SUB - || opline->extended_value == ZEND_MUL - || opline->extended_value == ZEND_DIV - || opline->extended_value == ZEND_POW) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) { - /* don't optimize if it should produce a runtime numeric string error */ - if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) { - convert_scalar_to_number(&ZEND_OP2_LITERAL(opline)); - } - } - } else if (opline->extended_value == ZEND_MOD - || opline->extended_value == ZEND_SL - || opline->extended_value == ZEND_SR) { - zval *op2 = &ZEND_OP2_LITERAL(opline); - if (Z_TYPE_P(op2) != IS_LONG) { - if (!zend_is_op_long_compatible(op2)) { - break; - } - convert_to_long(op2); - } - } else if (opline->extended_value == ZEND_CONCAT) { - if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { - convert_to_string(&ZEND_OP2_LITERAL(opline)); - } - } + if (opline->extended_value == ZEND_CONCAT && opline->op2_type == IS_CONST + && Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); } break; diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index 00ab129bf6eec..631037a2a8e8e 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -799,59 +799,21 @@ static inline zend_result ct_eval_array_key_exists(zval *result, zval *op1, zval return SUCCESS; } -static bool can_ct_eval_func_call(zend_string *name, uint32_t num_args, zval **args) { - /* Functions in this list must always produce the same result for the same arguments, +static bool can_ct_eval_func_call(zend_function *func, zend_string *name, uint32_t num_args, zval **args) { + /* Precondition: func->type == ZEND_INTERNAL_FUNCTION, this is a global function */ + /* Functions setting ZEND_ACC_COMPILE_TIME_EVAL (@compile-time-eval) must always produce the same result for the same arguments, * and have no dependence on global state (such as locales). It is okay if they throw * or warn on invalid arguments, as we detect this and will discard the evaluation result. */ - if (false - || zend_string_equals_literal(name, "array_diff") - || zend_string_equals_literal(name, "array_diff_assoc") - || zend_string_equals_literal(name, "array_diff_key") - || zend_string_equals_literal(name, "array_flip") - || zend_string_equals_literal(name, "array_is_list") - || zend_string_equals_literal(name, "array_key_exists") - || zend_string_equals_literal(name, "array_keys") - || zend_string_equals_literal(name, "array_merge") - || zend_string_equals_literal(name, "array_merge_recursive") - || zend_string_equals_literal(name, "array_replace") - || zend_string_equals_literal(name, "array_replace_recursive") - || zend_string_equals_literal(name, "array_unique") - || zend_string_equals_literal(name, "array_values") - || zend_string_equals_literal(name, "base64_decode") - || zend_string_equals_literal(name, "base64_encode") + if (func->common.fn_flags & ZEND_ACC_COMPILE_TIME_EVAL) { + /* This has @compile-time-eval in stub info and uses a macro such as ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE */ + return true; + } #ifndef ZEND_WIN32 - /* On Windows this function may be code page dependent. */ - || zend_string_equals_literal(name, "dirname") -#endif - || zend_string_equals_literal(name, "explode") - || zend_string_equals_literal(name, "imagetypes") - || zend_string_equals_literal(name, "in_array") - || zend_string_equals_literal(name, "implode") - || zend_string_equals_literal(name, "ltrim") - || zend_string_equals_literal(name, "php_sapi_name") - || zend_string_equals_literal(name, "php_uname") - || zend_string_equals_literal(name, "phpversion") - || zend_string_equals_literal(name, "pow") - || zend_string_equals_literal(name, "preg_quote") - || zend_string_equals_literal(name, "rawurldecode") - || zend_string_equals_literal(name, "rawurlencode") - || zend_string_equals_literal(name, "rtrim") - || zend_string_equals_literal(name, "serialize") - || zend_string_equals_literal(name, "str_contains") - || zend_string_equals_literal(name, "str_ends_with") - || zend_string_equals_literal(name, "str_replace") - || zend_string_equals_literal(name, "str_split") - || zend_string_equals_literal(name, "str_starts_with") - || zend_string_equals_literal(name, "strpos") - || zend_string_equals_literal(name, "strstr") - || zend_string_equals_literal(name, "substr") - || zend_string_equals_literal(name, "trim") - || zend_string_equals_literal(name, "urldecode") - || zend_string_equals_literal(name, "urlencode") - || zend_string_equals_literal(name, "version_compare") - ) { + /* On Windows this function may be code page dependent. */ + if (zend_string_equals_literal(name, "dirname")) { return true; } +#endif if (num_args == 2) { if (zend_string_equals_literal(name, "str_repeat")) { @@ -918,7 +880,7 @@ static inline zend_result ct_eval_func_call( } } - if (!can_ct_eval_func_call(name, num_args, args)) { + if (!can_ct_eval_func_call(func, name, num_args, args)) { return FAILURE; } @@ -2244,6 +2206,17 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zend_ssa_remove_result_def(ssa, ssa_op); if (opline->opcode == ZEND_DO_ICALL) { removed_ops = remove_call(ctx, opline, ssa_op); + } else if (opline->opcode == ZEND_TYPE_CHECK + && (opline->op1_type & (IS_VAR|IS_TMP_VAR)) + && (!value_known(&ctx->values[ssa_op->op1_use]) + || IS_PARTIAL_ARRAY(&ctx->values[ssa_op->op1_use]) + || IS_PARTIAL_OBJECT(&ctx->values[ssa_op->op1_use]))) { + /* For TYPE_CHECK we may compute the result value without knowing the + * operand, based on type inference information. Make sure the operand is + * freed and leave further cleanup to DCE. */ + opline->opcode = ZEND_FREE; + opline->result_type = IS_UNUSED; + removed_ops++; } else { zend_ssa_remove_instr(ssa, opline, ssa_op); removed_ops++; diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 0e18481f40738..d0d54989dc71d 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -171,9 +171,9 @@ static const func_info_t func_infos[] = { F1("gmp_sqrtrem", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), F1("gmp_rootrem", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), F1("gmp_gcdext", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_OBJECT), - F1("hash", MAY_BE_STRING|MAY_BE_FALSE), + F1("hash", MAY_BE_STRING), F1("hash_file", MAY_BE_STRING|MAY_BE_FALSE), - F1("hash_hmac", MAY_BE_STRING|MAY_BE_FALSE), + F1("hash_hmac", MAY_BE_STRING), F1("hash_hmac_file", MAY_BE_STRING|MAY_BE_FALSE), F1("hash_init", MAY_BE_OBJECT), F1("hash_final", MAY_BE_STRING), diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 6a952557d9ea7..52d093db116f7 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -1037,8 +1037,6 @@ static bool zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *s ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp) { - zend_long op1_min, op2_min, op1_max, op2_max; - tmp->underflow = 0; tmp->overflow = 0; switch (opline->opcode) { @@ -1066,8 +1064,8 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, zend tmp->min = ZEND_LONG_MIN; tmp->max = ZEND_LONG_MAX; } else { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); + zend_long op1_min = OP1_MIN_RANGE(); + zend_long op1_max = OP1_MAX_RANGE(); tmp->min = ~op1_max; tmp->max = ~op1_min; } @@ -1100,144 +1098,6 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, zend } } break; - case ZEND_BOOL: - case ZEND_JMPZ_EX: - case ZEND_JMPNZ_EX: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - tmp->min = (op1_min > 0 || op1_max < 0); - tmp->max = (op1_min != 0 || op1_max != 0); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_BOOL_NOT: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - tmp->min = (op1_min == 0 && op1_max == 0); - tmp->max = (op1_min <= 0 && op1_max >= 0); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_BOOL_XOR: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - op1_min = (op1_min > 0 || op1_max < 0); - op1_max = (op1_min != 0 || op1_max != 0); - op2_min = (op2_min > 0 || op2_max < 0); - op2_max = (op2_min != 0 || op2_max != 0); - tmp->min = 0; - tmp->max = 1; - if (op1_min == op1_max && op2_min == op2_max) { - if (op1_min == op2_min) { - tmp->max = 0; - } else { - tmp->min = 1; - } - } - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_IDENTICAL: - case ZEND_IS_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = (op1_min == op1_max && - op2_min == op2_max && - op1_min == op2_max); - tmp->max = (op1_min <= op2_max && op1_max >= op2_min); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_NOT_IDENTICAL: - case ZEND_IS_NOT_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = (op1_min > op2_max || op1_max < op2_min); - tmp->max = (op1_min != op1_max || - op2_min != op2_max || - op1_min != op2_max); - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_SMALLER: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = op1_max < op2_min; - tmp->max = op1_min < op2_max; - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; - case ZEND_IS_SMALLER_OR_EQUAL: - if (ssa_op->result_def == var) { - if (OP1_HAS_RANGE() && OP2_HAS_RANGE()) { - op1_min = OP1_MIN_RANGE(); - op2_min = OP2_MIN_RANGE(); - op1_max = OP1_MAX_RANGE(); - op2_max = OP2_MAX_RANGE(); - - tmp->min = op1_max <= op2_min; - tmp->max = op1_min <= op2_max; - return 1; - } else { - tmp->min = 0; - tmp->max = 1; - return 1; - } - } - break; case ZEND_QM_ASSIGN: case ZEND_JMP_SET: case ZEND_COALESCE: @@ -1263,13 +1123,6 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, zend } } break; - case ZEND_ASSERT_CHECK: - if (ssa_op->result_def == var) { - tmp->min = 0; - tmp->max = 1; - return 1; - } - break; case ZEND_SEND_VAR: if (ssa_op->op1_def == var) { if (ssa_op->op1_def >= 0) { @@ -1450,12 +1303,6 @@ ZEND_API bool zend_inference_propagate_range(const zend_op_array *op_array, zend tmp->max = ZEND_LONG_MAX; tmp->overflow = 0; return 1; - } else if (mask == MAY_BE_BOOL) { - tmp->underflow = 0; - tmp->min = 0; - tmp->max = 1; - tmp->overflow = 0; - return 1; } } } @@ -2636,7 +2483,7 @@ static zend_always_inline zend_result _zend_update_type_info( ssa, opline->extended_value, t1, t2, opline->opcode == ZEND_ASSIGN_OP ? ssa_op->op1_def : -1, optimization_level); if (tmp & (MAY_BE_STRING|MAY_BE_ARRAY)) { - tmp |= MAY_BE_RC1; + tmp |= MAY_BE_RC1 | MAY_BE_RCN; } if (tmp & (MAY_BE_OBJECT|MAY_BE_RESOURCE)) { tmp |= MAY_BE_RC1 | MAY_BE_RCN; @@ -3224,7 +3071,9 @@ static zend_always_inline zend_result _zend_update_type_info( arr_type = RES_USE_INFO(); } tmp = MAY_BE_RC1|MAY_BE_ARRAY|arr_type; - if (opline->op1_type != IS_UNUSED) { + if (opline->op1_type != IS_UNUSED + && (opline->op2_type == IS_UNUSED + || (t2 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE|MAY_BE_STRING)))) { tmp |= assign_dim_array_result_type(arr_type, t2, t1, opline->op2_type); if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) { tmp |= MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 6e8cd7be61908..9ca2e11a6f7ee 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -334,10 +334,6 @@ static void place_essa_pis( if (Z_TYPE_P(zv) == IS_LONG) { add_val2 = Z_LVAL_P(zv); - } else if (Z_TYPE_P(zv) == IS_FALSE) { - add_val2 = 0; - } else if (Z_TYPE_P(zv) == IS_TRUE) { - add_val2 = 1; } else { var1 = -1; } @@ -355,10 +351,6 @@ static void place_essa_pis( zval *zv = CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1); if (Z_TYPE_P(zv) == IS_LONG) { add_val1 = Z_LVAL_P(CRT_CONSTANT_EX(op_array, (opline-1), (opline-1)->op1)); - } else if (Z_TYPE_P(zv) == IS_FALSE) { - add_val1 = 0; - } else if (Z_TYPE_P(zv) == IS_TRUE) { - add_val1 = 1; } else { var2 = -1; } diff --git a/Zend/tests/assign_op_type_error.phpt b/Zend/tests/assign_op_type_error.phpt new file mode 100644 index 0000000000000..5f175613e20b8 --- /dev/null +++ b/Zend/tests/assign_op_type_error.phpt @@ -0,0 +1,57 @@ +--TEST-- +TypeError for compound assignment operations +--FILE-- +getMessage(), "\n"; +} +try { + $x -= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x *= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x /= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x **= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x %= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x <<= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $x >>= "1"; +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Unsupported operand types: array + string +Unsupported operand types: array - string +Unsupported operand types: array * string +Unsupported operand types: array / string +Unsupported operand types: array ** string +Unsupported operand types: array % string +Unsupported operand types: array << string +Unsupported operand types: array >> string diff --git a/Zend/tests/bug38623.phpt b/Zend/tests/bug38623.phpt index 873cdf42d4616..5060850054d10 100644 --- a/Zend/tests/bug38623.phpt +++ b/Zend/tests/bug38623.phpt @@ -2,8 +2,14 @@ Bug #38623 (leaks in a tricky code with switch() and exceptions) --FILE-- x; +var_dump($a); +?> +--EXPECT-- +array(2) { + [0]=> + NULL + [1]=> + NULL +} diff --git a/Zend/tests/constants/final_constants/final_const13.phpt b/Zend/tests/constants/final_constants/final_const13.phpt new file mode 100644 index 0000000000000..5e19e8a212695 --- /dev/null +++ b/Zend/tests/constants/final_constants/final_const13.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug GH-7757 (Multi-inherited final constant causes fatal error) +--FILE-- + +--EXPECT-- diff --git a/Zend/tests/falsetoarray_002.phpt b/Zend/tests/falsetoarray_002.phpt new file mode 100644 index 0000000000000..c01b799545965 --- /dev/null +++ b/Zend/tests/falsetoarray_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Autovivification of false to array with data clobbering by error handler +--FILE-- + +--EXPECT-- +Err: Automatic conversion of false to array is deprecated +string(0) "" diff --git a/Zend/tests/falsetoarray_003.phpt b/Zend/tests/falsetoarray_003.phpt new file mode 100644 index 0000000000000..11b32771e1fc1 --- /dev/null +++ b/Zend/tests/falsetoarray_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Autovivification of false to array with data clobbering by error handler +--FILE-- + +DONE +--EXPECTF-- +Err: Implicit conversion from float %f to int loses precision +Err: Undefined array key %i +DONE diff --git a/Zend/tests/lc_ctype_inheritance.phpt b/Zend/tests/lc_ctype_inheritance.phpt deleted file mode 100644 index 8c968f0615e6e..0000000000000 --- a/Zend/tests/lc_ctype_inheritance.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -Do not inherit LC_CTYPE from environment ---SKIPIF-- - ---ENV-- -LC_CTYPE=de_DE ---FILE-- - ---EXPECTF-- -string(%d) "C%r(\.UTF-8)?%r" -string(2) "e4" -int(0) -bool(true) -string(2) "c4" -int(1) diff --git a/Zend/tests/objects_034.phpt b/Zend/tests/objects_034.phpt index ddb11a13fea28..5594e756aface 100644 --- a/Zend/tests/objects_034.phpt +++ b/Zend/tests/objects_034.phpt @@ -2,7 +2,17 @@ Array object clobbering by user error handler --FILE-- +DONE +--EXPECT-- +DONE diff --git a/Zend/zend.c b/Zend/zend.c index e42fc648a4dc7..c37cd9ed97453 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1331,7 +1331,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at( /* This is very inefficient for a large number of errors. * Use pow2 realloc if it becomes a problem. */ EG(num_errors)++; - EG(errors) = erealloc(EG(errors), sizeof(zend_error_info) * EG(num_errors)); + EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors)); EG(errors)[EG(num_errors)-1] = info; } @@ -1575,7 +1575,7 @@ ZEND_API ZEND_COLD void zend_error_zstr(int type, zend_string *message) { ZEND_API void zend_begin_record_errors(void) { - ZEND_ASSERT(!EG(record_errors) && "Error recoreding already enabled"); + ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled"); EG(record_errors) = true; EG(num_errors) = 0; EG(errors) = NULL; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index e31745298d9da..ecb6e8296befb 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -95,6 +95,17 @@ typedef struct _zend_fcall_info_cache { #define ZEND_NS_FENTRY(ns, zend_name, name, arg_info, flags) ZEND_RAW_FENTRY(ZEND_NS_NAME(ns, #zend_name), name, arg_info, flags) #define ZEND_NS_RAW_FENTRY(ns, zend_name, name, arg_info, flags) ZEND_RAW_FENTRY(ZEND_NS_NAME(ns, zend_name), name, arg_info, flags) +/** + * Note that if you are asserting that a function is compile-time evaluable, you are asserting that + * + * 1. The function will always have the same result for the same arguments + * 2. The function does not depend on global state such as ini settings or locale (e.g. mb_strtolower), number_format(), etc. + * 3. The function does not have side effects. It is okay if they throw + * or warn on invalid arguments, as we detect this and will discard the evaluation result. + * 4. The function will not take an unreasonable amount of time or memory to compute on code that may be seen in practice. + * (e.g. str_repeat is special cased to check the length instead of using this) + */ +#define ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(name, arg_info) ZEND_RAW_FENTRY(#name, zif_##name, arg_info, ZEND_ACC_COMPILE_TIME_EVAL) /* Same as ZEND_NS_NAMED_FE */ #define ZEND_NS_RAW_NAMED_FE(ns, zend_name, name, arg_info) ZEND_NS_RAW_FENTRY(ns, #zend_name, name, arg_info, 0) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index aa0fe9fad5908..ace032e5e72d5 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -662,6 +662,18 @@ static zend_always_inline int zend_mm_bitset_is_free_range(zend_mm_bitset *bitse /* Chunks */ /**********/ +static zend_always_inline void zend_mm_hugepage(void* ptr, size_t size) +{ +#if defined(MADV_HUGEPAGE) + (void)madvise(ptr, size, MADV_HUGEPAGE); +#elif defined(HAVE_MEMCNTL) + struct memcntl_mha m = {.mha_cmd = MHA_MAPSIZE_VA, .mha_pagesize = ZEND_MM_CHUNK_SIZE, .mha_flags = 0}; + (void)memcntl(ptr, size, MC_HAT_ADVISE, (char *)&m, 0, 0); +#else + zend_error_noreturn(E_WARNING, "huge_pages: thp unsupported on this platform"); +#endif +} + static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) { void *ptr = zend_mm_mmap(size); @@ -669,11 +681,9 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) if (ptr == NULL) { return NULL; } else if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) == 0) { -#ifdef MADV_HUGEPAGE if (zend_mm_use_huge_pages) { - madvise(ptr, size, MADV_HUGEPAGE); + zend_mm_hugepage(ptr, size); } -#endif return ptr; } else { size_t offset; @@ -702,11 +712,9 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment) if (alignment > REAL_PAGE_SIZE) { zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE); } -# ifdef MADV_HUGEPAGE if (zend_mm_use_huge_pages) { - madvise(ptr, size, MADV_HUGEPAGE); + zend_mm_hugepage(ptr, size); } -# endif #endif return ptr; } @@ -2304,10 +2312,10 @@ void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) #endif memset(heap->free_slot, 0, sizeof(heap->free_slot)); #if ZEND_MM_STAT || ZEND_MM_LIMIT - heap->real_size = ZEND_MM_CHUNK_SIZE; + heap->real_size = (heap->cached_chunks_count + 1) * ZEND_MM_CHUNK_SIZE; #endif #if ZEND_MM_STAT - heap->real_peak = ZEND_MM_CHUNK_SIZE; + heap->real_peak = (heap->cached_chunks_count + 1) * ZEND_MM_CHUNK_SIZE; #endif heap->chunks_count = 1; heap->peak_chunks_count = 1; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 98a50c66194c7..20acca3a47ff7 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -18,12 +18,16 @@ function func_get_args(): array {} function strlen(string $string): int {} +/** @compile-time-eval */ function strcmp(string $string1, string $string2): int {} +/** @compile-time-eval */ function strncmp(string $string1, string $string2, int $length): int {} +/** @compile-time-eval */ function strcasecmp(string $string1, string $string2): int {} +/** @compile-time-eval */ function strncasecmp(string $string1, string $string2, int $length): int {} function error_reporting(?int $error_level = null): int {} diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index b358d313b60be..37ea1f7aab1b3 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f87d92c002674c431827895a8d8b3a5da3b95482 */ + * Stub hash: 69dcb08ae12b6acbba872f7de5018ca5c0aaf669 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -283,10 +283,10 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(func_get_arg, arginfo_func_get_arg) ZEND_FE(func_get_args, arginfo_func_get_args) ZEND_FE(strlen, arginfo_strlen) - ZEND_FE(strcmp, arginfo_strcmp) - ZEND_FE(strncmp, arginfo_strncmp) - ZEND_FE(strcasecmp, arginfo_strcasecmp) - ZEND_FE(strncasecmp, arginfo_strncasecmp) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strcmp, arginfo_strcmp) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strncmp, arginfo_strncmp) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strcasecmp, arginfo_strcasecmp) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strncasecmp, arginfo_strncasecmp) ZEND_FE(error_reporting, arginfo_error_reporting) ZEND_FE(define, arginfo_define) ZEND_FE(defined, arginfo_defined) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 106b2da8ae4f6..039e95913f6d3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2179,9 +2179,11 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */ ZEND_ASSERT(count >= offset); for (i = offset; i < count; ++i) { - if (oplines[i].opcode != ZEND_NOP) { + if (EXPECTED(oplines[i].opcode != ZEND_NOP)) { opline = get_next_op(); memcpy(opline, &oplines[i], sizeof(zend_op)); + } else { + opline = CG(active_op_array)->opcodes + oplines[i].extended_value; } } @@ -2825,15 +2827,28 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t zend_separate_if_call_and_write(&obj_node, obj_ast, type); if (nullsafe) { - /* Flush delayed oplines */ - zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack)); - uint32_t i, count = zend_stack_count(&CG(delayed_oplines_stack)); - - for (i = 0; i < count; ++i) { - if (oplines[i].opcode != ZEND_NOP) { - opline = get_next_op(); - memcpy(opline, &oplines[i], sizeof(zend_op)); - oplines[i].opcode = ZEND_NOP; + if (obj_node.op_type == IS_TMP_VAR) { + /* Flush delayed oplines */ + zend_op *opline = NULL, *oplines = zend_stack_base(&CG(delayed_oplines_stack)); + uint32_t var = obj_node.u.op.var; + uint32_t count = zend_stack_count(&CG(delayed_oplines_stack)); + uint32_t i = count; + + while (i > 0 && oplines[i-1].result_type == IS_TMP_VAR && oplines[i-1].result.var == var) { + i--; + if (oplines[i].op1_type == IS_TMP_VAR) { + var = oplines[i].op1.var; + } else { + break; + } + } + for (; i < count; ++i) { + if (oplines[i].opcode != ZEND_NOP) { + opline = get_next_op(); + memcpy(opline, &oplines[i], sizeof(zend_op)); + oplines[i].opcode = ZEND_NOP; + oplines[i].extended_value = opline - CG(active_op_array)->opcodes; + } } } zend_emit_jmp_null(&obj_node); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 65d675d77f3fd..b3e2d62737d51 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -301,7 +301,7 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ -/* Function Flags (unused: 27-30) | | | */ +/* Function Flags (unused: 28-30) | | | */ /* ============== | | | */ /* | | | */ /* deprecation flag | | | */ @@ -357,6 +357,9 @@ typedef struct _zend_oparray_context { /* method flag used by Closure::__invoke() (int only) | | | */ #define ZEND_ACC_USER_ARG_INFO (1 << 26) /* | X | | */ /* | | | */ +/* supports opcache compile-time evaluation (funcs) | | | */ +#define ZEND_ACC_COMPILE_TIME_EVAL (1 << 27) /* | X | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ @@ -556,6 +559,7 @@ struct _zend_execute_data { /* to prevent optimization in RETURN handler and */ /* keep all local variables for "fcall_end" handler */ #define ZEND_CALL_JIT_RESERVED (1 << 29) /* reserved for tracing JIT */ +#define ZEND_CALL_NEEDS_REATTACH (1 << 30) #define ZEND_CALL_SEND_ARG_BY_REF (1u << 31) #define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED) diff --git a/Zend/zend_cpuinfo.c b/Zend/zend_cpuinfo.c index 57889bb3f4ce3..08843a9401f46 100644 --- a/Zend/zend_cpuinfo.c +++ b/Zend/zend_cpuinfo.c @@ -75,7 +75,7 @@ static void __zend_cpuid(uint32_t func, uint32_t subfunc, zend_cpu_info *cpuinfo #if defined(__i386__) || defined(__x86_64__) /* Function based on compiler-rt implementation. */ -static unsigned get_xcr0_eax() { +static unsigned get_xcr0_eax(void) { # if defined(__GNUC__) || defined(__clang__) // Check xgetbv; this uses a .byte sequence instead of the instruction // directly because older assemblers do not include support for xgetbv and @@ -90,7 +90,7 @@ static unsigned get_xcr0_eax() { # endif } -static bool is_avx_supported() { +static bool is_avx_supported(void) { if (!(cpuinfo.ecx & ZEND_CPU_FEATURE_AVX)) { /* No support for AVX */ return 0; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 8375ff2ac5b1e..5f7c8d10e59c1 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1380,9 +1380,9 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_string_offset zend_type_error("Cannot access offset of type %s on string", zend_zval_type_name(offset)); } -static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline void zend_assign_to_object_dim(zend_object *obj, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC) { - Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), dim, value); + obj->handlers->write_dimension(obj, dim, value); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), value); @@ -1411,17 +1411,21 @@ static zend_always_inline int zend_binary_op(zval *ret, zval *op1, zval *op2 OPL return zend_binary_ops[opcode - ZEND_ADD](ret, op1, op2); } -static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline void zend_binary_assign_op_obj_dim(zend_object *obj, zval *property OPLINE_DC EXECUTE_DATA_DC) { zval *value; zval *z; zval rv, res; + GC_ADDREF(obj); + if (property && UNEXPECTED(Z_ISUNDEF_P(property))) { + property = ZVAL_UNDEFINED_OP2(); + } value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - if ((z = Z_OBJ_HT_P(object)->read_dimension(Z_OBJ_P(object), property, BP_VAR_R, &rv)) != NULL) { + if ((z = obj->handlers->read_dimension(obj, property, BP_VAR_R, &rv)) != NULL) { if (zend_binary_op(&res, z, value OPLINE_CC) == SUCCESS) { - Z_OBJ_HT_P(object)->write_dimension(Z_OBJ_P(object), property, &res); + obj->handlers->write_dimension(obj, property, &res); } if (z == &rv) { zval_ptr_dtor(&rv); @@ -1437,6 +1441,9 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval * } } FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value OPLINE_DC EXECUTE_DATA_DC) @@ -2390,22 +2397,17 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * } ZVAL_UNDEF(result); } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + GC_ADDREF(obj); if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - ZVAL_NULL(result); - return; - } } else if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result); + retval = obj->handlers->read_dimension(obj, dim, type, result); if (UNEXPECTED(retval == &EG(uninitialized_zval))) { - zend_class_entry *ce = Z_OBJCE_P(container); + zend_class_entry *ce = obj->ce; ZVAL_NULL(result); zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); @@ -2416,7 +2418,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * retval = result; } if (Z_TYPE_P(retval) != IS_OBJECT) { - zend_class_entry *ce = Z_OBJCE_P(container); + zend_class_entry *ce = obj->ce; zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); } } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { @@ -2429,18 +2431,32 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception"); ZVAL_UNDEF(result); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else { if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { - zend_false_to_array_deprecated(); - } if (type != BP_VAR_UNSET) { - array_init(container); + HashTable *ht = zend_new_array(0); + zend_uchar old_type = Z_TYPE_P(container); + + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto return_null; + } + } goto fetch_from_array; } else { + if (UNEXPECTED(Z_TYPE_P(container) == IS_FALSE)) { + zend_false_to_array_deprecated(); + } return_null: /* for read-mode only */ if (ZEND_CONST_COND(dim_type == IS_CV, dim != NULL) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { @@ -2585,13 +2601,16 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z ZVAL_CHAR(result, c); } } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + + GC_ADDREF(obj); if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { dim = ZVAL_UNDEFINED_OP2(); } if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, type, result); + retval = obj->handlers->read_dimension(obj, dim, type, result); ZEND_ASSERT(result != NULL); if (retval) { @@ -2603,6 +2622,9 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z } else { ZVAL_NULL(result); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else { if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { container = ZVAL_UNDEFINED_OP1(); @@ -2956,6 +2978,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } return; } + flags &= ZEND_FETCH_OBJ_FLAGS; if (flags) { zend_handle_fetch_obj_flags(result, ptr, NULL, prop_info, flags); } @@ -3001,6 +3024,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } ZVAL_INDIRECT(result, ptr); + flags &= ZEND_FETCH_OBJ_FLAGS; if (flags) { zend_property_info *prop_info; @@ -3198,6 +3222,7 @@ static zend_always_inline zend_result zend_fetch_static_property_address(zval ** } } + flags &= ZEND_FETCH_OBJ_FLAGS; if (flags && ZEND_TYPE_IS_SET(property_info->type)) { zend_handle_fetch_obj_flags(NULL, *retval, NULL, property_info, flags); } @@ -3817,7 +3842,9 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu EX(call) = NULL; EX(return_value) = return_value; - zend_attach_symbol_table(execute_data); + if (op_array->last_var) { + zend_attach_symbol_table(execute_data); + } if (!ZEND_MAP_PTR(op_array->run_time_cache)) { void *ptr; diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 92ae9b8837cb7..dd06f86ae8da6 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -32,6 +32,10 @@ # include #endif +#ifdef ZEND_FIBER_UCONTEXT +# include +#endif + #ifndef ZEND_WIN32 # include # include @@ -118,7 +122,6 @@ static zend_always_inline void zend_fiber_restore_vm_state(zend_fiber_vm_state * } #ifdef ZEND_FIBER_UCONTEXT -# include ZEND_TLS zend_fiber_transfer *transfer_data; #else /* boost_context_data is our customized definition of struct transfer_t as diff --git a/Zend/zend_gdb.c b/Zend/zend_gdb.c index e38e0023095fd..02afb6bc6f7bc 100644 --- a/Zend/zend_gdb.c +++ b/Zend/zend_gdb.c @@ -25,6 +25,11 @@ #include #include +#if defined(__FreeBSD__) +# include +# include +#endif + enum { ZEND_GDBJIT_NOACTION, ZEND_GDBJIT_REGISTER, @@ -105,6 +110,7 @@ ZEND_API void zend_gdb_unregister_all(void) ZEND_API bool zend_gdb_present(void) { bool ret = 0; +#if defined(__linux__) /* netbsd while having this procfs part, does not hold the tracer pid */ int fd = open("/proc/self/status", O_RDONLY); if (fd > 0) { @@ -136,6 +142,17 @@ ZEND_API bool zend_gdb_present(void) close(fd); } +#elif defined(__FreeBSD__) + struct kinfo_proc *proc = kinfo_getproc(getpid()); + + if (proc) { + if ((proc->ki_flag & P_TRACED) != 0) { + struct kinfo_proc *dbg = kinfo_getproc(proc->ki_tracer); + + ret = (dbg && strstr(dbg->ki_comm, "gdb")); + } + } +#endif return ret; } diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index a52a7fc96912c..680263b5cb07b 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1404,9 +1404,6 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (UNEXPECTED(Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF))); ht->nInternalPointer = MIN(ht->nInternalPointer, ht->nNumUsed); } - if (p->key) { - zend_string_release(p->key); - } if (ht->pDestructor) { zval tmp; ZVAL_COPY_VALUE(&tmp, &p->val); @@ -1435,6 +1432,10 @@ static zend_always_inline void _zend_hash_del_el(HashTable *ht, uint32_t idx, Bu } _zend_hash_del_el_ex(ht, idx, p, prev); + if (p->key) { + zend_string_release(p->key); + p->key = NULL; + } } ZEND_API void ZEND_FASTCALL zend_hash_packed_del_val(HashTable *ht, zval *zv) @@ -1476,6 +1477,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key p->key && zend_string_equal_content(p->key, key))) { _zend_hash_del_el_ex(ht, idx, p, prev); + zend_string_release(p->key); + p->key = NULL; return SUCCESS; } prev = p; @@ -1523,6 +1526,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string } } else { _zend_hash_del_el_ex(ht, idx, p, prev); + zend_string_release(p->key); + p->key = NULL; } return SUCCESS; } @@ -1567,6 +1572,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const ch } } else { _zend_hash_del_el_ex(ht, idx, p, prev); + zend_string_release(p->key); + p->key = NULL; } return SUCCESS; } @@ -1598,6 +1605,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char * && (ZSTR_LEN(p->key) == len) && !memcmp(ZSTR_VAL(p->key), str, len)) { _zend_hash_del_el_ex(ht, idx, p, prev); + zend_string_release(p->key); + p->key = NULL; return SUCCESS; } prev = p; @@ -1698,7 +1707,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { ht->pDestructor(&p->val); if (EXPECTED(p->key)) { - zend_string_release(p->key); + zend_string_release(p->key); } } } while (++p != end); @@ -1708,10 +1717,8 @@ ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht) } else { if (!HT_HAS_STATIC_KEYS_ONLY(ht)) { do { - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { - if (EXPECTED(p->key)) { - zend_string_release(p->key); - } + if (EXPECTED(p->key)) { + zend_string_release(p->key); } } while (++p != end); } @@ -1846,21 +1853,11 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) } } else { if (!HT_HAS_STATIC_KEYS_ONLY(ht)) { - if (HT_IS_WITHOUT_HOLES(ht)) { - do { - if (EXPECTED(p->key)) { - zend_string_release(p->key); - } - } while (++p != end); - } else { - do { - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF)) { - if (EXPECTED(p->key)) { - zend_string_release(p->key); - } - } - } while (++p != end); - } + do { + if (EXPECTED(p->key)) { + zend_string_release(p->key); + } + } while (++p != end); } } HT_HASH_RESET(ht); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index eab874d71e6cb..82af3d8afa75a 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1601,7 +1601,7 @@ static bool do_inherit_constant_check( } zend_class_constant *old_constant = Z_PTR_P(zv); - if ((ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { + if (parent_constant->ce != old_constant->ce && (ZEND_CLASS_CONST_FLAGS(parent_constant) & ZEND_ACC_FINAL)) { zend_error_noreturn(E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s", ZSTR_VAL(old_constant->ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_constant->ce->name), ZSTR_VAL(name) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 529893b1de882..e75ff8318cb48 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -619,11 +619,10 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; goto exit; } @@ -1754,11 +1753,10 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { value = &p->val; goto found; } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ca93f48603f58..de3664f5c653f 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -126,9 +126,9 @@ ZEND_API const unsigned char zend_toupper_map[256] = { * Functions using locale lowercase: zend_binary_strncasecmp_l zend_binary_strcasecmp_l + * Functions using ascii lowercase: string_compare_function_ex string_case_compare_function - * Functions using ascii lowercase: zend_str_tolower_copy zend_str_tolower_dup zend_str_tolower @@ -1997,7 +1997,7 @@ ZEND_API int ZEND_FASTCALL string_compare_function_ex(zval *op1, zval *op2, bool int ret; if (case_insensitive) { - ret = zend_binary_strcasecmp_l(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str1)); + ret = zend_binary_strcasecmp(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str1)); } else { ret = zend_binary_strcmp(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2)); } @@ -2037,13 +2037,13 @@ ZEND_API int ZEND_FASTCALL string_case_compare_function(zval *op1, zval *op2) /* if (Z_STR_P(op1) == Z_STR_P(op2)) { return 0; } else { - return zend_binary_strcasecmp_l(Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); + return zend_binary_strcasecmp(Z_STRVAL_P(op1), Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); } } else { zend_string *tmp_str1, *tmp_str2; zend_string *str1 = zval_get_tmp_string(op1, &tmp_str1); zend_string *str2 = zval_get_tmp_string(op2, &tmp_str2); - int ret = zend_binary_strcasecmp_l(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str1)); + int ret = zend_binary_strcasecmp(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str1)); zend_tmp_string_release(tmp_str1); zend_tmp_string_release(tmp_str2); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 0236bad20cf21..19cd926eff7be 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -429,25 +429,25 @@ static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size ((chunk >> (8 * 7)) & 0xff); # else hash = - hash * 33 * 33 * 33 * 33 + - str[0] * 33 * 33 * 33 + - str[1] * 33 * 33 + - str[2] * 33 + + hash * Z_L(33 * 33 * 33 * 33) + + str[0] * Z_L(33 * 33 * 33) + + str[1] * Z_L(33 * 33) + + str[2] * Z_L(33) + str[3]; hash = - hash * 33 * 33 * 33 * 33 + - str[4] * 33 * 33 * 33 + - str[5] * 33 * 33 + - str[6] * 33 + + hash * Z_L(33 * 33 * 33 * 33) + + str[4] * Z_L(33 * 33 * 33) + + str[5] * Z_L(33 * 33) + + str[6] * Z_L(33) + str[7]; # endif } if (len >= 4) { hash = - hash * 33 * 33 * 33 * 33 + - str[0] * 33 * 33 * 33 + - str[1] * 33 * 33 + - str[2] * 33 + + hash * Z_L(33 * 33 * 33 * 33) + + str[0] * Z_L(33 * 33 * 33) + + str[1] * Z_L(33 * 33) + + str[2] * Z_L(33) + str[3]; len -= 4; str += 4; @@ -455,18 +455,18 @@ static zend_always_inline zend_ulong zend_inline_hash_func(const char *str, size if (len >= 2) { if (len > 2) { hash = - hash * 33 * 33 * 33 + - str[0] * 33 * 33 + - str[1] * 33 + + hash * Z_L(33 * 33 * 33) + + str[0] * Z_L(33 * 33) + + str[1] * Z_L(33) + str[2]; } else { hash = - hash * 33 * 33 + - str[0] * 33 + + hash * Z_L(33 * 33) + + str[0] * Z_L(33) + str[1]; } } else if (len != 0) { - hash = hash * 33 + *str; + hash = hash * Z_L(33) + *str; } #else /* variant with the hash unrolled eight times */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 46c78e2bd5a1f..f0ad4037eb145 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1150,6 +1150,7 @@ ZEND_VM_HANDLER(27, ZEND_ASSIGN_DIM_OP, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, OP) USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW); @@ -1157,19 +1158,20 @@ ZEND_VM_HANDLER(27, ZEND_ASSIGN_DIM_OP, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, OP) if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { ZEND_VM_C_LABEL(assign_dim_op_array): SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); ZEND_VM_C_LABEL(assign_dim_op_new_array): dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP2_TYPE == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); ZEND_VM_C_GOTO(assign_dim_op_ret_null); } } else { if (OP2_TYPE == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { ZEND_VM_C_GOTO(assign_dim_op_ret_null); @@ -1203,27 +1205,30 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); - if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - ZEND_VM_C_GOTO(assign_dim_op_ret_null); - } - } else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + ZEND_VM_C_GOTO(assign_dim_op_ret_null); + } } - ZVAL_ARR(container, zend_new_array(8)); ZEND_VM_C_GOTO(assign_dim_op_new_array); } else { dim = GET_OP2_ZVAL_PTR(BP_VAR_R); @@ -1832,7 +1837,7 @@ ZEND_VM_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type) SAVE_OPLINE(); - if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { + if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS)); prop = &EG(uninitialized_zval); } @@ -2078,11 +2083,10 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy): if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) { ZEND_VM_C_GOTO(fetch_obj_r_copy); @@ -2161,7 +2165,7 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, FETCH zend_fetch_property_address( result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -2243,11 +2247,10 @@ ZEND_VM_C_LABEL(fetch_obj_is_fast_copy): if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (!ZEND_VM_SPEC || (OP1_TYPE & (IS_TMP_VAR|IS_VAR)) != 0) { ZEND_VM_C_GOTO(fetch_obj_is_copy); @@ -2594,35 +2597,29 @@ ZEND_VM_C_LABEL(try_assign_dim_array): } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP2_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - ZEND_VM_C_GOTO(assign_dim_error); - } } else if (OP2_TYPE == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP_DATA_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - ZEND_VM_C_GOTO(assign_dim_error); - } } else if (OP_DATA_TYPE & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); FREE_OP_DATA(); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (OP2_TYPE == IS_UNUSED) { zend_use_new_element_for_string(); @@ -2635,10 +2632,6 @@ ZEND_VM_C_LABEL(try_assign_dim_array): FREE_OP_DATA(); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -2646,7 +2639,18 @@ ZEND_VM_C_LABEL(try_assign_dim_array): FREE_OP_DATA(); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + ZEND_VM_C_GOTO(assign_dim_error); + } + } ZEND_VM_C_GOTO(try_assign_dim_array); } } else { @@ -2857,18 +2861,24 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { - zend_detach_symbol_table(execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); -#ifdef ZEND_PREFER_RELOAD - call_info = EX_CALL_INFO(); -#endif old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); - zend_attach_symbol_table(execute_data); + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + if (EX(func)->op_array.last_var > 0) { + zend_attach_symbol_table(execute_data); + } else { + ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); HANDLE_EXCEPTION_LEAVE(); @@ -2899,16 +2909,25 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); - zend_detach_symbol_table(execute_data); - old_execute_data = EX(prev_execute_data); - while (old_execute_data) { - if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { - if (old_execute_data->symbol_table == symbol_table) { - zend_attach_symbol_table(old_execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + old_execute_data = EX(prev_execute_data); + while (old_execute_data) { + if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { + if (old_execute_data->symbol_table == symbol_table) { + if (old_execute_data->func->op_array.last_var > 0) { + zend_attach_symbol_table(old_execute_data); + } else { + ZEND_ADD_CALL_FLAG(old_execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } + break; } - break; + old_execute_data = old_execute_data->prev_execute_data; } - old_execute_data = old_execute_data->prev_execute_data; } EG(current_execute_data) = EX(prev_execute_data); ZEND_VM_RETURN(); @@ -3896,9 +3915,7 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT) if (UNEXPECTED(fbc == NULL)) { fname = (zval*)RT_CONSTANT(opline, opline->op2); func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(fname)); - if (UNEXPECTED(func == NULL)) { - ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper); - } + ZEND_ASSERT(func != NULL && "Function existence must be checked at compile time"); fbc = Z_FUNC_P(func); if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); @@ -6275,10 +6292,24 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER if (RETURN_VALUE_USED(opline)) { ZVAL_TRUE(EX_VAR(opline->result.var)); } - } else if (EXPECTED(new_op_array != NULL)) { + } else if (UNEXPECTED(new_op_array == NULL)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + } else if (new_op_array->last == 1 + && new_op_array->opcodes[0].opcode == ZEND_RETURN + && new_op_array->opcodes[0].op1_type == IS_CONST) { + if (RETURN_VALUE_USED(opline)) { + const zend_op *op = new_op_array->opcodes; + + ZVAL_COPY(EX_VAR(opline->result.var), RT_CONSTANT(op, op->op1)); + } + zend_destroy_static_vars(new_op_array); + destroy_op_array(new_op_array); + efree_size(new_op_array, sizeof(zend_op_array)); + } else { zval *return_value = NULL; zend_execute_data *call; - if (RETURN_VALUE_USED(opline)) { return_value = EX_VAR(opline->result.var); } @@ -6317,8 +6348,6 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER UNDEF_RESULT(); HANDLE_EXCEPTION(); } - } else if (RETURN_VALUE_USED(opline)) { - ZVAL_FALSE(EX_VAR(opline->result.var)); } FREE_OP1(); ZEND_VM_NEXT_OPCODE(); @@ -8339,11 +8368,10 @@ ZEND_VM_HOT_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST, CACHE_SLOT) if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == varname) || - (EXPECTED(p->h == ZSTR_H(varname)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, varname))))) { + if (EXPECTED(p->key == varname) || + (EXPECTED(p->h == ZSTR_H(varname)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, varname)))) { value = (zval*)p; /* value = &p->val; */ ZEND_VM_C_GOTO(check_indirect); @@ -9706,7 +9734,10 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_array): if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, OP2_TYPE OPLINE_CC EXECUTE_DATA_CC); + FREE_OP1(); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, ZEND_VM_C_LABEL(fetch_dim_r_index_undef)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9e252360432e9..78dab76a303fb 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -846,7 +846,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static SAVE_OPLINE(); - if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { + if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) { ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS)); prop = &EG(uninitialized_zval); } @@ -1145,18 +1145,24 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { - zend_detach_symbol_table(execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); -#ifdef ZEND_PREFER_RELOAD - call_info = EX_CALL_INFO(); -#endif old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); - zend_attach_symbol_table(execute_data); + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + if (EX(func)->op_array.last_var > 0) { + zend_attach_symbol_table(execute_data); + } else { + ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); HANDLE_EXCEPTION_LEAVE(); @@ -1187,16 +1193,25 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); - zend_detach_symbol_table(execute_data); - old_execute_data = EX(prev_execute_data); - while (old_execute_data) { - if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { - if (old_execute_data->symbol_table == symbol_table) { - zend_attach_symbol_table(old_execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + old_execute_data = EX(prev_execute_data); + while (old_execute_data) { + if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { + if (old_execute_data->symbol_table == symbol_table) { + if (old_execute_data->func->op_array.last_var > 0) { + zend_attach_symbol_table(old_execute_data); + } else { + ZEND_ADD_CALL_FLAG(old_execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } + break; } - break; + old_execute_data = old_execute_data->prev_execute_data; } - old_execute_data = old_execute_data->prev_execute_data; } EG(current_execute_data) = EX(prev_execute_data); ZEND_VM_RETURN(); @@ -3672,9 +3687,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CO if (UNEXPECTED(fbc == NULL)) { fname = (zval*)RT_CONSTANT(opline, opline->op2); func = zend_hash_find_known_hash(EG(function_table), Z_STR_P(fname)); - if (UNEXPECTED(func == NULL)) { - ZEND_VM_TAIL_CALL(zend_undefined_function_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); - } + ZEND_ASSERT(func != NULL && "Function existence must be checked at compile time"); fbc = Z_FUNC_P(func); if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); @@ -4859,10 +4872,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN if (RETURN_VALUE_USED(opline)) { ZVAL_TRUE(EX_VAR(opline->result.var)); } - } else if (EXPECTED(new_op_array != NULL)) { + } else if (UNEXPECTED(new_op_array == NULL)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + } else if (new_op_array->last == 1 + && new_op_array->opcodes[0].opcode == ZEND_RETURN + && new_op_array->opcodes[0].op1_type == IS_CONST) { + if (RETURN_VALUE_USED(opline)) { + const zend_op *op = new_op_array->opcodes; + + ZVAL_COPY(EX_VAR(opline->result.var), RT_CONSTANT(op, op->op1)); + } + zend_destroy_static_vars(new_op_array); + destroy_op_array(new_op_array); + efree_size(new_op_array, sizeof(zend_op_array)); + } else { zval *return_value = NULL; zend_execute_data *call; - if (RETURN_VALUE_USED(opline)) { return_value = EX_VAR(opline->result.var); } @@ -4901,8 +4928,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN UNDEF_RESULT(); HANDLE_EXCEPTION(); } - } else if (RETURN_VALUE_USED(opline)) { - ZVAL_FALSE(EX_VAR(opline->result.var)); } ZEND_VM_NEXT_OPCODE(); @@ -4929,10 +4954,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_ if (RETURN_VALUE_USED(opline)) { ZVAL_TRUE(EX_VAR(opline->result.var)); } - } else if (EXPECTED(new_op_array != NULL)) { + } else if (UNEXPECTED(new_op_array == NULL)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + } else if (new_op_array->last == 1 + && new_op_array->opcodes[0].opcode == ZEND_RETURN + && new_op_array->opcodes[0].op1_type == IS_CONST) { + if (RETURN_VALUE_USED(opline)) { + const zend_op *op = new_op_array->opcodes; + + ZVAL_COPY(EX_VAR(opline->result.var), RT_CONSTANT(op, op->op1)); + } + zend_destroy_static_vars(new_op_array); + destroy_op_array(new_op_array); + efree_size(new_op_array, sizeof(zend_op_array)); + } else { zval *return_value = NULL; zend_execute_data *call; - if (RETURN_VALUE_USED(opline)) { return_value = EX_VAR(opline->result.var); } @@ -4971,8 +5010,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_ UNDEF_RESULT(); HANDLE_EXCEPTION(); } - } else if (RETURN_VALUE_USED(opline)) { - ZVAL_FALSE(EX_VAR(opline->result.var)); } FREE_OP(opline->op1_type, opline->op1.var); ZEND_VM_NEXT_OPCODE(); @@ -6265,11 +6302,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -6392,11 +6428,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -8315,7 +8350,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, (IS_TMP_VAR|IS_VAR|IS_CV) OPLINE_CC EXECUTE_DATA_CC); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, fetch_dim_r_index_undef); @@ -8594,11 +8632,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -8721,11 +8758,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -10961,11 +10997,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_ if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -11088,11 +11123,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CONST & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -14494,10 +14528,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA if (RETURN_VALUE_USED(opline)) { ZVAL_TRUE(EX_VAR(opline->result.var)); } - } else if (EXPECTED(new_op_array != NULL)) { + } else if (UNEXPECTED(new_op_array == NULL)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + } else if (new_op_array->last == 1 + && new_op_array->opcodes[0].opcode == ZEND_RETURN + && new_op_array->opcodes[0].op1_type == IS_CONST) { + if (RETURN_VALUE_USED(opline)) { + const zend_op *op = new_op_array->opcodes; + + ZVAL_COPY(EX_VAR(opline->result.var), RT_CONSTANT(op, op->op1)); + } + zend_destroy_static_vars(new_op_array); + destroy_op_array(new_op_array); + efree_size(new_op_array, sizeof(zend_op_array)); + } else { zval *return_value = NULL; zend_execute_data *call; - if (RETURN_VALUE_USED(opline)) { return_value = EX_VAR(opline->result.var); } @@ -14536,8 +14584,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA UNDEF_RESULT(); HANDLE_EXCEPTION(); } - } else if (RETURN_VALUE_USED(opline)) { - ZVAL_FALSE(EX_VAR(opline->result.var)); } zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE(); @@ -15393,11 +15439,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CONST_ if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -15520,11 +15565,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -16151,7 +16195,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, IS_CONST OPLINE_CC EXECUTE_DATA_CC); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, fetch_dim_r_index_undef); @@ -16203,7 +16250,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, (IS_TMP_VAR|IS_VAR|IS_CV) OPLINE_CC EXECUTE_DATA_CC); + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, fetch_dim_r_index_undef); @@ -16825,11 +16875,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_TMPVAR if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -16952,11 +17001,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVA if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -18152,11 +18200,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMPVAR_CV_HAN if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -18279,11 +18326,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HA if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || ((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -22461,6 +22507,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -22468,19 +22515,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_CONST == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -22514,27 +22562,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = RT_CONSTANT(opline, opline->op2); - if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = RT_CONSTANT(opline, opline->op2); @@ -22791,7 +22842,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CONST_HAN zend_fetch_property_address( result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -23460,34 +23511,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -23500,10 +23545,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -23511,7 +23552,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -23596,35 +23648,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -23637,10 +23683,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -23648,7 +23690,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -23733,35 +23786,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -23774,10 +23821,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -23785,7 +23828,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -23870,34 +23924,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -23910,10 +23958,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -23921,7 +23965,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -25102,6 +25157,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -25109,19 +25165,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -25155,27 +25212,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); @@ -25436,7 +25496,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HA zend_fetch_property_address( result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -26106,34 +26166,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -26146,10 +26200,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -26157,7 +26207,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -26242,35 +26303,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -26283,10 +26338,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -26294,7 +26345,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -26379,35 +26441,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -26420,10 +26476,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -26431,7 +26483,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -26516,34 +26579,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -26556,10 +26613,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -26567,7 +26620,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_OP_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -27381,6 +27445,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -27388,19 +27453,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = NULL; if (IS_UNUSED == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_UNUSED == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -27434,27 +27500,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = NULL; - if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = NULL; @@ -27584,34 +27653,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -27624,10 +27687,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -27635,7 +27694,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -27720,35 +27790,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -27761,10 +27825,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -27772,7 +27832,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -27857,35 +27928,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -27898,10 +27963,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -27909,7 +27970,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -27994,34 +28066,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -28034,10 +28100,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -28045,7 +28107,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_OP_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -29208,6 +29281,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); @@ -29215,19 +29289,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = EX_VAR(opline->op2.var); if (IS_CV == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_CV == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -29261,27 +29336,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = EX_VAR(opline->op2.var); - if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); @@ -29538,7 +29616,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLE zend_fetch_property_address( result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -30207,34 +30285,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -30247,10 +30319,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -30258,7 +30326,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -30343,35 +30422,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -30384,10 +30457,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -30395,7 +30464,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -30480,35 +30560,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -30521,10 +30595,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -30532,7 +30602,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -30617,34 +30698,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -30657,10 +30732,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -30668,7 +30739,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_OP_DATA UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -31894,11 +31976,10 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -31982,7 +32063,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_ zend_fetch_property_address( result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -32064,11 +32145,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -33761,11 +33841,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -33844,7 +33923,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR zend_fetch_property_address( result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -33926,11 +34005,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVA if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -36240,11 +36318,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -36323,7 +36400,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HAN zend_fetch_property_address( result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -36405,11 +36482,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HA if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_UNUSED & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -38594,10 +38670,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE if (RETURN_VALUE_USED(opline)) { ZVAL_TRUE(EX_VAR(opline->result.var)); } - } else if (EXPECTED(new_op_array != NULL)) { + } else if (UNEXPECTED(new_op_array == NULL)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_FALSE(EX_VAR(opline->result.var)); + } + } else if (new_op_array->last == 1 + && new_op_array->opcodes[0].opcode == ZEND_RETURN + && new_op_array->opcodes[0].op1_type == IS_CONST) { + if (RETURN_VALUE_USED(opline)) { + const zend_op *op = new_op_array->opcodes; + + ZVAL_COPY(EX_VAR(opline->result.var), RT_CONSTANT(op, op->op1)); + } + zend_destroy_static_vars(new_op_array); + destroy_op_array(new_op_array); + efree_size(new_op_array, sizeof(zend_op_array)); + } else { zval *return_value = NULL; zend_execute_data *call; - if (RETURN_VALUE_USED(opline)) { return_value = EX_VAR(opline->result.var); } @@ -38636,8 +38726,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE UNDEF_RESULT(); HANDLE_EXCEPTION(); } - } else if (RETURN_VALUE_USED(opline)) { - ZVAL_FALSE(EX_VAR(opline->result.var)); } ZEND_VM_NEXT_OPCODE(); @@ -39963,6 +40051,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); @@ -39970,19 +40059,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_CONST == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -40016,27 +40106,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = RT_CONSTANT(opline, opline->op2); - if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = RT_CONSTANT(opline, opline->op2); @@ -40386,11 +40479,10 @@ static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -40474,7 +40566,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HAND zend_fetch_property_address( result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -40556,11 +40648,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HAN if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -41234,34 +41325,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -41274,10 +41359,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -41285,7 +41366,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -41370,35 +41462,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -41411,10 +41497,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -41422,7 +41504,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -41507,35 +41600,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -41548,10 +41635,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -41559,7 +41642,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -41644,34 +41738,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = RT_CONSTANT(opline, opline->op2); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CONST == IS_UNUSED) { zend_use_new_element_for_string(); @@ -41684,10 +41772,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -41695,7 +41779,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_OP_DA UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -42844,11 +42939,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_GLOBAL_SPEC_C if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == varname) || - (EXPECTED(p->h == ZSTR_H(varname)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, varname))))) { + if (EXPECTED(p->key == varname) || + (EXPECTED(p->h == ZSTR_H(varname)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, varname)))) { value = (zval*)p; /* value = &p->val; */ goto check_indirect; @@ -43022,7 +43116,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, IS_CONST OPLINE_CC EXECUTE_DATA_CC); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, fetch_dim_r_index_undef); @@ -43074,7 +43171,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_ if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { offset = Z_LVAL_P(dim); } else { - offset = zval_get_long_ex(dim, /* is_strict */ true); + SAVE_OPLINE(); + zend_fetch_dimension_address_read_R(container, dim, (IS_TMP_VAR|IS_VAR|IS_CV) OPLINE_CC EXECUTE_DATA_CC); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } ht = Z_ARRVAL_P(container); ZEND_HASH_INDEX_FIND(ht, offset, value, fetch_dim_r_index_undef); @@ -43692,6 +43792,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); @@ -43699,19 +43800,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -43745,27 +43847,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); @@ -44119,11 +44224,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -44202,7 +44306,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HAN zend_fetch_property_address( result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -44284,11 +44388,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HA if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -44962,34 +45065,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -45002,10 +45099,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -45013,7 +45106,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -45098,35 +45202,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -45139,10 +45237,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -45150,7 +45244,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -45235,35 +45340,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -45276,10 +45375,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -45287,7 +45382,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -45372,34 +45478,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if ((IS_TMP_VAR|IS_VAR) == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { zend_use_new_element_for_string(); @@ -45412,10 +45512,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -45423,7 +45519,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -46556,6 +46663,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); @@ -46563,19 +46671,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = NULL; if (IS_UNUSED == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_UNUSED == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -46609,27 +46718,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = NULL; - if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = NULL; @@ -46887,34 +46999,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -46927,10 +47033,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -46938,7 +47040,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -47023,35 +47136,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -47064,10 +47171,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -47075,7 +47178,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -47160,35 +47274,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -47201,10 +47309,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -47212,7 +47316,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -47297,34 +47412,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = NULL; if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_UNUSED == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_UNUSED == IS_UNUSED) { zend_use_new_element_for_string(); @@ -47337,10 +47446,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -47348,7 +47453,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_OP_D UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -48915,6 +49031,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL USE_OPLINE zval *var_ptr; zval *value, *container, *dim; + HashTable *ht; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); @@ -48922,19 +49039,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); assign_dim_op_new_array: dim = EX_VAR(opline->op2.var); if (IS_CV == IS_UNUSED) { - var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); + var_ptr = zend_hash_next_index_insert(ht, &EG(uninitialized_zval)); if (UNEXPECTED(!var_ptr)) { zend_cannot_add_element(); goto assign_dim_op_ret_null; } } else { if (IS_CV == IS_CONST) { - var_ptr = zend_fetch_dimension_address_inner_RW_CONST(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW_CONST(ht, dim EXECUTE_DATA_CC); } else { - var_ptr = zend_fetch_dimension_address_inner_RW(Z_ARRVAL_P(container), dim EXECUTE_DATA_CC); + var_ptr = zend_fetch_dimension_address_inner_RW(ht, dim EXECUTE_DATA_CC); } if (UNEXPECTED(!var_ptr)) { goto assign_dim_op_ret_null; @@ -48968,27 +49086,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL } if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(container); + dim = EX_VAR(opline->op2.var); - if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(container); - GC_ADDREF(obj); - dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_op_ret_null; - } - } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } - zend_binary_assign_op_obj_dim(container, dim OPLINE_CC EXECUTE_DATA_CC); + zend_binary_assign_op_obj_dim(obj, dim OPLINE_CC EXECUTE_DATA_CC); } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + zend_uchar old_type; + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(container) == IS_UNDEF)) { ZVAL_UNDEFINED_OP1(); } - if (Z_TYPE_P(container) == IS_FALSE) { + ht = zend_new_array(8); + old_type = Z_TYPE_P(container); + ZVAL_ARR(container, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_op_ret_null; + } } - ZVAL_ARR(container, zend_new_array(8)); goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); @@ -49338,11 +49459,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_r_copy; @@ -49421,7 +49541,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER zend_fetch_property_address( result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value, 1 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -49503,11 +49623,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLE if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, name))))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { retval = &p->val; if (0 || (IS_CV & (IS_TMP_VAR|IS_VAR)) != 0) { goto fetch_obj_is_copy; @@ -50181,34 +50300,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = RT_CONSTANT((opline+1), (opline+1)->op1); if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -50221,10 +50334,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -50232,7 +50341,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -50317,35 +50437,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_TMP_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -50358,10 +50472,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -50369,7 +50479,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -50454,35 +50575,29 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_VAR & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -50495,10 +50610,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -50506,7 +50617,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -50591,34 +50713,28 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_object *obj = Z_OBJ_P(object_ptr); + + GC_ADDREF(obj); dim = EX_VAR(opline->op2.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(dim))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); dim = ZVAL_UNDEFINED_OP2(); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { dim++; } value = EX_VAR((opline+1)->op1.var); if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(value))) { - zend_object *obj = Z_OBJ_P(object_ptr); - GC_ADDREF(obj); value = zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - goto assign_dim_error; - } } else if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(value); } - zend_assign_to_object_dim(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); + zend_assign_to_object_dim(obj, dim, value OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (IS_CV == IS_UNUSED) { zend_use_new_element_for_string(); @@ -50631,10 +50747,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ } } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); - } - if (Z_ISREF_P(orig_object_ptr) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(orig_object_ptr)) && !zend_verify_ref_array_assignable(Z_REF_P(orig_object_ptr))) { @@ -50642,7 +50754,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_OP_DATA_ UNDEF_RESULT(); } else { - ZVAL_ARR(object_ptr, zend_new_array(8)); + HashTable *ht = zend_new_array(8); + zend_uchar old_type = Z_TYPE_P(object_ptr); + + ZVAL_ARR(object_ptr, ht); + if (UNEXPECTED(old_type == IS_FALSE)) { + GC_ADDREF(ht); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(ht) == 0)) { + zend_array_destroy(ht); + goto assign_dim_error; + } + } goto try_assign_dim_array; } } else { @@ -55313,18 +55436,24 @@ ZEND_API void execute_ex(zend_execute_data *ex) LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { - zend_detach_symbol_table(execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); -#ifdef ZEND_PREFER_RELOAD - call_info = EX_CALL_INFO(); -#endif old_execute_data = execute_data; execute_data = EG(current_execute_data) = EX(prev_execute_data); zend_vm_stack_free_call_frame_ex(call_info, old_execute_data); - zend_attach_symbol_table(execute_data); + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + if (EX(func)->op_array.last_var > 0) { + zend_attach_symbol_table(execute_data); + } else { + ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); HANDLE_EXCEPTION_LEAVE(); @@ -55355,16 +55484,25 @@ ZEND_API void execute_ex(zend_execute_data *ex) } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { zend_array *symbol_table = EX(symbol_table); - zend_detach_symbol_table(execute_data); - old_execute_data = EX(prev_execute_data); - while (old_execute_data) { - if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { - if (old_execute_data->symbol_table == symbol_table) { - zend_attach_symbol_table(old_execute_data); + if (EX(func)->op_array.last_var > 0) { + zend_detach_symbol_table(execute_data); + call_info |= ZEND_CALL_NEEDS_REATTACH; + } + if (call_info & ZEND_CALL_NEEDS_REATTACH) { + old_execute_data = EX(prev_execute_data); + while (old_execute_data) { + if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) { + if (old_execute_data->symbol_table == symbol_table) { + if (old_execute_data->func->op_array.last_var > 0) { + zend_attach_symbol_table(old_execute_data); + } else { + ZEND_ADD_CALL_FLAG(old_execute_data, ZEND_CALL_NEEDS_REATTACH); + } + } + break; } - break; + old_execute_data = old_execute_data->prev_execute_data; } - old_execute_data = old_execute_data->prev_execute_data; } EG(current_execute_data) = EX(prev_execute_data); ZEND_VM_RETURN(); diff --git a/azure/apt.yml b/azure/apt.yml index c3b33050d6482..e30aa1d9d918c 100644 --- a/azure/apt.yml +++ b/azure/apt.yml @@ -11,7 +11,6 @@ steps: openssl \ slapd \ language-pack-de \ - re2c \ libgmp-dev \ libicu-dev \ libtidy-dev \ @@ -44,7 +43,6 @@ steps: unixodbc-dev \ llvm \ libc-client-dev \ - libkrb5-dev \ dovecot-core \ dovecot-pop3d \ dovecot-imapd \ @@ -54,6 +52,9 @@ steps: libtokyocabinet-dev \ libdb-dev \ libqdbm-dev \ + libjpeg-dev \ + libpng-dev \ + libfreetype6-dev \ ${{ parameters.packages }} displayName: 'APT' - script: | diff --git a/build/gen_stub.php b/build/gen_stub.php index 5f74d26dbc37d..8708bf8ff7d06 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -959,6 +959,8 @@ class FuncInfo { /** @var bool */ public $isDeprecated; /** @var bool */ + public $supportsCompileTimeEval; + /** @var bool */ public $verify; /** @var ArgInfo[] */ public $args; @@ -979,6 +981,7 @@ public function __construct( ?string $aliasType, ?FunctionOrMethodName $alias, bool $isDeprecated, + bool $supportsCompileTimeEval, bool $verify, array $args, ReturnInfo $return, @@ -991,6 +994,7 @@ public function __construct( $this->aliasType = $aliasType; $this->alias = $alias; $this->isDeprecated = $isDeprecated; + $this->supportsCompileTimeEval = $supportsCompileTimeEval; $this->verify = $verify; $this->args = $args; $this->return = $return; @@ -1155,6 +1159,10 @@ public function getFunctionEntry(): string { "\tZEND_NS_FE(\"%s\", %s, %s)\n", addslashes($namespace), $declarationName, $this->getArgInfoName()); } else { + if ($this->supportsCompileTimeEval) { + return sprintf( + "\tZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(%s, %s)\n", $declarationName, $this->getArgInfoName()); + } return sprintf("\tZEND_FE(%s, %s)\n", $declarationName, $this->getArgInfoName()); } } else { @@ -2232,6 +2240,7 @@ function parseFunctionLike( $aliasType = null; $alias = null; $isDeprecated = false; + $supportsCompileTimeEval = false; $verify = true; $docReturnType = null; $tentativeReturnType = false; @@ -2267,6 +2276,8 @@ function parseFunctionLike( $docParamTypes[$tag->getVariableName()] = $tag->getType(); } else if ($tag->name === 'refcount') { $refcount = $tag->getValue(); + } else if ($tag->name === 'compile-time-eval') { + $supportsCompileTimeEval = true; } } } @@ -2355,6 +2366,7 @@ function parseFunctionLike( $aliasType, $alias, $isDeprecated, + $supportsCompileTimeEval, $verify, $args, $return, diff --git a/configure.ac b/configure.ac index db1cac4ca6018..6ad00bc22217c 100644 --- a/configure.ac +++ b/configure.ac @@ -587,6 +587,7 @@ inet_ntop \ inet_pton \ localtime_r \ lchown \ +memcntl \ memmove \ mkstemp \ mmap \ diff --git a/ext/ctype/tests/lc_ctype_inheritance.phpt b/ext/ctype/tests/lc_ctype_inheritance.phpt new file mode 100644 index 0000000000000..bcfd4ff463d75 --- /dev/null +++ b/ext/ctype/tests/lc_ctype_inheritance.phpt @@ -0,0 +1,38 @@ +--TEST-- +Do not inherit LC_CTYPE from environment +--SKIPIF-- + +--ENV-- +LC_CTYPE=de_DE +--FILE-- + +--EXPECTF-- +inherited +ctype_lower(\xe4): n +preg_match(\w, \xe4): int(0) +string(%d) "C%r(\.UTF-8)?%r" +ctype_lower(\xe4): n +preg_match(\w, \xe4): int(0) +de_DE +bool(true) +ctype_lower(\xe4): y +preg_match(\w, \xe4): int(1) diff --git a/ext/curl/sync-constants.php b/ext/curl/sync-constants.php index 2db773d5f80a1..035dbcf6a237d 100755 --- a/ext/curl/sync-constants.php +++ b/ext/curl/sync-constants.php @@ -12,7 +12,7 @@ const SOURCE_FILE = __DIR__ . '/interface.c'; -const MIN_SUPPORTED_CURL_VERSION = '7.15.5'; +const MIN_SUPPORTED_CURL_VERSION = '7.29.0'; const IGNORED_CONSTANTS = [ 'CURLOPT_PROGRESSDATA' diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 1a1c2f0a4a2fe..108e8e3259c78 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -937,7 +937,7 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || (ip[0] == 0x5f) || (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) - || ((ip[0] == 0x2001 && ip[1] == 0x0db8) || (ip[1] >= 0x0010 && ip[1] <= 0x001f)) + || (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) || (ip[0] == 0x3ff3) ) { RETURN_VALIDATION_FAILED diff --git a/ext/filter/tests/bug47435.phpt b/ext/filter/tests/bug47435.phpt index 1be5c96dba152..b035bc0a5f69f 100644 --- a/ext/filter/tests/bug47435.phpt +++ b/ext/filter/tests/bug47435.phpt @@ -14,6 +14,10 @@ var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); +var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); +var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); +var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); +var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("3ff3::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); @@ -30,6 +34,10 @@ string(11) "fe80:5:6::1" bool(false) string(12) "2001:0db8::1" bool(false) +string(12) "2001:0010::1" +bool(false) +string(12) "240b:0010::1" +string(12) "240b:0010::1" string(5) "5f::1" bool(false) string(7) "3ff3::1" diff --git a/ext/hash/hash.stub.php b/ext/hash/hash.stub.php index cbe0ed555966c..ce439d620fdda 100644 --- a/ext/hash/hash.stub.php +++ b/ext/hash/hash.stub.php @@ -3,13 +3,13 @@ /** @generate-class-entries */ /** @refcount 1 */ -function hash(string $algo, string $data, bool $binary = false, array $options = []): string|false {} +function hash(string $algo, string $data, bool $binary = false, array $options = []): string {} /** @refcount 1 */ function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): string|false {} /** @refcount 1 */ -function hash_hmac(string $algo, string $data, string $key, bool $binary = false): string|false {} +function hash_hmac(string $algo, string $data, string $key, bool $binary = false): string {} /** @refcount 1 */ function hash_hmac_file(string $algo, string $data, string $key, bool $binary = false): string|false {} diff --git a/ext/hash/hash_arginfo.h b/ext/hash/hash_arginfo.h index 0b9d151b31051..5a8758bfa2d44 100644 --- a/ext/hash/hash_arginfo.h +++ b/ext/hash/hash_arginfo.h @@ -1,7 +1,7 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d0ed07f608581a5700a10ec53a817ca788ef9638 */ + * Stub hash: 715957cf1785912eed75ce92c2a2708defe1ebd7 */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_hash, 0, 2, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false") @@ -15,14 +15,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash_file, 0, 2, MAY_BE_STRING|M ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash_hmac, 0, 3, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_hash_hmac, 0, 3, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -#define arginfo_hash_hmac_file arginfo_hash_hmac +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash_hmac_file, 0, 3, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_hash_init, 0, 1, HashContext, 0) ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0) diff --git a/ext/mbstring/libmbfl/filters/mbfilter_7bit.c b/ext/mbstring/libmbfl/filters/mbfilter_7bit.c index dbf8993128472..0a6fe78957084 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_7bit.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_7bit.c @@ -39,6 +39,8 @@ const mbfl_encoding mbfl_encoding_7bit = { NULL, MBFL_ENCTYPE_SBCS, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/filters/mbfilter_base64.c b/ext/mbstring/libmbfl/filters/mbfilter_base64.c index 04161c94ab3bc..86700c84990da 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_base64.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_base64.c @@ -39,6 +39,8 @@ const mbfl_encoding mbfl_encoding_base64 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/filters/mbfilter_big5.c b/ext/mbstring/libmbfl/filters/mbfilter_big5.c index 561ab8c225ae0..2ba32ee04563d 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_big5.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_big5.c @@ -63,7 +63,9 @@ const mbfl_encoding mbfl_encoding_big5 = { mblen_table_big5, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_big5_wchar, - &vtbl_wchar_big5 + &vtbl_wchar_big5, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_cp950 = { @@ -74,7 +76,9 @@ const mbfl_encoding mbfl_encoding_cp950 = { mblen_table_big5, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp950_wchar, - &vtbl_wchar_cp950 + &vtbl_wchar_cp950, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_big5_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp5022x.c b/ext/mbstring/libmbfl/filters/mbfilter_cp5022x.c index deb6e8ee0204f..a67b538bb4311 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp5022x.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp5022x.c @@ -55,7 +55,9 @@ const mbfl_encoding mbfl_encoding_cp50220 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp50220_wchar, - &vtbl_wchar_cp50220 + &vtbl_wchar_cp50220, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_cp50221 = { @@ -66,7 +68,9 @@ const mbfl_encoding mbfl_encoding_cp50221 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp50221_wchar, - &vtbl_wchar_cp50221 + &vtbl_wchar_cp50221, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_cp50222 = { @@ -77,7 +81,9 @@ const mbfl_encoding mbfl_encoding_cp50222 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp50222_wchar, - &vtbl_wchar_cp50222 + &vtbl_wchar_cp50222, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_cp50220_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp51932.c b/ext/mbstring/libmbfl/filters/mbfilter_cp51932.c index c0069a93fb98b..2f7193462e025 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp51932.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp51932.c @@ -65,7 +65,9 @@ const mbfl_encoding mbfl_encoding_cp51932 = { mblen_table_eucjp, 0, &vtbl_cp51932_wchar, - &vtbl_wchar_cp51932 + &vtbl_wchar_cp51932, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_cp51932_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp932.c b/ext/mbstring/libmbfl/filters/mbfilter_cp932.c index 9c24fa64494f2..06ac6b22b26f2 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp932.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp932.c @@ -64,7 +64,9 @@ const mbfl_encoding mbfl_encoding_cp932 = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp932_wchar, - &vtbl_wchar_cp932 + &vtbl_wchar_cp932, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_cp932_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp936.c b/ext/mbstring/libmbfl/filters/mbfilter_cp936.c index 22a759ee79ff4..f12523dedca2a 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp936.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp936.c @@ -63,7 +63,9 @@ const mbfl_encoding mbfl_encoding_cp936 = { mblen_table_cp936, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_cp936_wchar, - &vtbl_wchar_cp936 + &vtbl_wchar_cp936, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_cp936_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_cn.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_cn.c index 01c48e433eedd..0465a814ff3d8 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_cn.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_cn.c @@ -63,7 +63,9 @@ const mbfl_encoding mbfl_encoding_euc_cn = { mblen_table_euccn, 0, &vtbl_euccn_wchar, - &vtbl_wchar_euccn + &vtbl_wchar_euccn, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_euccn_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp.c index 7dd095862ec87..d83c47bd67006 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp.c @@ -34,6 +34,8 @@ #include "unicode_table_jis.h" static int mbfl_filt_conv_eucjp_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_eucjp_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_eucjp(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); const unsigned char mblen_table_eucjp[] = { /* 0xA1-0xFE */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -64,7 +66,9 @@ const mbfl_encoding mbfl_encoding_euc_jp = { mblen_table_eucjp, 0, &vtbl_eucjp_wchar, - &vtbl_wchar_eucjp + &vtbl_wchar_eucjp, + mb_eucjp_to_wchar, + mb_wchar_to_eucjp }; const struct mbfl_convert_vtbl vtbl_eucjp_wchar = { @@ -243,3 +247,125 @@ mbfl_filt_conv_wchar_eucjp(int c, mbfl_convert_filter *filter) return 0; } + +static size_t mb_eucjp_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + + if (c < 0x80) { + *out++ = c; + } else if (c >= 0xA1 && c <= 0xFE && p < e) { + /* JISX 0208 */ + unsigned char c2 = *p++; + if (c2 >= 0xA1 && c2 <= 0xFE) { + unsigned int s = (c - 0xA1)*94 + c2 - 0xA1; + if (s < jisx0208_ucs_table_size) { + uint32_t w = jisx0208_ucs_table[s]; + if (!w) + w = MBFL_BAD_INPUT; + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else if (c == 0x8E && p < e) { + /* Kana */ + unsigned char c2 = *p++; + *out++ = (c2 >= 0xA1 && c2 <= 0xDF) ? 0xFEC0 + c2 : MBFL_BAD_INPUT; + } else if (c == 0x8F) { + /* JISX 0212 */ + if ((e - p) >= 2) { + unsigned char c2 = *p++; + unsigned char c3 = *p++; + if (c3 >= 0xA1 && c3 <= 0xFE && c2 >= 0xA1 && c2 <= 0xFE) { + unsigned int s = (c2 - 0xA1)*94 + c3 - 0xA1; + if (s < jisx0212_ucs_table_size) { + uint32_t w = jisx0212_ucs_table[s]; + if (!w) + w = MBFL_BAD_INPUT; + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else { + *out++ = MBFL_BAD_INPUT; + p = e; /* Jump to end of string */ + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_eucjp(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + unsigned int s = 0; + + if (w == 0xAF) { /* U+00AF is MACRON */ + s = 0xA2B4; /* Use JIS X 0212 overline */ + } else if (w >= ucs_a1_jis_table_min && w < ucs_a1_jis_table_max) { + s = ucs_a1_jis_table[w - ucs_a1_jis_table_min]; + } else if (w >= ucs_a2_jis_table_min && w < ucs_a2_jis_table_max) { + s = ucs_a2_jis_table[w - ucs_a2_jis_table_min]; + } else if (w >= ucs_i_jis_table_min && w < ucs_i_jis_table_max) { + s = ucs_i_jis_table[w - ucs_i_jis_table_min]; + } else if (w >= ucs_r_jis_table_min && w < ucs_r_jis_table_max) { + s = ucs_r_jis_table[w - ucs_r_jis_table_min]; + } + + if (s == 0) { + if (w == 0xFF3C) { /* FULLWIDTH REVERSE SOLIDUS */ + s = 0x2140; + } else if (w == 0x2225) { /* PARALLEL TO */ + s = 0x2142; + } else if (w == 0xFF0D) { /* FULLWIDTH HYPHEN-MINUS */ + s = 0x215D; + } else if (w == 0xFFE0) { /* FULLWIDTH CENT SIGN */ + s = 0x2171; + } else if (w == 0xFFE1) { /* FULLWIDTH POUND SIGN */ + s = 0x2172; + } else if (w == 0xFFE2) { /* FULLWIDTH NOT SIGN */ + s = 0x224C; + } else if (w == 0) { + out = mb_convert_buf_add(out, 0); + continue; + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_eucjp); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + continue; + } + } + + if (s < 0x80) { + out = mb_convert_buf_add(out, s); + } else if (s < 0x100) { + out = mb_convert_buf_add2(out, 0x8E, s); + } else if (s < 0x8080) { + out = mb_convert_buf_add2(out, ((s >> 8) & 0xFF) | 0x80, (s & 0xFF) | 0x80); + } else { + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 3); + out = mb_convert_buf_add3(out, 0x8F, ((s >> 8) & 0xFF) | 0x80, (s & 0xFF) | 0x80); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_2004.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_2004.c index 0709a9f12d075..e4f919304e2b8 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_2004.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_2004.c @@ -43,7 +43,9 @@ const mbfl_encoding mbfl_encoding_eucjp2004 = { mblen_table_eucjp, 0, &vtbl_eucjp2004_wchar, - &vtbl_wchar_eucjp2004 + &vtbl_wchar_eucjp2004, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_eucjp2004_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_win.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_win.c index 1209e63148f75..49e7f9dccd279 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_win.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_jp_win.c @@ -65,7 +65,9 @@ const mbfl_encoding mbfl_encoding_eucjp_win = { mblen_table_eucjp, 0, &vtbl_eucjpwin_wchar, - &vtbl_wchar_eucjpwin + &vtbl_wchar_eucjpwin, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_eucjpwin_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_kr.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_kr.c index 8362138d15a13..70c4e3f9d5949 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_kr.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_kr.c @@ -62,7 +62,9 @@ const mbfl_encoding mbfl_encoding_euc_kr = { mblen_table_euckr, 0, &vtbl_euckr_wchar, - &vtbl_wchar_euckr + &vtbl_wchar_euckr, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_euckr_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_euc_tw.c b/ext/mbstring/libmbfl/filters/mbfilter_euc_tw.c index e2c3bceaa509a..f505ca17a3923 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_euc_tw.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_euc_tw.c @@ -64,7 +64,9 @@ const mbfl_encoding mbfl_encoding_euc_tw = { mblen_table_euctw, 0, &vtbl_euctw_wchar, - &vtbl_wchar_euctw + &vtbl_wchar_euctw, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_euctw_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_gb18030.c b/ext/mbstring/libmbfl/filters/mbfilter_gb18030.c index 4a8939523a5bf..00494daf358f0 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_gb18030.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_gb18030.c @@ -45,7 +45,9 @@ const mbfl_encoding mbfl_encoding_gb18030 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_gb18030_wchar, - &vtbl_wchar_gb18030 + &vtbl_wchar_gb18030, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_gb18030_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_htmlent.c b/ext/mbstring/libmbfl/filters/mbfilter_htmlent.c index 3a7e879ceaab4..bf6a0ec2cc479 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_htmlent.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_htmlent.c @@ -61,7 +61,9 @@ const mbfl_encoding mbfl_encoding_html_ent = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_html_wchar, - &vtbl_wchar_html + &vtbl_wchar_html, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_wchar_html = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_hz.c b/ext/mbstring/libmbfl/filters/mbfilter_hz.c index 722d73dd40c64..3370cd93ef88b 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_hz.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_hz.c @@ -43,7 +43,9 @@ const mbfl_encoding mbfl_encoding_hz = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_hz_wchar, - &vtbl_wchar_hz + &vtbl_wchar_hz, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_hz_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_iso2022_jp_ms.c b/ext/mbstring/libmbfl/filters/mbfilter_iso2022_jp_ms.c index 41d4088c475e8..f26cbbeb8aa97 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_iso2022_jp_ms.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_iso2022_jp_ms.c @@ -46,7 +46,9 @@ const mbfl_encoding mbfl_encoding_2022jpms = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_2022jpms_wchar, - &vtbl_wchar_2022jpms + &vtbl_wchar_2022jpms, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_2022jpms_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_iso2022_kr.c b/ext/mbstring/libmbfl/filters/mbfilter_iso2022_kr.c index f012656b1974b..cbb32bd635e3f 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_iso2022_kr.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_iso2022_kr.c @@ -47,7 +47,9 @@ const mbfl_encoding mbfl_encoding_2022kr = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_2022kr_wchar, - &vtbl_wchar_2022kr + &vtbl_wchar_2022kr, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_wchar_2022kr = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_2004.c b/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_2004.c index c54941f23dcbb..45b71a8a98bde 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_2004.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_2004.c @@ -43,7 +43,9 @@ const mbfl_encoding mbfl_encoding_2022jp_2004 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_2022jp_2004_wchar, - &vtbl_wchar_2022jp_2004 + &vtbl_wchar_2022jp_2004, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_2022jp_2004_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_mobile.c b/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_mobile.c index 3d289f1191595..450ac0c932f8c 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_mobile.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_iso2022jp_mobile.c @@ -48,7 +48,9 @@ const mbfl_encoding mbfl_encoding_2022jp_kddi = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_2022jp_kddi_wchar, - &vtbl_wchar_2022jp_kddi + &vtbl_wchar_2022jp_kddi, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_2022jp_kddi_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_jis.c b/ext/mbstring/libmbfl/filters/mbfilter_jis.c index 9de18964ccef2..b175743a4afbe 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_jis.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_jis.c @@ -34,6 +34,8 @@ #include "unicode_table_jis.h" static int mbfl_filt_conv_jis_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_iso2022jp_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_iso2022jp(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); const mbfl_encoding mbfl_encoding_jis = { mbfl_no_encoding_jis, @@ -43,7 +45,9 @@ const mbfl_encoding mbfl_encoding_jis = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_jis_wchar, - &vtbl_wchar_jis + &vtbl_wchar_jis, + mb_iso2022jp_to_wchar, + NULL }; const mbfl_encoding mbfl_encoding_2022jp = { @@ -54,7 +58,9 @@ const mbfl_encoding mbfl_encoding_2022jp = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_2022jp_wchar, - &vtbl_wchar_2022jp + &vtbl_wchar_2022jp, + mb_iso2022jp_to_wchar, + mb_wchar_to_iso2022jp }; const struct mbfl_convert_vtbl vtbl_jis_wchar = { @@ -446,3 +452,218 @@ mbfl_filt_conv_any_jis_flush(mbfl_convert_filter *filter) return 0; } + +#define ASCII 0 +#define JISX_0201_LATIN 1 +#define JISX_0201_KANA 2 +#define JISX_0208 3 +#define JISX_0212 4 + +static size_t mb_iso2022jp_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + + if (c == 0x1B) { + /* ESC seen; this is an escape sequence */ + if ((e - p) < 2) { + *out++ = MBFL_BAD_INPUT; + continue; + } + + unsigned char c2 = *p++; + if (c2 == '$') { + unsigned char c3 = *p++; + if (c3 == '@' || c3 == 'B') { + *state = JISX_0208; + } else if (c3 == '(') { + if (p == e) { + *out++ = MBFL_BAD_INPUT; + break; + } + unsigned char c4 = *p++; + if (c4 == '@' || c4 == 'B') { + *state = JISX_0208; + } else if (c4 == 'D') { + *state = JISX_0212; + } else { + if ((limit - out) < 3) { + p -= 4; + break; + } + *out++ = MBFL_BAD_INPUT; + *out++ = '$'; + *out++ = '('; + p--; + } + } else { + if ((limit - out) < 2) { + p -= 3; + break; + } + *out++ = MBFL_BAD_INPUT; + *out++ = '$'; + p--; + } + } else if (c2 == '(') { + unsigned char c3 = *p++; + if (c3 == 'B' || c3 == 'H') { + *state = ASCII; + } else if (c3 == 'J') { + *state = JISX_0201_LATIN; + } else if (c3 == 'I') { + *state = JISX_0201_KANA; + } else { + if ((limit - out) < 2) { + p -= 3; + break; + } + *out++ = MBFL_BAD_INPUT; + *out++ = '('; + p--; + } + } else { + *out++ = MBFL_BAD_INPUT; + p--; + } + } else if (c == 0xE) { + /* "Kana In" marker; this is just for JIS-7/8, but we also accept it for ISO-2022-JP */ + *state = JISX_0201_KANA; + } else if (c == 0xF) { + /* "Kana Out" marker */ + *state = ASCII; + } else if (*state == JISX_0201_LATIN && c == 0x5C) { /* YEN SIGN */ + *out++ = 0xA5; + } else if (*state == JISX_0201_LATIN && c == 0x7E) { /* OVER LINE */ + *out++ = 0x203E; + } else if (*state == JISX_0201_KANA && c > 0x20 && c < 0x60) { + *out++ = 0xFF40 + c; + } else if (*state >= JISX_0208 && c > 0x20 && c < 0x7F) { + if (p == e) { + *out++ = MBFL_BAD_INPUT; + break; + } + unsigned char c2 = *p++; + if (c2 > 0x20 && c2 < 0x7F) { + unsigned int s = (c - 0x21)*94 + c2 - 0x21; + uint32_t w = 0; + if (*state == JISX_0208) { + if (s < jisx0208_ucs_table_size) { + w = jisx0208_ucs_table[s]; + } + if (!w) { + w = MBFL_BAD_INPUT; + } + } else { + if (s < jisx0212_ucs_table_size) { + w = jisx0212_ucs_table[s]; + } + if (!w) { + w = MBFL_BAD_INPUT; + } + } + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } else if (c < 0x80) { + *out++ = c; + } else if (c >= 0xA1 && c <= 0xDF) { + *out++ = 0xFEC0 + c; + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_iso2022jp(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + unsigned int s = 0; + + if (w >= ucs_a1_jis_table_min && w < ucs_a1_jis_table_max) { + s = ucs_a1_jis_table[w - ucs_a1_jis_table_min]; + } else if (w >= ucs_a2_jis_table_min && w < ucs_a2_jis_table_max) { + s = ucs_a2_jis_table[w - ucs_a2_jis_table_min]; + } else if (w >= ucs_i_jis_table_min && w < ucs_i_jis_table_max) { + s = ucs_i_jis_table[w - ucs_i_jis_table_min]; + } else if (w >= ucs_r_jis_table_min && w < ucs_r_jis_table_max) { + s = ucs_r_jis_table[w - ucs_r_jis_table_min]; + } + + if (s == 0) { + if (w == 0xA5) { /* YEN SIGN */ + s = 0x1005C; + } else if (w == 0xFF3C) { /* FULLWIDTH REVERSE SOLIDUS */ + s = 0x2140; + } else if (w == 0x2225) { /* PARALLEL TO */ + s = 0x2142; + } else if (w == 0xFF0D) { /* FULLWIDTH HYPHEN-MINUS */ + s = 0x215D; + } else if (w == 0xFFE0) { /* FULLWIDTH CENT SIGN */ + s = 0x2171; + } else if (w == 0xFFE1) { /* FULLWIDTH POUND SIGN */ + s = 0x2172; + } else if (w == 0xFFE2) { /* FULLWIDTH NOT SIGN */ + s = 0x224C; + } else if (w != 0) { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_iso2022jp); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + continue; + } + } else if ((s >= 0x80 && s < 0x2121) || (s > 0x8080)) { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_iso2022jp); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + continue; + } + + if (s < 0x80) { /* ASCII */ + if (buf->state != ASCII) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 4); + out = mb_convert_buf_add3(out, 0x1B, '(', 'B'); + buf->state = ASCII; + } + out = mb_convert_buf_add(out, s); + } else if (s < 0x8080) { /* JIS X 0208 */ + if (buf->state != JISX_0208) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 5); + out = mb_convert_buf_add3(out, 0x1B, '$', 'B'); + buf->state = JISX_0208; + } + out = mb_convert_buf_add2(out, (s >> 8) & 0x7F, s & 0x7F); + } else if (s < 0x10000) { /* JIS X 0212 */ + if (buf->state != JISX_0212) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 6); + out = mb_convert_buf_add4(out, 0x1B, '$', '(', 'D'); + buf->state = JISX_0212; + } + out = mb_convert_buf_add2(out, (s >> 8) & 0x7F, s & 0x7F); + } else { /* X 0201 Latin */ + if (buf->state != JISX_0201_LATIN) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 4); + out = mb_convert_buf_add3(out, 0x1B, '(', 'J'); + buf->state = JISX_0201_LATIN; + } + out = mb_convert_buf_add(out, s & 0x7F); + } + } + + if (end && buf->state != ASCII) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, 3); + out = mb_convert_buf_add3(out, 0x1B, '(', 'B'); + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_qprint.c b/ext/mbstring/libmbfl/filters/mbfilter_qprint.c index 2c9e82348726d..8f3aa16b9c579 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_qprint.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_qprint.c @@ -41,6 +41,8 @@ const mbfl_encoding mbfl_encoding_qprint = { NULL, MBFL_ENCTYPE_GL_UNSAFE, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/filters/mbfilter_singlebyte.c b/ext/mbstring/libmbfl/filters/mbfilter_singlebyte.c index 43ab8a2e48718..2020bc3ab09d0 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_singlebyte.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_singlebyte.c @@ -16,16 +16,18 @@ #define CK(statement) do { if ((statement) < 0) return (-1); } while (0) +static inline uint32_t coalesce(uint32_t a, uint32_t b) +{ + return a ? a : b; +} + /* Helper for single-byte encodings which use a conversion table */ static int mbfl_conv_singlebyte_table(int c, mbfl_convert_filter *filter, int tbl_min, const unsigned short tbl[]) { if (c < tbl_min) { CK((*filter->output_function)(c, filter->data)); } else { - int s = tbl[c - tbl_min]; - if (!s) - s = MBFL_BAD_INPUT; - CK((*filter->output_function)(s, filter->data)); + CK((*filter->output_function)(coalesce(tbl[c - tbl_min], MBFL_BAD_INPUT), filter->data)); } return 0; } @@ -52,6 +54,8 @@ static int mbfl_conv_reverselookup_table(int c, mbfl_convert_filter *filter, int #define DEF_SB(id, name, mime_name, aliases) \ static int mbfl_filt_conv_##id##_wchar(int c, mbfl_convert_filter *filter); \ static int mbfl_filt_conv_wchar_##id(int c, mbfl_convert_filter *filter); \ + static size_t mb_##id##_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); \ + static void mb_wchar_to_##id(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); \ static const struct mbfl_convert_vtbl vtbl_##id##_wchar = { \ mbfl_no_encoding_##id, \ mbfl_no_encoding_wchar, \ @@ -78,7 +82,9 @@ static int mbfl_conv_reverselookup_table(int c, mbfl_convert_filter *filter, int NULL, \ MBFL_ENCTYPE_SBCS, \ &vtbl_##id##_wchar, \ - &vtbl_wchar_##id \ + &vtbl_wchar_##id, \ + mb_##id##_to_wchar, \ + mb_wchar_to_##id \ } /* For single-byte encodings which use a conversion table */ @@ -89,6 +95,41 @@ static int mbfl_conv_reverselookup_table(int c, mbfl_convert_filter *filter, int static int mbfl_filt_conv_wchar_##id(int c, mbfl_convert_filter *filter) { \ return mbfl_conv_reverselookup_table(c, filter, tbl_min, tbl); \ } \ + static size_t mb_##id##_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) \ + { \ + unsigned char *p = *in, *e = p + *in_len; \ + uint32_t *out = buf, *limit = buf + bufsize; \ + while (p < e && out < limit) { \ + unsigned char c = *p++; \ + *out++ = (c < tbl_min) ? c : coalesce(tbl[c - tbl_min], MBFL_BAD_INPUT); \ + } \ + *in_len = e - p; \ + *in = p; \ + return out - buf; \ + } \ + static void mb_wchar_to_##id(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) \ + { \ + unsigned char *out, *limit; \ + MB_CONVERT_BUF_LOAD(buf, out, limit); \ + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); \ + while (len--) { \ + uint32_t w = *in++; \ + if (w < tbl_min) { \ + out = mb_convert_buf_add(out, w & 0xFF); \ + } else { \ + for (int i = 0; i < 256 - tbl_min; i++) { \ + if (w == tbl[i]) { \ + out = mb_convert_buf_add(out, i + tbl_min); \ + goto next_iteration; \ + } \ + } \ + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_##id); \ + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); \ + next_iteration: ; \ + } \ + } \ + MB_CONVERT_BUF_STORE(buf, out, limit); \ + } \ DEF_SB(id, name, mime_name, aliases) /* The grand-daddy of them all: ASCII */ @@ -97,11 +138,7 @@ DEF_SB(ascii, "ASCII", "US-ASCII", ascii_aliases); static int mbfl_filt_conv_ascii_wchar(int c, mbfl_convert_filter *filter) { - if (c < 0x80) { - CK((*filter->output_function)(c, filter->data)); - } else { - CK((*filter->output_function)(MBFL_BAD_INPUT, filter->data)); - } + CK((*filter->output_function)((c < 0x80) ? c : MBFL_BAD_INPUT, filter->data)); return 0; } @@ -115,6 +152,40 @@ static int mbfl_filt_conv_wchar_ascii(int c, mbfl_convert_filter *filter) return 0; } +static size_t mb_ascii_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + *out++ = (c < 0x80) ? c : MBFL_BAD_INPUT; + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_ascii(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + while (len--) { + uint32_t w = *in++; + if (w < 0x80) { + out = mb_convert_buf_add(out, w & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_ascii); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + /* ISO-8859-X */ static const char *iso8859_1_aliases[] = {"ISO8859-1", "latin1", NULL}; @@ -135,260 +206,293 @@ static int mbfl_filt_conv_wchar_8859_1(int c, mbfl_convert_filter *filter) return 0; } +static size_t mb_8859_1_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + *out++ = *p++; + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_8859_1(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + while (len--) { + uint32_t w = *in++; + if (w < 0x100) { + out = mb_convert_buf_add(out, w); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_8859_1); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + static const char *iso8859_2_aliases[] = {"ISO8859-2", "latin2", NULL}; static const unsigned short iso8859_2_ucs_table[] = { - 0X00A0, 0X0104, 0X02D8, 0X0141, 0X00A4, 0X013D, 0X015A, 0X00A7, - 0X00A8, 0X0160, 0X015E, 0X0164, 0X0179, 0X00AD, 0X017D, 0X017B, - 0X00B0, 0X0105, 0X02DB, 0X0142, 0X00B4, 0X013E, 0X015B, 0X02C7, - 0X00B8, 0X0161, 0X015F, 0X0165, 0X017A, 0X02DD, 0X017E, 0X017C, - 0X0154, 0X00C1, 0X00C2, 0X0102, 0X00C4, 0X0139, 0X0106, 0X00C7, - 0X010C, 0X00C9, 0X0118, 0X00CB, 0X011A, 0X00CD, 0X00CE, 0X010E, - 0X0110, 0X0143, 0X0147, 0X00D3, 0X00D4, 0X0150, 0X00D6, 0X00D7, - 0X0158, 0X016E, 0X00DA, 0X0170, 0X00DC, 0X00DD, 0X0162, 0X00DF, - 0X0155, 0X00E1, 0X00E2, 0X0103, 0X00E4, 0X013A, 0X0107, 0X00E7, - 0X010D, 0X00E9, 0X0119, 0X00EB, 0X011B, 0X00ED, 0X00EE, 0X010F, - 0X0111, 0X0144, 0X0148, 0X00F3, 0X00F4, 0X0151, 0X00F6, 0X00F7, - 0X0159, 0X016F, 0X00FA, 0X0171, 0X00FC, 0X00FD, 0X0163, 0X02D9 + 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, + 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, + 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, + 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; DEF_SB_TBL(8859_2, "ISO-8859-2", "ISO-8859-2", iso8859_2_aliases, 0xA0, iso8859_2_ucs_table); static const char *iso8859_3_aliases[] = {"ISO8859-3", "latin3", NULL}; static const unsigned short iso8859_3_ucs_table[] = { - 0X00A0, 0X0126, 0X02D8, 0X00A3, 0X00A4, 0X0000, 0X0124, 0X00A7, - 0X00A8, 0X0130, 0X015E, 0X011E, 0X0134, 0X00AD, 0X0000, 0X017B, - 0X00B0, 0X0127, 0X00B2, 0X00B3, 0X00B4, 0X00B5, 0X0125, 0X00B7, - 0X00B8, 0X0131, 0X015F, 0X011F, 0X0135, 0X00BD, 0X0000, 0X017C, - 0X00C0, 0X00C1, 0X00C2, 0X0000, 0X00C4, 0X010A, 0X0108, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X0000, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X0120, 0X00D6, 0X00D7, - 0X011C, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X016C, 0X015C, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X0000, 0X00E4, 0X010B, 0X0109, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X0000, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X0121, 0X00F6, 0X00F7, - 0X011D, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X016D, 0X015D, 0X02D9 + 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0x0000, 0x0124, 0x00A7, + 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0x0000, 0x017B, + 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, + 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x0000, 0x017C, + 0x00C0, 0x00C1, 0x00C2, 0x0000, 0x00C4, 0x010A, 0x0108, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, + 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0000, 0x00E4, 0x010B, 0x0109, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x0000, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, + 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 }; DEF_SB_TBL(8859_3, "ISO-8859-3", "ISO-8859-3", iso8859_3_aliases, 0xA0, iso8859_3_ucs_table); static const char *iso8859_4_aliases[] = {"ISO8859-4", "latin4", NULL}; static const unsigned short iso8859_4_ucs_table[] = { - 0X00A0, 0X0104, 0X0138, 0X0156, 0X00A4, 0X0128, 0X013B, 0X00A7, - 0X00A8, 0X0160, 0X0112, 0X0122, 0X0166, 0X00AD, 0X017D, 0X00AF, - 0X00B0, 0X0105, 0X02DB, 0X0157, 0X00B4, 0X0129, 0X013C, 0X02C7, - 0X00B8, 0X0161, 0X0113, 0X0123, 0X0167, 0X014A, 0X017E, 0X014B, - 0X0100, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X012E, - 0X010C, 0X00C9, 0X0118, 0X00CB, 0X0116, 0X00CD, 0X00CE, 0X012A, - 0X0110, 0X0145, 0X014C, 0X0136, 0X00D4, 0X00D5, 0X00D6, 0X00D7, - 0X00D8, 0X0172, 0X00DA, 0X00DB, 0X00DC, 0X0168, 0X016A, 0X00DF, - 0X0101, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X012F, - 0X010D, 0X00E9, 0X0119, 0X00EB, 0X0117, 0X00ED, 0X00EE, 0X012B, - 0X0111, 0X0146, 0X014D, 0X0137, 0X00F4, 0X00F5, 0X00F6, 0X00F7, - 0X00F8, 0X0173, 0X00FA, 0X00FB, 0X00FC, 0X0169, 0X016B, 0X02D9 + 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, + 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, + 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, + 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, + 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, + 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, + 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, + 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 }; DEF_SB_TBL(8859_4, "ISO-8859-4", "ISO-8859-4", iso8859_4_aliases, 0xA0, iso8859_4_ucs_table); static const char *iso8859_5_aliases[] = {"ISO8859-5", "cyrillic", NULL}; static const unsigned short iso8859_5_ucs_table[] = { - 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, + 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f + 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F }; DEF_SB_TBL(8859_5, "ISO-8859-5", "ISO-8859-5", iso8859_5_aliases, 0xA0, iso8859_5_ucs_table); static const char *iso8859_6_aliases[] = {"ISO8859-6", "arabic", NULL}; static const unsigned short iso8859_6_ucs_table[] = { - 0X00A0, 0X0000, 0X0000, 0X0000, 0X00A4, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X060C, 0X00AD, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X061B, 0X0000, 0X0000, 0X0000, 0X061F, - 0X0000, 0X0621, 0X0622, 0X0623, 0X0624, 0X0625, 0X0626, 0X0627, - 0X0628, 0X0629, 0X062A, 0X062B, 0X062C, 0X062D, 0X062E, 0X062F, - 0X0630, 0X0631, 0X0632, 0X0633, 0X0634, 0X0635, 0X0636, 0X0637, - 0X0638, 0X0639, 0X063A, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0640, 0X0641, 0X0642, 0X0643, 0X0644, 0X0645, 0X0646, 0X0647, - 0X0648, 0X0649, 0X064A, 0X064B, 0X064C, 0X064D, 0X064E, 0X064F, - 0X0650, 0X0651, 0X0652, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000 + 0x00A0, 0x0000, 0x0000, 0x0000, 0x00A4, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x060C, 0x00AD, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x061B, 0x0000, 0x0000, 0x0000, 0x061F, + 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, + 0x0638, 0x0639, 0x063A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, + 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, + 0x0650, 0x0651, 0x0652, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; DEF_SB_TBL(8859_6, "ISO-8859-6", "ISO-8859-6", iso8859_6_aliases, 0xA0, iso8859_6_ucs_table); static const char *iso8859_7_aliases[] = {"ISO8859-7", "greek", NULL}; static const unsigned short iso8859_7_ucs_table[] = { - 0X00A0, 0X2018, 0X2019, 0X00A3, 0X20AC, 0X20AF, 0X00A6, 0X00A7, - 0X00A8, 0X00A9, 0X037A, 0X00AB, 0X00AC, 0X00AD, 0X0000, 0X2015, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X0384, 0X0385, 0X0386, 0X00B7, - 0X0388, 0X0389, 0X038A, 0X00BB, 0X038C, 0X00BD, 0X038E, 0X038F, - 0X0390, 0X0391, 0X0392, 0X0393, 0X0394, 0X0395, 0X0396, 0X0397, - 0X0398, 0X0399, 0X039A, 0X039B, 0X039C, 0X039D, 0X039E, 0X039F, - 0X03A0, 0X03A1, 0X0000, 0X03A3, 0X03A4, 0X03A5, 0X03A6, 0X03A7, - 0X03A8, 0X03A9, 0X03AA, 0X03AB, 0X03AC, 0X03AD, 0X03AE, 0X03AF, - 0X03B0, 0X03B1, 0X03B2, 0X03B3, 0X03B4, 0X03B5, 0X03B6, 0X03B7, - 0X03B8, 0X03B9, 0X03BA, 0X03BB, 0X03BC, 0X03BD, 0X03BE, 0X03BF, - 0X03C0, 0X03C1, 0X03C2, 0X03C3, 0X03C4, 0X03C5, 0X03C6, 0X03C7, - 0X03C8, 0X03C9, 0X03CA, 0X03CB, 0X03CC, 0X03CD, 0X03CE, 0X0000 + 0x00A0, 0x2018, 0x2019, 0x00A3, 0x20AC, 0x20AF, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x037A, 0x00AB, 0x00AC, 0x00AD, 0x0000, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000 }; DEF_SB_TBL(8859_7, "ISO-8859-7", "ISO-8859-7", iso8859_7_aliases, 0xA0, iso8859_7_ucs_table); static const char *iso8859_8_aliases[] = {"ISO8859-8", "hebrew", NULL}; static const unsigned short iso8859_8_ucs_table[] = { - 0X00A0, 0X0000, 0X00A2, 0X00A3, 0X00A4, 0X00A5, 0X00A6, 0X00A7, - 0X00A8, 0X00A9, 0X00D7, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00AF, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X00B4, 0X00B5, 0X00B6, 0X00B7, - 0X00B8, 0X00B9, 0X00F7, 0X00BB, 0X00BC, 0X00BD, 0X00BE, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, - 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X2017, - 0X05D0, 0X05D1, 0X05D2, 0X05D3, 0X05D4, 0X05D5, 0X05D6, 0X05D7, - 0X05D8, 0X05D9, 0X05DA, 0X05DB, 0X05DC, 0X05DD, 0X05DE, 0X05DF, - 0X05E0, 0X05E1, 0X05E2, 0X05E3, 0X05E4, 0X05E5, 0X05E6, 0X05E7, - 0X05E8, 0X05E9, 0X05EA, 0X0000, 0X0000, 0X200E, 0X200F, 0X0000 + 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000 }; DEF_SB_TBL(8859_8, "ISO-8859-8", "ISO-8859-8", iso8859_8_aliases, 0xA0, iso8859_8_ucs_table); static const char *iso8859_9_aliases[] = {"ISO8859-9", "latin5", NULL}; static const unsigned short iso8859_9_ucs_table[] = { - 0X00A0, 0X00A1, 0X00A2, 0X00A3, 0X00A4, 0X00A5, 0X00A6, 0X00A7, - 0X00A8, 0X00A9, 0X00AA, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00AF, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X00B4, 0X00B5, 0X00B6, 0X00B7, - 0X00B8, 0X00B9, 0X00BA, 0X00BB, 0X00BC, 0X00BD, 0X00BE, 0X00BF, - 0X00C0, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X011E, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X00D7, - 0X00D8, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X0130, 0X015E, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X011F, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X00F7, - 0X00F8, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X0131, 0X015F, 0X00FF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF }; DEF_SB_TBL(8859_9, "ISO-8859-9", "ISO-8859-9", iso8859_9_aliases, 0xA0, iso8859_9_ucs_table); static const char *iso8859_10_aliases[] = {"ISO8859-10", "latin6", NULL}; static const unsigned short iso8859_10_ucs_table[] = { - 0X00A0, 0X0104, 0X0112, 0X0122, 0X012A, 0X0128, 0X0136, 0X00A7, - 0X013B, 0X0110, 0X0160, 0X0166, 0X017D, 0X00AD, 0X016A, 0X014A, - 0X00B0, 0X0105, 0X0113, 0X0123, 0X012B, 0X0129, 0X0137, 0X00B7, - 0X013C, 0X0111, 0X0161, 0X0167, 0X017E, 0X2015, 0X016B, 0X014B, - 0X0100, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X012E, - 0X010C, 0X00C9, 0X0118, 0X00CB, 0X0116, 0X00CD, 0X00CE, 0X00CF, - 0X00D0, 0X0145, 0X014C, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X0168, - 0X00D8, 0X0172, 0X00DA, 0X00DB, 0X00DC, 0X00DD, 0X00DE, 0X00DF, - 0X0101, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X012F, - 0X010D, 0X00E9, 0X0119, 0X00EB, 0X0117, 0X00ED, 0X00EE, 0X00EF, - 0X00F0, 0X0146, 0X014D, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X0169, - 0X00F8, 0X0173, 0X00FA, 0X00FB, 0X00FC, 0X00FD, 0X00FE, 0X0138 + 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, + 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A, + 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, + 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B, + 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, + 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, + 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138 }; DEF_SB_TBL(8859_10, "ISO-8859-10", "ISO-8859-10", iso8859_10_aliases, 0xA0, iso8859_10_ucs_table); static const char *iso8859_13_aliases[] = {"ISO8859-13", NULL}; static const unsigned short iso8859_13_ucs_table[] = { - 0X00A0, 0X201D, 0X00A2, 0X00A3, 0X00A4, 0X201E, 0X00A6, 0X00A7, - 0X00D8, 0X00A9, 0X0156, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00C6, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X201C, 0X00B5, 0X00B6, 0X00B7, - 0X00F8, 0X00B9, 0X0157, 0X00BB, 0X00BC, 0X00BD, 0X00BE, 0X00E6, - 0X0104, 0X012E, 0X0100, 0X0106, 0X00C4, 0X00C5, 0X0118, 0X0112, - 0X010C, 0X00C9, 0X0179, 0X0116, 0X0122, 0X0136, 0X012A, 0X013B, - 0X0160, 0X0143, 0X0145, 0X00D3, 0X014C, 0X00D5, 0X00D6, 0X00D7, - 0X0172, 0X0141, 0X015A, 0X016A, 0X00DC, 0X017B, 0X017D, 0X00DF, - 0X0105, 0X012F, 0X0101, 0X0107, 0X00E4, 0X00E5, 0X0119, 0X0113, - 0X010D, 0X00E9, 0X017A, 0X0117, 0X0123, 0X0137, 0X012B, 0X013C, - 0X0161, 0X0144, 0X0146, 0X00F3, 0X014D, 0X00F5, 0X00F6, 0X00F7, - 0X0173, 0X0142, 0X015B, 0X016B, 0X00FC, 0X017C, 0X017E, 0X2019 + 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, + 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019 }; DEF_SB_TBL(8859_13, "ISO-8859-13", "ISO-8859-13", iso8859_13_aliases, 0xA0, iso8859_13_ucs_table); static const char *iso8859_14_aliases[] = {"ISO8859-14", "latin8", NULL}; static const unsigned short iso8859_14_ucs_table[] = { - 0X00A0, 0X1E02, 0X1E03, 0X00A3, 0X010A, 0X010B, 0X1E0A, 0X00A7, - 0X1E80, 0X00A9, 0X1E82, 0X1E0B, 0X1EF2, 0X00AD, 0X00AE, 0X0178, - 0X1E1E, 0X1E1F, 0X0120, 0X0121, 0X1E40, 0X1E41, 0X00B6, 0X1E56, - 0X1E81, 0X1E57, 0X1E83, 0X1E60, 0X1EF3, 0X1E84, 0X1E85, 0X1E61, - 0X00C0, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X0174, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X1E6A, - 0X00D8, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X00DD, 0X0176, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X0175, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X1E6B, - 0X00F8, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X00FD, 0X0177, 0X00FF + 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, + 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178, + 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, + 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF }; DEF_SB_TBL(8859_14, "ISO-8859-14", "ISO-8859-14", iso8859_14_aliases, 0xA0, iso8859_14_ucs_table); static const char *iso8859_15_aliases[] = {"ISO8859-15", NULL}; static const unsigned short iso8859_15_ucs_table[] = { - 0X00A0, 0X00A1, 0X00A2, 0X00A3, 0X20AC, 0X00A5, 0X0160, 0X00A7, - 0X0161, 0X00A9, 0X00AA, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00AF, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X017D, 0X00B5, 0X00B6, 0X00B7, - 0X017E, 0X00B9, 0X00BA, 0X00BB, 0X0152, 0X0153, 0X0178, 0X00BF, - 0X00C0, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X00D0, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X00D7, - 0X00D8, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X00DD, 0X00DE, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X00F0, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X00F7, - 0X00F8, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X00FD, 0X00FE, 0X00FF + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, + 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, + 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; DEF_SB_TBL(8859_15, "ISO-8859-15", "ISO-8859-15", iso8859_15_aliases, 0xA0, iso8859_15_ucs_table); static const char *iso8859_16_aliases[] = {"ISO8859-16", NULL}; static const unsigned short iso8859_16_ucs_table[] = { - 0X00A0, 0X0104, 0X0105, 0X0141, 0X20AC, 0X201E, 0X0160, 0X00A7, - 0X0161, 0X00A9, 0X0218, 0X00AB, 0X0179, 0X00AD, 0X017A, 0X017B, - 0X00B0, 0X00B1, 0X010C, 0X0142, 0X017D, 0X201D, 0X00B6, 0X00B7, - 0X017E, 0X010D, 0X0219, 0X00BB, 0X0152, 0X0153, 0X0178, 0X017C, - 0X00C0, 0X00C1, 0X00C2, 0X0102, 0X00C4, 0X0106, 0X00C6, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X0110, 0X0143, 0X00D2, 0X00D3, 0X00D4, 0X0150, 0X00D6, 0X015A, - 0X0170, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X0118, 0X021A, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X0103, 0X00E4, 0X0107, 0X00E6, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X0111, 0X0144, 0X00F2, 0X00F3, 0X00F4, 0X0151, 0X00F6, 0X015B, - 0X0171, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X0119, 0X021B, 0X00FF + 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, + 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B, + 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, + 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, + 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, + 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF }; DEF_SB_TBL(8859_16, "ISO-8859-16", "ISO-8859-16", iso8859_16_aliases, 0xA0, iso8859_16_ucs_table); static const char *cp1251_aliases[] = {"CP1251", "CP-1251", "WINDOWS-1251", NULL}; static const unsigned short cp1251_ucs_table[] = { - 0X0402, 0X0403, 0X201A, 0X0453, 0X201E, 0X2026, 0X2020, 0X2021, - 0X20AC, 0X2030, 0X0409, 0X2039, 0X040A, 0X040C, 0X040B, 0X040F, - 0X0452, 0X2018, 0X2019, 0X201C, 0X201D, 0X2022, 0X2013, 0X2014, - 0X0000, 0X2122, 0X0459, 0X203A, 0X045A, 0X045C, 0X045B, 0X045F, - 0X00A0, 0X040E, 0X045E, 0X0408, 0X00A4, 0X0490, 0X00A6, 0X00A7, - 0X0401, 0X00A9, 0X0404, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X0407, - 0X00B0, 0X00B1, 0X0406, 0X0456, 0X0491, 0X00B5, 0X00B6, 0X00B7, - 0X0451, 0X2116, 0X0454, 0X00BB, 0X0458, 0X0405, 0X0455, 0X0457, - 0X0410, 0X0411, 0X0412, 0X0413, 0X0414, 0X0415, 0X0416, 0X0417, - 0X0418, 0X0419, 0X041A, 0X041B, 0X041C, 0X041D, 0X041E, 0X041F, - 0X0420, 0X0421, 0X0422, 0X0423, 0X0424, 0X0425, 0X0426, 0X0427, - 0X0428, 0X0429, 0X042A, 0X042B, 0X042C, 0X042D, 0X042E, 0X042F, - 0X0430, 0X0431, 0X0432, 0X0433, 0X0434, 0X0435, 0X0436, 0X0437, - 0X0438, 0X0439, 0X043A, 0X043B, 0X043C, 0X043D, 0X043E, 0X043F, - 0X0440, 0X0441, 0X0442, 0X0443, 0X0444, 0X0445, 0X0446, 0X0447, - 0X0448, 0X0449, 0X044A, 0X044B, 0X044C, 0X044D, 0X044E, 0X044F + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F }; DEF_SB_TBL(cp1251, "Windows-1251", "Windows-1251", cp1251_aliases, 0x80, cp1251_ucs_table); static const char *cp1252_aliases[] = {"cp1252", NULL}; static const unsigned short cp1252_ucs_table[] = { - 0X20AC,0X0000,0X201A,0X0192,0X201E,0X2026,0X2020,0X2021, - 0X02C6,0X2030,0X0160,0X2039,0X0152,0X0000,0X017D,0X0000, - 0X0000,0X2018,0X2019,0X201C,0X201D,0X2022,0X2013,0X2014, - 0X02DC,0X2122,0X0161,0X203A,0X0153,0X0000,0X017E,0X0178 + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178 }; DEF_SB(cp1252, "Windows-1252", "Windows-1252", cp1252_aliases); static int mbfl_filt_conv_wchar_cp1252(int c, mbfl_convert_filter *filter) { - if (c >= 0x100) { + if (c >= 0x100) { for (int n = 0; n < 32; n++) { if (c == cp1252_ucs_table[n]) { CK((*filter->output_function)(0x80 + n, filter->data)); @@ -407,157 +511,193 @@ static int mbfl_filt_conv_wchar_cp1252(int c, mbfl_convert_filter *filter) static int mbfl_filt_conv_cp1252_wchar(int c, mbfl_convert_filter *filter) { int s; - if (c >= 0x80 && c < 0xA0) { - s = cp1252_ucs_table[c - 0x80]; - if (!s) - s = MBFL_BAD_INPUT; + s = coalesce(cp1252_ucs_table[c - 0x80], MBFL_BAD_INPUT); } else { s = c; } - CK((*filter->output_function)(s, filter->data)); return 0; } +static size_t mb_cp1252_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + + if (c >= 0x80 && c < 0xA0) { + *out++ = coalesce(cp1252_ucs_table[c - 0x80], MBFL_BAD_INPUT); + } else { + *out++ = c; + } + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_cp1252(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + while (len--) { + uint32_t w = *in++; + + if (w >= 0x100) { + for (int i = 0; i < 32; i++) { + if (w == cp1252_ucs_table[i]) { + out = mb_convert_buf_add(out, i + 0x80); + goto continue_cp1252; + } + } + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_cp1252); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } else if (w <= 0x7F || w >= 0xA0) { + out = mb_convert_buf_add(out, w); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_cp1252); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } + continue_cp1252: ; + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + static const char *cp1254_aliases[] = {"CP1254", "CP-1254", "WINDOWS-1254", NULL}; static const unsigned short cp1254_ucs_table[] = { - 0X20AC, 0X0000, 0X201A, 0X0192, 0X201E, 0X2026, 0X2020, 0X2021, - 0X02C6, 0X2030, 0X0160, 0X2039, 0X0152, 0X0000, 0X0000, 0X0000, - 0X0000, 0X2018, 0X2019, 0X201C, 0X201D, 0X2022, 0X2013, 0X2014, - 0X02DC, 0X2122, 0X0161, 0X203A, 0X0153, 0X0000, 0X0000, 0X0178, - 0X00A0, 0X00A1, 0X00A2, 0X00A3, 0X00A4, 0X00A5, 0X00A6, 0X00A7, - 0X00A8, 0X00A9, 0X00AA, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00AF, - 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X00B4, 0X00B5, 0X00B6, 0X00B7, - 0X00B8, 0X00B9, 0X00BA, 0X00BB, 0X00BC, 0X00BD, 0X00BE, 0X00BF, - 0X00C0, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X00C7, - 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, - 0X011E, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X00D7, - 0X00D8, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X0130, 0X015E, 0X00DF, - 0X00E0, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X00E7, - 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, - 0X011F, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X00F7, - 0X00F8, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X0131, 0X015F, 0X00FF + 0x20AC, 0X0000, 0X201A, 0X0192, 0X201E, 0X2026, 0X2020, 0X2021, + 0X02C6, 0X2030, 0X0160, 0X2039, 0X0152, 0X0000, 0X0000, 0X0000, + 0X0000, 0X2018, 0X2019, 0X201C, 0X201D, 0X2022, 0X2013, 0X2014, + 0X02DC, 0X2122, 0X0161, 0X203A, 0X0153, 0X0000, 0X0000, 0X0178, + 0X00A0, 0X00A1, 0X00A2, 0X00A3, 0X00A4, 0X00A5, 0X00A6, 0X00A7, + 0X00A8, 0X00A9, 0X00AA, 0X00AB, 0X00AC, 0X00AD, 0X00AE, 0X00AF, + 0X00B0, 0X00B1, 0X00B2, 0X00B3, 0X00B4, 0X00B5, 0X00B6, 0X00B7, + 0X00B8, 0X00B9, 0X00BA, 0X00BB, 0X00BC, 0X00BD, 0X00BE, 0X00BF, + 0X00C0, 0X00C1, 0X00C2, 0X00C3, 0X00C4, 0X00C5, 0X00C6, 0X00C7, + 0X00C8, 0X00C9, 0X00CA, 0X00CB, 0X00CC, 0X00CD, 0X00CE, 0X00CF, + 0X011E, 0X00D1, 0X00D2, 0X00D3, 0X00D4, 0X00D5, 0X00D6, 0X00D7, + 0X00D8, 0X00D9, 0X00DA, 0X00DB, 0X00DC, 0X0130, 0X015E, 0X00DF, + 0X00E0, 0X00E1, 0X00E2, 0X00E3, 0X00E4, 0X00E5, 0X00E6, 0X00E7, + 0X00E8, 0X00E9, 0X00EA, 0X00EB, 0X00EC, 0X00ED, 0X00EE, 0X00EF, + 0X011F, 0X00F1, 0X00F2, 0X00F3, 0X00F4, 0X00F5, 0X00F6, 0X00F7, + 0X00F8, 0X00F9, 0X00FA, 0X00FB, 0X00FC, 0X0131, 0X015F, 0X00FF }; DEF_SB_TBL(cp1254, "Windows-1254", "Windows-1254", cp1254_aliases, 0x80, cp1254_ucs_table); static const char *cp866_aliases[] = {"CP-866", "IBM866", "IBM-866", NULL}; static const unsigned short cp866_ucs_table[] = { - 0X0410, 0X0411, 0X0412, 0X0413, 0X0414, 0X0415, 0X0416, 0X0417, - 0X0418, 0X0419, 0X041A, 0X041B, 0X041C, 0X041D, 0X041E, 0X041F, - 0X0420, 0X0421, 0X0422, 0X0423, 0X0424, 0X0425, 0X0426, 0X0427, - 0X0428, 0X0429, 0X042A, 0X042B, 0X042C, 0X042D, 0X042E, 0X042F, - 0X0430, 0X0431, 0X0432, 0X0433, 0X0434, 0X0435, 0X0436, 0X0437, - 0X0438, 0X0439, 0X043A, 0X043B, 0X043C, 0X043D, 0X043E, 0X043F, - 0X2591, 0X2592, 0X2593, 0X2502, 0X2524, 0X2561, 0X2562, 0X2556, - 0X2555, 0X2563, 0X2551, 0X2557, 0X255D, 0X255C, 0X255B, 0X2510, - 0X2514, 0X2534, 0X252C, 0X251C, 0X2500, 0X253C, 0X255E, 0X255F, - 0X255A, 0X2554, 0X2569, 0X2566, 0X2560, 0X2550, 0X256C, 0X2567, - 0X2568, 0X2564, 0X2565, 0X2559, 0X2558, 0X2552, 0X2553, 0X256B, - 0X256A, 0X2518, 0X250C, 0X2588, 0X2584, 0X258C, 0X2590, 0X2580, - 0X0440, 0X0441, 0X0442, 0X0443, 0X0444, 0X0445, 0X0446, 0X0447, - 0X0448, 0X0449, 0X044A, 0X044B, 0X044C, 0X044D, 0X044E, 0X044F, - 0X0401, 0X0451, 0X0404, 0X0454, 0X0407, 0X0457, 0X040E, 0X045E, - 0X00B0, 0X2219, 0X00B7, 0X221A, 0X2116, 0X00A4, 0X25A0, 0X00A0 + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 }; DEF_SB_TBL(cp866, "CP866", "CP866", cp866_aliases, 0x80, cp866_ucs_table); static const char *cp850_aliases[] = {"CP-850", "IBM850", "IBM-850", NULL}; static const unsigned short cp850_ucs_table[] = { - 0X00C7, 0X00FC, 0X00E9, 0X00E2, 0X00E4, 0X00E0, 0X00E5, 0X00E7, - 0X00EA, 0X00EB, 0X00E8, 0X00EF, 0X00EE, 0X00EC, 0X00C4, 0X00C5, - 0X00C9, 0X00E6, 0X00C6, 0X00F4, 0X00F6, 0X00F2, 0X00FB, 0X00F9, - 0X00FF, 0X00D6, 0X00DC, 0X00F8, 0X00A3, 0X00D8, 0X00D7, 0X0192, - 0X00E1, 0X00ED, 0X00F3, 0X00FA, 0X00F1, 0X00D1, 0X00AA, 0X00BA, - 0X00BF, 0X00AE, 0X00AC, 0X00BD, 0X00BC, 0X00A1, 0X00AB, 0X00BB, - 0X2591, 0X2592, 0X2593, 0X2502, 0X2524, 0X00C1, 0X00C2, 0X00C0, - 0X00A9, 0X2563, 0X2551, 0X2557, 0X255D, 0X00A2, 0X00A5, 0X2510, - 0X2514, 0X2534, 0X252C, 0X251C, 0X2500, 0X253C, 0X00E3, 0X00C3, - 0X255A, 0X2554, 0X2569, 0X2566, 0X2560, 0X2550, 0X256C, 0X00A4, - 0X00F0, 0X00D0, 0X00CA, 0X00CB, 0X00C8, 0X0131, 0X00CD, 0X00CE, - 0X00CF, 0X2518, 0X250C, 0X2588, 0X2584, 0X00A6, 0X00CC, 0X2580, - 0X00D3, 0X00DF, 0X00D4, 0X00D2, 0X00F5, 0X00D5, 0X00B5, 0X00FE, - 0X00DE, 0X00DA, 0X00DB, 0X00D9, 0X00FD, 0X00DD, 0X00AF, 0X00B4, - 0X00AD, 0X00B1, 0X2017, 0X00BE, 0X00B6, 0X00A7, 0X00F7, 0X00B8, - 0X00B0, 0X00A8, 0X00B7, 0X00B9, 0X00B3, 0X00B2, 0X25A0, 0X00A0 + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; DEF_SB_TBL(cp850, "CP850", "CP850", cp850_aliases, 0x80, cp850_ucs_table); static const char *koi8r_aliases[] = {"KOI8R", NULL}; static const unsigned short koi8r_ucs_table[] = { - 0X2500, 0X2502, 0X250C, 0X2510, 0X2514, 0X2518, 0X251C, 0X2524, - 0X252C, 0X2534, 0X253C, 0X2580, 0X2584, 0X2588, 0X258C, 0X2590, - 0X2591, 0X2592, 0X2593, 0X2320, 0X25A0, 0X2219, 0X221A, 0X2248, - 0X2264, 0X2265, 0X00A0, 0X2321, 0X00B0, 0X00B2, 0X00B7, 0X00F7, - 0X2550, 0X2551, 0X2552, 0X0451, 0X2553, 0X2554, 0X2555, 0X2556, - 0X2557, 0X2558, 0X2559, 0X255A, 0X255B, 0X255C, 0X255D, 0X255E, - 0X255F, 0X2560, 0X2561, 0X0401, 0X2562, 0X2563, 0X2564, 0X2565, - 0X2566, 0X2567, 0X2568, 0X2569, 0X256A, 0X256B, 0X256C, 0X00A9, - 0X044E, 0X0430, 0X0431, 0X0446, 0X0434, 0X0435, 0X0444, 0X0433, - 0X0445, 0X0438, 0X0439, 0X043A, 0X043B, 0X043C, 0X043D, 0X043E, - 0X043F, 0X044F, 0X0440, 0X0441, 0X0442, 0X0443, 0X0436, 0X0432, - 0X044C, 0X044B, 0X0437, 0X0448, 0X044D, 0X0449, 0X0447, 0X044A, - 0X042E, 0X0410, 0X0411, 0X0426, 0X0414, 0X0415, 0X0424, 0X0413, - 0X0425, 0X0418, 0X0419, 0X041A, 0X041B, 0X041C, 0X041D, 0X041E, - 0X041F, 0X042F, 0X0420, 0X0421, 0X0422, 0X0423, 0X0416, 0X0412, - 0X042C, 0X042B, 0X0417, 0X0428, 0X042D, 0X0429, 0X0427, 0X042A + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, + 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, + 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, + 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, + 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, + 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, + 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, + 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, + 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A }; DEF_SB_TBL(koi8r, "KOI8-R", "KOI8-R", koi8r_aliases, 0x80, koi8r_ucs_table); static const char *koi8u_aliases[] = {"KOI8U", NULL}; static const unsigned short koi8u_ucs_table[] = { - 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, - 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, - 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, - 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, - 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, - 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, - 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, - 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, - 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, - 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, - 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, - 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, - 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, - 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, - 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, - 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A + 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, + 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, + 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, + 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, + 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, + 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, + 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, + 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, + 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, + 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, + 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, + 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, + 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, + 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, + 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, + 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A }; DEF_SB_TBL(koi8u, "KOI8-U", "KOI8-U", koi8u_aliases, 0x80, koi8u_ucs_table); static const char *armscii8_aliases[] = {"ArmSCII8", "ARMSCII-8", "ARMSCII8", NULL}; static const unsigned short armscii8_ucs_table[] = { - 0X00A0, 0X0000, 0X0587, 0X0589, 0X0029, 0X0028, 0X00BB, 0X00AB, - 0X2014, 0X002E, 0X055D, 0X002C, 0X002D, 0X058A, 0X2026, 0X055C, - 0X055B, 0X055E, 0X0531, 0X0561, 0X0532, 0X0562, 0X0533, 0X0563, - 0X0534, 0X0564, 0X0535, 0X0565, 0X0536, 0X0566, 0X0537, 0X0567, - 0X0538, 0X0568, 0X0539, 0X0569, 0X053A, 0X056A, 0X053B, 0X056B, - 0X053C, 0X056C, 0X053D, 0X056D, 0X053E, 0X056E, 0X053F, 0X056F, - 0X0540, 0X0570, 0X0541, 0X0571, 0X0542, 0X0572, 0X0543, 0X0573, - 0X0544, 0X0574, 0X0545, 0X0575, 0X0546, 0X0576, 0X0547, 0X0577, - 0X0548, 0X0578, 0X0549, 0X0579, 0X054A, 0X057A, 0X054B, 0X057B, - 0X054C, 0X057C, 0X054D, 0X057D, 0X054E, 0X057E, 0X054F, 0X057F, - 0X0550, 0X0580, 0X0551, 0X0581, 0X0552, 0X0582, 0X0553, 0X0583, - 0X0554, 0X0584, 0X0555, 0X0585, 0X0556, 0X0586, 0X055A, 0X0000 + 0x00A0, 0x0000, 0x0587, 0x0589, 0x0029, 0x0028, 0x00BB, 0x00AB, + 0x2014, 0x002E, 0x055D, 0x002C, 0x002D, 0x058A, 0x2026, 0x055C, + 0x055B, 0x055E, 0x0531, 0x0561, 0x0532, 0x0562, 0x0533, 0x0563, + 0x0534, 0x0564, 0x0535, 0x0565, 0x0536, 0x0566, 0x0537, 0x0567, + 0x0538, 0x0568, 0x0539, 0x0569, 0x053A, 0x056A, 0x053B, 0x056B, + 0x053C, 0x056C, 0x053D, 0x056D, 0x053E, 0x056E, 0x053F, 0x056F, + 0x0540, 0x0570, 0x0541, 0x0571, 0x0542, 0x0572, 0x0543, 0x0573, + 0x0544, 0x0574, 0x0545, 0x0575, 0x0546, 0x0576, 0x0547, 0x0577, + 0x0548, 0x0578, 0x0549, 0x0579, 0x054A, 0x057A, 0x054B, 0x057B, + 0x054C, 0x057C, 0x054D, 0x057D, 0x054E, 0x057E, 0x054F, 0x057F, + 0x0550, 0x0580, 0x0551, 0x0581, 0x0552, 0x0582, 0x0553, 0x0583, + 0x0554, 0x0584, 0x0555, 0x0585, 0x0556, 0x0586, 0x055A, 0x0000 }; static const unsigned char ucs_armscii8_table[] = { - 0XA5, 0XA4, 0X2A, 0X2B, 0XAB, 0XAC, 0XA9, 0X2F + 0xA5, 0xA4, 0x2A, 0x2B, 0xAB, 0xAC, 0xA9, 0x2F }; DEF_SB(armscii8, "ArmSCII-8", "ArmSCII-8", armscii8_aliases); static int mbfl_filt_conv_armscii8_wchar(int c, mbfl_convert_filter *filter) { - int s; - - if (c < 0xA0) { - s = c; - } else { - s = armscii8_ucs_table[c - 0xA0]; - if (!s) - s = MBFL_BAD_INPUT; - } - - CK((*filter->output_function)(s, filter->data)); + CK((*filter->output_function)((c < 0xA0) ? c : coalesce(armscii8_ucs_table[c - 0xA0], MBFL_BAD_INPUT), filter->data)); return 0; } @@ -580,3 +720,47 @@ static int mbfl_filt_conv_wchar_armscii8(int c, mbfl_convert_filter *filter) } return 0; } + +static size_t mb_armscii8_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + *out++ = (c < 0xA0) ? c : coalesce(armscii8_ucs_table[c - 0xA0], MBFL_BAD_INPUT); + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_armscii8(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + while (len--) { + uint32_t w = *in++; + + if (w >= 0x28 && w <= 0x2F) { + out = mb_convert_buf_add(out, ucs_armscii8_table[w - 0x28]); + } else if (w < 0xA0) { + out = mb_convert_buf_add(out, w); + } else { + for (int i = 0; i < 0x60; i++) { + if (w == armscii8_ucs_table[i]) { + out = mb_convert_buf_add(out, 0xA0 + i); + goto continue_armscii8; + } + } + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_armscii8); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } + continue_armscii8: ; + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis.c index ef59026a9eea1..1fd372edc8f47 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_sjis.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis.c @@ -37,6 +37,8 @@ #include "unicode_table_jis.h" static int mbfl_filt_conv_sjis_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_sjis_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_sjis(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); const unsigned char mblen_table_sjis[] = { /* 0x80-0x9f,0xE0-0xFF */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -67,7 +69,9 @@ const mbfl_encoding mbfl_encoding_sjis = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis_wchar, - &vtbl_wchar_sjis + &vtbl_wchar_sjis, + mb_sjis_to_wchar, + mb_wchar_to_sjis }; const struct mbfl_convert_vtbl vtbl_sjis_wchar = { @@ -256,3 +260,116 @@ int mbfl_filt_conv_wchar_sjis(int c, mbfl_convert_filter *filter) return 0; } + +static size_t mb_sjis_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + + if (c == 0x5C) { + *out++ = 0xA5; + } else if (c == 0x7E) { + *out++ = 0x203E; + } else if (c <= 0x7F) { + *out++ = c; + } else if (c >= 0xA1 && c <= 0xDF) { /* Kana */ + *out++ = 0xFEC0 + c; + } else if (c > 0x80 && c <= 0xEF && c != 0xA0 && p < e) { + unsigned char c2 = *p++; + if (c2 >= 0x40 && c2 <= 0xFC && c2 != 0x7F) { + unsigned int s1, s2; + SJIS_DECODE(c, c2, s1, s2); + uint32_t w = (s1 - 0x21)*94 + s2 - 0x21; + if (w < jisx0208_ucs_table_size) { + w = jisx0208_ucs_table[w]; + if (!w) + w = MBFL_BAD_INPUT; + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_sjis(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + unsigned int s = 0; + + if (w == 0x5C) { + /* Unicode 0x5C is a backslash; but Shift-JIS uses 0x5C for the + * Yen sign. JIS X 0208 kuten 0x2140 is a backslash. */ + s = 0x2140; + } else if (w == 0x7E) { + /* Unicode 0x7E is a tilde, but Shift-JIS uses 0x7E for overline (or + * macron). JIS X 0208 kuten 0x2141 is 'WAVE DASH' */ + s = 0x2141; + } else if (w == 0xAF || w == 0x203E) { /* U+00AF is MACRON, U+203E is OVERLINE */ + s = 0x7E; /* Halfwidth overline/macron */ + } else if (w >= ucs_a1_jis_table_min && w < ucs_a1_jis_table_max) { + s = ucs_a1_jis_table[w - ucs_a1_jis_table_min]; + } else if (w >= ucs_a2_jis_table_min && w < ucs_a2_jis_table_max) { + s = ucs_a2_jis_table[w - ucs_a2_jis_table_min]; + } else if (w >= ucs_i_jis_table_min && w < ucs_i_jis_table_max) { + s = ucs_i_jis_table[w - ucs_i_jis_table_min]; + } else if (w >= ucs_r_jis_table_min && w < ucs_r_jis_table_max) { + s = ucs_r_jis_table[w - ucs_r_jis_table_min]; + } + + if (s == 0) { + if (w == 0xA5) { /* YEN SIGN */ + s = 0x5C; + } else if (w == 0xFF3C) { /* FULLWIDTH REVERSE SOLIDUS */ + s = 0x2140; + } else if (w == 0x2225) { /* PARALLEL TO */ + s = 0x2142; + } else if (w == 0xFF0D) { /* FULLWIDTH HYPHEN-MINUS */ + s = 0x215D; + } else if (w == 0xFFE0) { /* FULLWIDTH CENT SIGN */ + s = 0x2171; + } else if (w == 0xFFE1) { /* FULLWIDTH POUND SIGN */ + s = 0x2172; + } else if (w == 0xFFE2) { /* FULLWIDTH NOT SIGN */ + s = 0x224C; + } else if (w != 0) { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_sjis); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + continue; + } + } else if (s >= 0x8080) { /* JIS X 0212; not supported */ + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_sjis); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + continue; + } + + if (s <= 0xFF) { + /* Latin/Kana */ + out = mb_convert_buf_add(out, s); + } else { + /* Kanji */ + unsigned int c1 = (s >> 8) & 0xFF, c2 = s & 0xFF, s2; + SJIS_ENCODE(c1, c2, s, s2); + out = mb_convert_buf_add2(out, s, s2); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c index 19602dd9da4da..993fc151dec4a 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_2004.c @@ -48,7 +48,9 @@ const mbfl_encoding mbfl_encoding_sjis2004 = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis2004_wchar, - &vtbl_wchar_sjis2004 + &vtbl_wchar_sjis2004, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_sjis2004_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c index e24e0b1a3f7cb..97fbb59e50583 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mac.c @@ -50,7 +50,9 @@ const mbfl_encoding mbfl_encoding_sjis_mac = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis_mac_wchar, - &vtbl_wchar_sjis_mac + &vtbl_wchar_sjis_mac, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_sjis_mac_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c index 4eaa1c2557512..38ec5fe11d89f 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_sjis_mobile.c @@ -52,7 +52,9 @@ const mbfl_encoding mbfl_encoding_sjis_docomo = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis_docomo_wchar, - &vtbl_wchar_sjis_docomo + &vtbl_wchar_sjis_docomo, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_sjis_kddi = { @@ -63,7 +65,9 @@ const mbfl_encoding mbfl_encoding_sjis_kddi = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis_kddi_wchar, - &vtbl_wchar_sjis_kddi + &vtbl_wchar_sjis_kddi, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_sjis_sb = { @@ -74,7 +78,9 @@ const mbfl_encoding mbfl_encoding_sjis_sb = { mblen_table_sjis, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_sjis_sb_wchar, - &vtbl_wchar_sjis_sb + &vtbl_wchar_sjis_sb, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_sjis_docomo_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_ucs2.c b/ext/mbstring/libmbfl/filters/mbfilter_ucs2.c index 388a901fe66d2..9ee1e4bf63a89 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_ucs2.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_ucs2.c @@ -31,6 +31,11 @@ #include "mbfilter_ucs2.h" static int mbfl_filt_conv_ucs2_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_ucs2_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static size_t mb_ucs2be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_ucs2be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); +static size_t mb_ucs2le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_ucs2le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); static const char *mbfl_encoding_ucs2_aliases[] = {"ISO-10646-UCS-2", "UCS2" , "UNICODE", NULL}; @@ -49,7 +54,9 @@ const mbfl_encoding mbfl_encoding_ucs2 = { NULL, MBFL_ENCTYPE_WCS2, &vtbl_ucs2_wchar, - &vtbl_wchar_ucs2 + &vtbl_wchar_ucs2, + mb_ucs2_to_wchar, + mb_wchar_to_ucs2be }; const mbfl_encoding mbfl_encoding_ucs2be = { @@ -60,7 +67,9 @@ const mbfl_encoding mbfl_encoding_ucs2be = { NULL, MBFL_ENCTYPE_WCS2, &vtbl_ucs2be_wchar, - &vtbl_wchar_ucs2be + &vtbl_wchar_ucs2be, + mb_ucs2be_to_wchar, + mb_wchar_to_ucs2be }; const mbfl_encoding mbfl_encoding_ucs2le = { @@ -71,7 +80,9 @@ const mbfl_encoding mbfl_encoding_ucs2le = { NULL, MBFL_ENCTYPE_WCS2, &vtbl_ucs2le_wchar, - &vtbl_wchar_ucs2le + &vtbl_wchar_ucs2le, + mb_ucs2le_to_wchar, + mb_wchar_to_ucs2le }; const struct mbfl_convert_vtbl vtbl_ucs2_wchar = { @@ -216,3 +227,119 @@ static int mbfl_filt_conv_ucs2_wchar_flush(mbfl_convert_filter *filter) return 0; } + +#define DETECTED_BE 1 +#define DETECTED_LE 2 + +static size_t mb_ucs2_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + if (*state == DETECTED_BE) { + return mb_ucs2be_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*state == DETECTED_LE) { + return mb_ucs2le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*in_len >= 2) { + unsigned char *p = *in; + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint32_t w = (c1 << 8) | c2; + + if (w == 0xFFFE) { + /* Little-endian BOM */ + *in = p; + *in_len -= 2; + *state = DETECTED_LE; + return mb_ucs2le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (w == 0xFEFF) { + /* Big-endian BOM; don't send it to output */ + *in = p; + *in_len -= 2; + } + } + + *state = DETECTED_BE; + return mb_ucs2be_to_wchar(in, in_len, buf, bufsize, NULL); +} + +static size_t mb_ucs2be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~1); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint32_t w = (c1 << 8) | c2; + *out++ = w; + } + + if (p == e && (*in_len & 0x1) && out < limit) { + /* There is 1 trailing byte, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p++; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_ucs2be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + if (w < MBFL_WCSPLANE_UCS2MAX) { + out = mb_convert_buf_add2(out, (w >> 8) & 0xFF, w & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_ucs2be); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + +static size_t mb_ucs2le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~1); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint32_t w = (c2 << 8) | c1; + *out++ = w; + } + + if (p == e && (*in_len & 0x1) && out < limit) { + /* There is 1 trailing byte, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p++; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_ucs2le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + if (w < MBFL_WCSPLANE_UCS2MAX) { + out = mb_convert_buf_add2(out, w & 0xFF, (w >> 8) & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_ucs2le); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_ucs4.c b/ext/mbstring/libmbfl/filters/mbfilter_ucs4.c index 6676f67c3ac22..c7f5fb2ac04fa 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_ucs4.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_ucs4.c @@ -30,6 +30,12 @@ #include "mbfilter.h" #include "mbfilter_ucs4.h" +static size_t mb_ucs4_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static size_t mb_ucs4be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_ucs4be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); +static size_t mb_ucs4le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_ucs4le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); + static const char *mbfl_encoding_ucs4_aliases[] = {"ISO-10646-UCS-4", "UCS4", NULL}; /* This library historically had encodings called 'byte4be' and 'byte4le' @@ -48,7 +54,9 @@ const mbfl_encoding mbfl_encoding_ucs4 = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_ucs4_wchar, - &vtbl_wchar_ucs4 + &vtbl_wchar_ucs4, + mb_ucs4_to_wchar, + mb_wchar_to_ucs4be }; const mbfl_encoding mbfl_encoding_ucs4be = { @@ -59,7 +67,9 @@ const mbfl_encoding mbfl_encoding_ucs4be = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_ucs4be_wchar, - &vtbl_wchar_ucs4be + &vtbl_wchar_ucs4be, + mb_ucs4be_to_wchar, + mb_wchar_to_ucs4be }; const mbfl_encoding mbfl_encoding_ucs4le = { @@ -70,7 +80,9 @@ const mbfl_encoding mbfl_encoding_ucs4le = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_ucs4le_wchar, - &vtbl_wchar_ucs4le + &vtbl_wchar_ucs4le, + mb_ucs4le_to_wchar, + mb_wchar_to_ucs4le }; const struct mbfl_convert_vtbl vtbl_ucs4_wchar = { @@ -296,3 +308,125 @@ static int mbfl_filt_conv_ucs4_wchar_flush(mbfl_convert_filter *filter) return 0; } + +#define DETECTED_BE 1 +#define DETECTED_LE 2 + +static size_t mb_ucs4_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + if (*state == DETECTED_BE) { + return mb_ucs4be_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*state == DETECTED_LE) { + return mb_ucs4le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*in_len >= 4) { + unsigned char *p = *in; + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; + + if (w == 0xFFFE0000) { + /* Little-endian BOM */ + *in = p; + *in_len -= 4; + *state = DETECTED_LE; + return mb_ucs4le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (w == 0xFEFF) { + /* Big-endian BOM; don't send it to output */ + *in = p; + *in_len -= 4; + } + } + + *state = DETECTED_BE; + return mb_ucs4be_to_wchar(in, in_len, buf, bufsize, NULL); +} + +static size_t mb_ucs4be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~3); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; + *out++ = w; + } + + if (p == e && (*in_len & 0x3) && out < limit) { + /* There are 1-3 trailing bytes, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p = *in + *in_len; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_ucs4be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + + while (len--) { + uint32_t w = *in++; + if (w != MBFL_BAD_INPUT) { + out = mb_convert_buf_add4(out, (w >> 24) & 0xFF, (w >> 16) & 0xFF, (w >> 8) & 0xFF, w & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_ucs4be); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + +static size_t mb_ucs4le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~3); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c4 << 24) | (c3 << 16) | (c2 << 8) | c1; + *out++ = w; + } + + if (p == e && (*in_len & 0x3) && out < limit) { + /* There are 1-3 trailing bytes, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p = *in + *in_len; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_ucs4le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + + while (len--) { + uint32_t w = *in++; + if (w != MBFL_BAD_INPUT) { + out = mb_convert_buf_add4(out, w & 0xFF, (w >> 8) & 0xFF, (w >> 16) & 0xFF, (w >> 24) & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_ucs4le); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_uhc.c b/ext/mbstring/libmbfl/filters/mbfilter_uhc.c index 06300404b6e27..e465fbe2b4d4e 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_uhc.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_uhc.c @@ -67,7 +67,9 @@ const mbfl_encoding mbfl_encoding_uhc = { mblen_table_uhc, 0, &vtbl_uhc_wchar, - &vtbl_wchar_uhc + &vtbl_wchar_uhc, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_uhc_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf16.c b/ext/mbstring/libmbfl/filters/mbfilter_utf16.c index 8477367f79d87..3a3b1d5269094 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf16.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf16.c @@ -31,6 +31,11 @@ #include "mbfilter_utf16.h" static int mbfl_filt_conv_utf16_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_utf16_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static size_t mb_utf16be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf16be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); +static size_t mb_utf16le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf16le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); static const char *mbfl_encoding_utf16_aliases[] = {"utf16", NULL}; @@ -42,7 +47,9 @@ const mbfl_encoding mbfl_encoding_utf16 = { NULL, 0, &vtbl_utf16_wchar, - &vtbl_wchar_utf16 + &vtbl_wchar_utf16, + mb_utf16_to_wchar, + mb_wchar_to_utf16be }; const mbfl_encoding mbfl_encoding_utf16be = { @@ -53,7 +60,9 @@ const mbfl_encoding mbfl_encoding_utf16be = { NULL, 0, &vtbl_utf16be_wchar, - &vtbl_wchar_utf16be + &vtbl_wchar_utf16be, + mb_utf16be_to_wchar, + mb_wchar_to_utf16be }; const mbfl_encoding mbfl_encoding_utf16le = { @@ -64,7 +73,9 @@ const mbfl_encoding mbfl_encoding_utf16le = { NULL, 0, &vtbl_utf16le_wchar, - &vtbl_wchar_utf16le + &vtbl_wchar_utf16le, + mb_utf16le_to_wchar, + mb_wchar_to_utf16le }; const struct mbfl_convert_vtbl vtbl_utf16_wchar = { @@ -322,3 +333,191 @@ static int mbfl_filt_conv_utf16_wchar_flush(mbfl_convert_filter *filter) return 0; } + +#define DETECTED_BE 1 +#define DETECTED_LE 2 + +static size_t mb_utf16_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + if (*state == DETECTED_BE) { + return mb_utf16be_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*state == DETECTED_LE) { + return mb_utf16le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*in_len >= 2) { + unsigned char *p = *in; + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint16_t n = (c1 << 8) | c2; + + if (n == 0xFFFE) { + /* Little-endian BOM */ + *in = p; + *in_len -= 2; + *state = DETECTED_LE; + return mb_utf16le_to_wchar(in, in_len, buf, bufsize, NULL); + } if (n == 0xFEFF) { + /* Big-endian BOM; don't send to output */ + *in = p; + *in_len -= 2; + } + } + + *state = DETECTED_BE; + return mb_utf16be_to_wchar(in, in_len, buf, bufsize, NULL); +} + +static size_t mb_utf16be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + /* We only want to read 16-bit words out of `str`; any trailing byte will be handled at the end */ + unsigned char *p = *in, *e = p + (*in_len & ~1); + /* Set `limit` to one less than the actual amount of space in the buffer; this is because + * on some iterations of the below loop, we might produce two output words */ + uint32_t *out = buf, *limit = buf + bufsize - 1; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint16_t n = (c1 << 8) | c2; + + if (n >= 0xD800 && n <= 0xDBFF) { + /* Handle surrogate */ + if (p < e) { + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint16_t n2 = (c3 << 8) | c4; + + if (n2 >= 0xD800 && n2 <= 0xDBFF) { + /* Wrong; that's the first half of a surrogate pair, when we were expecting the second */ + *out++ = MBFL_BAD_INPUT; + p -= 2; + } else if (n2 >= 0xDC00 && n2 <= 0xDFFF) { + *out++ = (((n & 0x3FF) << 10) | (n2 & 0x3FF)) + 0x10000; + } else { + /* The first half of a surrogate pair was followed by a 'normal' codepoint */ + *out++ = MBFL_BAD_INPUT; + *out++ = n2; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else if (n >= 0xDC00 && n <= 0xDFFF) { + /* This is wrong; second part of surrogate pair has come first */ + *out++ = MBFL_BAD_INPUT; + } else { + *out++ = n; + } + } + + if (p == e && (*in_len & 0x1) && out < limit) { + /* There is an extra trailing byte (which shouldn't be there) */ + *out++ = MBFL_BAD_INPUT; + p++; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_utf16be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + + if (w < MBFL_WCSPLANE_UCS2MAX) { + out = mb_convert_buf_add2(out, (w >> 8) & 0xFF, w & 0xFF); + } else if (w < MBFL_WCSPLANE_UTF32MAX) { + uint16_t n1 = ((w >> 10) - 0x40) | 0xD800; + uint16_t n2 = (w & 0x3FF) | 0xDC00; + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 4); + out = mb_convert_buf_add4(out, (n1 >> 8) & 0xFF, n1 & 0xFF, (n2 >> 8) & 0xFF, n2 & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf16be); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + +static size_t mb_utf16le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + /* We only want to read 16-bit words out of `str`; any trailing byte will be handled at the end */ + unsigned char *p = *in, *e = p + (*in_len & ~1); + /* Set `limit` to one less than the actual amount of space in the buffer; this is because + * on some iterations of the below loop, we might produce two output words */ + uint32_t *out = buf, *limit = buf + bufsize - 1; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + uint16_t n = (c2 << 8) | c1; + + if (n >= 0xD800 && n <= 0xDBFF) { + /* Handle surrogate */ + if (p < e) { + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint16_t n2 = (c4 << 8) | c3; + + if (n2 >= 0xD800 && n2 <= 0xDBFF) { + /* Wrong; that's the first half of a surrogate pair, when we were expecting the second */ + *out++ = MBFL_BAD_INPUT; + p -= 2; + } else if (n2 >= 0xDC00 && n2 <= 0xDFFF) { + *out++ = (((n & 0x3FF) << 10) | (n2 & 0x3FF)) + 0x10000; + } else { + /* The first half of a surrogate pair was followed by a 'normal' codepoint */ + *out++ = MBFL_BAD_INPUT; + *out++ = n2; + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else if (n >= 0xDC00 && n <= 0xDFFF) { + /* This is wrong; second part of surrogate pair has come first */ + *out++ = MBFL_BAD_INPUT; + } else { + *out++ = n; + } + } + + if (p == e && (*in_len & 0x1) && out < limit) { + /* There is an extra trailing byte (which shouldn't be there) */ + *out++ = MBFL_BAD_INPUT; + p++; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_utf16le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + + while (len--) { + uint32_t w = *in++; + + if (w < MBFL_WCSPLANE_UCS2MAX) { + out = mb_convert_buf_add2(out, w & 0xFF, (w >> 8) & 0xFF); + } else if (w < MBFL_WCSPLANE_UTF32MAX) { + uint16_t n1 = ((w >> 10) - 0x40) | 0xD800; + uint16_t n2 = (w & 0x3FF) | 0xDC00; + MB_CONVERT_BUF_ENSURE(buf, out, limit, (len * 2) + 4); + out = mb_convert_buf_add4(out, n1 & 0xFF, (n1 >> 8) & 0xFF, n2 & 0xFF, (n2 >> 8) & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf16le); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 2); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf32.c b/ext/mbstring/libmbfl/filters/mbfilter_utf32.c index ec9945e13ef50..c654e4cf51020 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf32.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf32.c @@ -31,6 +31,11 @@ #include "mbfilter_utf32.h" static int mbfl_filt_conv_utf32_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_utf32_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static size_t mb_utf32be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf32be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); +static size_t mb_utf32le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf32le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); static const char *mbfl_encoding_utf32_aliases[] = {"utf32", NULL}; @@ -42,7 +47,9 @@ const mbfl_encoding mbfl_encoding_utf32 = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_utf32_wchar, - &vtbl_wchar_utf32 + &vtbl_wchar_utf32, + mb_utf32_to_wchar, + mb_wchar_to_utf32be }; const mbfl_encoding mbfl_encoding_utf32be = { @@ -53,7 +60,9 @@ const mbfl_encoding mbfl_encoding_utf32be = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_utf32be_wchar, - &vtbl_wchar_utf32be + &vtbl_wchar_utf32be, + mb_utf32be_to_wchar, + mb_wchar_to_utf32be }; const mbfl_encoding mbfl_encoding_utf32le = { @@ -64,7 +73,9 @@ const mbfl_encoding mbfl_encoding_utf32le = { NULL, MBFL_ENCTYPE_WCS4, &vtbl_utf32le_wchar, - &vtbl_wchar_utf32le + &vtbl_wchar_utf32le, + mb_utf32le_to_wchar, + mb_wchar_to_utf32le }; const struct mbfl_convert_vtbl vtbl_utf32_wchar = { @@ -229,3 +240,135 @@ static int mbfl_filt_conv_utf32_wchar_flush(mbfl_convert_filter *filter) return 0; } + +#define DETECTED_BE 1 +#define DETECTED_LE 2 + +static size_t mb_utf32_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + if (*state == DETECTED_BE) { + return mb_utf32be_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*state == DETECTED_LE) { + return mb_utf32le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (*in_len >= 4) { + unsigned char *p = *in; + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; + + if (w == 0xFFFE0000) { + /* Little-endian BOM */ + *in = p; + *in_len -= 4; + *state = DETECTED_LE; + return mb_utf32le_to_wchar(in, in_len, buf, bufsize, NULL); + } else if (w == 0xFEFF) { + /* Big-endian BOM; don't send it to output */ + *in = p; + *in_len -= 4; + } + } + + *state = DETECTED_BE; + return mb_utf32be_to_wchar(in, in_len, buf, bufsize, NULL); +} + +static size_t mb_utf32be_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~3); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c1 << 24) | (c2 << 16) | (c3 << 8) | c4; + + if (w < MBFL_WCSPLANE_UTF32MAX && (w < 0xD800 || w > 0xDFFF)) { + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + if (p == e && (*in_len & 0x3) && out < limit) { + /* There are 1-3 trailing bytes, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p = *in + *in_len; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_utf32be(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + + while (len--) { + uint32_t w = *in++; + if (w < MBFL_WCSPLANE_UTF32MAX) { + out = mb_convert_buf_add4(out, (w >> 24) & 0xFF, (w >> 16) & 0xFF, (w >> 8) & 0xFF, w & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf32be); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} + +static size_t mb_utf32le_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + (*in_len & ~3); + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c1 = *p++; + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + uint32_t w = (c4 << 24) | (c3 << 16) | (c2 << 8) | c1; + + if (w < MBFL_WCSPLANE_UTF32MAX && (w < 0xD800 || w > 0xDFFF)) { + *out++ = w; + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + if (p == e && (*in_len & 0x3) && out < limit) { + /* There are 1-3 trailing bytes, which shouldn't be there */ + *out++ = MBFL_BAD_INPUT; + p = *in + *in_len; + } + + *in_len -= (p - *in); + *in = p; + return out - buf; +} + +static void mb_wchar_to_utf32le(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + + while (len--) { + uint32_t w = *in++; + if (w < MBFL_WCSPLANE_UTF32MAX) { + out = mb_convert_buf_add4(out, w & 0xFF, (w >> 8) & 0xFF, (w >> 16) & 0xFF, (w >> 24) & 0xFF); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf32le); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len * 4); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf7.c b/ext/mbstring/libmbfl/filters/mbfilter_utf7.c index c3144b4779584..7f843f88bb603 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf7.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf7.c @@ -31,6 +31,8 @@ #include "mbfilter_utf7.h" static int mbfl_filt_conv_utf7_wchar_flush(mbfl_convert_filter *filter); +static size_t mb_utf7_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf7(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); static const unsigned char mbfl_base64_table[] = { /* 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', */ @@ -55,7 +57,9 @@ const mbfl_encoding mbfl_encoding_utf7 = { NULL, MBFL_ENCTYPE_GL_UNSAFE, &vtbl_utf7_wchar, - &vtbl_wchar_utf7 + &vtbl_wchar_utf7, + mb_utf7_to_wchar, + mb_wchar_to_utf7 }; const struct mbfl_convert_vtbl vtbl_utf7_wchar = { @@ -401,3 +405,280 @@ int mbfl_filt_conv_wchar_utf7_flush(mbfl_convert_filter *filter) return 0; } + +/* Ways which a Base64-encoded section can end: */ +#define DASH 0xFD +#define ASCII 0xFE +#define ILLEGAL 0xFF + +static inline bool is_base64_end(unsigned char c) +{ + return c >= DASH; +} + +static unsigned char decode_base64(unsigned char c) +{ + if (c >= 'A' && c <= 'Z') { + return c - 65; + } else if (c >= 'a' && c <= 'z') { + return c - 71; + } else if (c >= '0' && c <= '9') { + return c + 4; + } else if (c == '+') { + return 62; + } else if (c == '/') { + return 63; + } else if (c == '-') { + return DASH; + } else if (c <= 0x7F) { + return ASCII; + } + return ILLEGAL; +} + +static uint32_t* handle_utf16_cp(uint16_t cp, uint32_t *out, uint16_t *surrogate1) +{ +retry: + if (*surrogate1) { + if (cp >= 0xDC00 && cp <= 0xDFFF) { + *out++ = ((*surrogate1 & 0x3FF) << 10) + (cp & 0x3FF) + 0x10000; + *surrogate1 = 0; + } else { + *out++ = MBFL_BAD_INPUT; + *surrogate1 = 0; + goto retry; + } + } else if (cp >= 0xD800 && cp <= 0xDBFF) { + *surrogate1 = cp; + } else if (cp >= 0xDC00 && cp <= 0xDFFF) { + /* 2nd part of surrogate pair came unexpectedly */ + *out++ = MBFL_BAD_INPUT; + } else { + *out++ = cp; + } + return out; +} + +static uint32_t* handle_base64_end(unsigned char n, unsigned char **p, uint32_t *out, bool *base64, bool abrupt, uint16_t *surrogate1) +{ + if (abrupt || *surrogate1) { + *out++ = MBFL_BAD_INPUT; + *surrogate1 = 0; + } + + if (n == ILLEGAL) { + *out++ = MBFL_BAD_INPUT; + } else if (n == ASCII) { + (*p)--; /* Unconsume byte */ + } + + *base64 = false; + return out; +} + +static size_t mb_utf7_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + ZEND_ASSERT(bufsize >= 4); /* This function will infinite-loop if called with a tiny output buffer */ + + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + bool base64 = *state & 1; + uint16_t surrogate1 = (*state >> 1); /* First half of a surrogate pair which still needs 2nd half */ + + while (p < e && out < limit) { + if (base64) { + /* Base64 section */ + if ((limit - out) < 4) { + break; + } + + unsigned char n1 = decode_base64(*p++); + if (is_base64_end(n1)) { + out = handle_base64_end(n1, &p, out, &base64, false, &surrogate1); + continue; + } else if (p == e) { + out = handle_base64_end(n1, &p, out, &base64, true, &surrogate1); + continue; + } + unsigned char n2 = decode_base64(*p++); + if (is_base64_end(n2) || p == e) { + out = handle_base64_end(n2, &p, out, &base64, true, &surrogate1); + continue; + } + unsigned char n3 = decode_base64(*p++); + if (is_base64_end(n3)) { + out = handle_base64_end(n3, &p, out, &base64, true, &surrogate1); + continue; + } + out = handle_utf16_cp((n1 << 10) | (n2 << 4) | ((n3 & 0x3C) >> 2), out, &surrogate1); + if (p == e) { + /* It is an error if trailing padding bits are not zeroes or if we were + * expecting the 2nd part of a surrogate pair when Base64 section ends */ + if ((n3 & 0x3) || surrogate1) + *out++ = MBFL_BAD_INPUT; + break; + } + + unsigned char n4 = decode_base64(*p++); + if (is_base64_end(n4) || p == e) { + out = handle_base64_end(n4, &p, out, &base64, n3 & 0x3, &surrogate1); + continue; + } + unsigned char n5 = decode_base64(*p++); + if (is_base64_end(n5) || p == e) { + out = handle_base64_end(n5, &p, out, &base64, true, &surrogate1); + continue; + } + unsigned char n6 = decode_base64(*p++); + if (is_base64_end(n6)) { + out = handle_base64_end(n6, &p, out, &base64, true, &surrogate1); + continue; + } + out = handle_utf16_cp((n3 << 14) | (n4 << 8) | (n5 << 2) | ((n6 & 0x30) >> 4), out, &surrogate1); + if (p == e) { + if ((n6 & 0xF) || surrogate1) + *out++ = MBFL_BAD_INPUT; + break; + } + + unsigned char n7 = decode_base64(*p++); + if (is_base64_end(n7) || p == e) { + out = handle_base64_end(n7, &p, out, &base64, n6 & 0xF, &surrogate1); + continue; + } + unsigned char n8 = decode_base64(*p++); + if (is_base64_end(n8)) { + out = handle_base64_end(n8, &p, out, &base64, true, &surrogate1); + continue; + } + out = handle_utf16_cp((n6 << 12) | (n7 << 6) | n8, out, &surrogate1); + } else { + /* ASCII text section */ + unsigned char c = *p++; + + if (c == '+') { + if (p < e) { + if (*p == '-') { + *out++ = '+'; + p++; + } else { + base64 = true; + } + } + /* If a + comes at the end of the input string... do nothing about it */ + } else if (c <= 0x7F) { + *out++ = c; + } else { + *out++ = MBFL_BAD_INPUT; + } + } + } + + *state = (surrogate1 << 1) | base64; + *in_len = e - p; + *in = p; + return out - buf; +} + +static bool can_end_base64(uint32_t c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\'' || c == '(' || c == ')' || c == ',' || c == '.' || c == ':' || c == '?'; +} + +static bool should_direct_encode(uint32_t c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '\0' || c == '/' || c == '-' || can_end_base64(c); +} + +#define SAVE_CONVERSION_STATE() buf->state = (cache << 4) | (nbits << 1) | base64 +#define RESTORE_CONVERSION_STATE() base64 = (buf->state & 1); nbits = (buf->state >> 1) & 0x7; cache = (buf->state >> 4) + +static void mb_wchar_to_utf7(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + + /* Make enough space such that if the input string is all ASCII (not including '+'), + * we can copy it to the output buffer without checking for available space. + * However, if we find anything which is not plain ASCII, additional checks for + * output buffer space will be needed. */ + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + bool base64; + unsigned char nbits, cache; /* `nbits` is the number of cached bits; either 0, 2, or 4 */ + RESTORE_CONVERSION_STATE(); + + while (len--) { + uint32_t w = *in++; + if (base64) { + if (should_direct_encode(w)) { + /* End of Base64 section. Drain buffered bits (if any), close Base64 section */ + base64 = false; + in--; len++; /* Unconsume codepoint; it will be handled by 'ASCII section' code below */ + MB_CONVERT_BUF_ENSURE(buf, out, limit, len + 2); + if (nbits) { + out = mb_convert_buf_add(out, mbfl_base64_table[(cache << (6 - nbits)) & 0x3F]); + } + nbits = cache = 0; + if (!can_end_base64(w)) { + out = mb_convert_buf_add(out, '-'); + } + } else if (w >= MBFL_WCSPLANE_UTF32MAX) { + /* Make recursive call to add an error marker character */ + SAVE_CONVERSION_STATE(); + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf7); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + RESTORE_CONVERSION_STATE(); + } else { + /* Encode codepoint, preceded by any cached bits, as Base64 */ + uint64_t bits; + if (w >= MBFL_WCSPLANE_SUPMIN) { + /* Must use surrogate pair */ + MB_CONVERT_BUF_ENSURE(buf, out, limit, 6); + w -= 0x10000; + bits = ((uint64_t)cache << 32) | 0xD800DC00L | ((w & 0xFFC00) << 6) | (w & 0x3FF); + nbits += 32; + } else { + MB_CONVERT_BUF_ENSURE(buf, out, limit, 3); + bits = (cache << 16) | w; + nbits += 16; + } + + while (nbits >= 6) { + out = mb_convert_buf_add(out, mbfl_base64_table[(bits >> (nbits - 6)) & 0x3F]); + nbits -= 6; + } + cache = bits; + } + } else { + /* ASCII section */ + if (should_direct_encode(w)) { + out = mb_convert_buf_add(out, w); + } else if (w >= MBFL_WCSPLANE_UTF32MAX) { + buf->state = 0; + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf7); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + RESTORE_CONVERSION_STATE(); + } else { + out = mb_convert_buf_add(out, '+'); + base64 = true; + in--; len++; /* Unconsume codepoint; it will be handled by Base64 code above */ + } + } + } + + if (end) { + if (nbits) { + out = mb_convert_buf_add(out, mbfl_base64_table[(cache << (6 - nbits)) & 0x3F]); + } + if (base64) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, 1); + out = mb_convert_buf_add(out, '-'); + } + } else { + SAVE_CONVERSION_STATE(); + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf7imap.c b/ext/mbstring/libmbfl/filters/mbfilter_utf7imap.c index 473a1f56c1396..7de68dea3cc22 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf7imap.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf7imap.c @@ -91,7 +91,9 @@ const mbfl_encoding mbfl_encoding_utf7imap = { NULL, 0, &vtbl_utf7imap_wchar, - &vtbl_wchar_utf7imap + &vtbl_wchar_utf7imap, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_utf7imap_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf8.c b/ext/mbstring/libmbfl/filters/mbfilter_utf8.c index 7c371d2e4d138..83d2158c37d10 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf8.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf8.c @@ -49,6 +49,9 @@ const unsigned char mblen_table_utf8[] = { 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +static size_t mb_utf8_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state); +static void mb_wchar_to_utf8(uint32_t *in, size_t len, mb_convert_buf *buf, bool end); + static const char *mbfl_encoding_utf8_aliases[] = {"utf8", NULL}; const mbfl_encoding mbfl_encoding_utf8 = { @@ -59,7 +62,9 @@ const mbfl_encoding mbfl_encoding_utf8 = { mblen_table_utf8, 0, &vtbl_utf8_wchar, - &vtbl_wchar_utf8 + &vtbl_wchar_utf8, + mb_utf8_to_wchar, + mb_wchar_to_utf8 }; const struct mbfl_convert_vtbl vtbl_utf8_wchar = { @@ -216,3 +221,157 @@ int mbfl_filt_conv_wchar_utf8(int c, mbfl_convert_filter *filter) return 0; } + +static size_t mb_utf8_to_wchar(unsigned char **in, size_t *in_len, uint32_t *buf, size_t bufsize, unsigned int *state) +{ + unsigned char *p = *in, *e = p + *in_len; + uint32_t *out = buf, *limit = buf + bufsize; + + while (p < e && out < limit) { + unsigned char c = *p++; + + if (c < 0x80) { + *out++ = c; + } else if (c >= 0xC2 && c <= 0xDF) { /* 2 byte character */ + if (p < e) { + unsigned char c2 = *p++; + if ((c2 & 0xC0) != 0x80) { + *out++ = MBFL_BAD_INPUT; + if (c2 < 0x80 || (c2 >= 0xC2 && c2 <= 0xF4)) { + p--; + } + } else { + *out++ = ((c & 0x1F) << 6) | (c2 & 0x3F); + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } else if (c >= 0xE0 && c <= 0xEF) { /* 3 byte character */ + if ((e - p) >= 2) { + unsigned char c2 = *p++; + unsigned char c3 = *p++; + if ((c2 & 0xC0) != 0x80 || !((c2 >= 0x80 && c2 <= 0xBF) && ((c == 0xE0 && c2 >= 0xA0) || (c == 0xED && c2 < 0xA0) || (c > 0xE0 && c != 0xED)))) { + *out++ = MBFL_BAD_INPUT; + if (c2 < 0x80 || (c2 >= 0xC2 && c2 <= 0xF4)) { + p -= 2; + } else { + p--; + } + } else if ((c3 & 0xC0) != 0x80) { + *out++ = MBFL_BAD_INPUT; + if (c3 < 0x80 || (c3 >= 0xC2 && c3 <= 0xF4)) { + p--; + } + } else { + uint32_t decoded = ((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); + if (decoded >= 0xD800 && decoded <= 0xDFFF) { + *out++ = MBFL_BAD_INPUT; + } else { + *out++ = (decoded < 0x800) ? MBFL_BAD_INPUT : decoded; + } + } + } else { + *out++ = MBFL_BAD_INPUT; + /* Skip over some number of bytes to duplicate error-handling behavior of old implementation */ + while (p < e) { + c = *p; + if ((c & 0xC0) != 0x80) { + if (c >= 0x80 && (c < 0xC2 || c > 0xF4)) + p++; + break; + } + p++; + } + } + } else if (c >= 0xF0 && c <= 0xF4) { /* 4 byte character */ + if ((e - p) >= 3) { + unsigned char c2 = *p++; + unsigned char c3 = *p++; + unsigned char c4 = *p++; + if ((c2 & 0xC0) != 0x80) { + *out++ = MBFL_BAD_INPUT; + if (c2 < 0x80 || (c2 >= 0xC2 && c2 <= 0xF4)) { + p -= 3; + } else { + p -= 2; + } + } else if ((c3 & 0xC0) != 0x80 || !((c == 0xF0 && c2 >= 0x90) || (c == 0xF4 && c2 < 0x90) || (c > 0xF0 && c < 0xF4))) { + *out++ = MBFL_BAD_INPUT; + if (!((c == 0xF0 && c2 >= 0x90) || (c == 0xF4 && c2 < 0x90) || (c > 0xF0 && c < 0xF4))) { + if (c2 >= 0x80 && (c2 < 0xC2 || c2 > 0xF4)) { + p -= 2; + } else { + p -= 3; + } + } else if (c3 < 0x80 || (c3 >= 0xC2 && c3 <= 0xF4)) { + p -= 2; + } else { + p--; + } + } else if ((c4 & 0xC0) != 0x80) { + *out++ = MBFL_BAD_INPUT; + if (c4 < 0x80 || (c4 >= 0xC2 && c4 <= 0xF4)) { + p--; + } + } else { + uint32_t decoded = ((c & 0x7) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F); + *out++ = (decoded < 0x10000) ? MBFL_BAD_INPUT : decoded; + } + } else { + *out++ = MBFL_BAD_INPUT; + /* Skip over some number of bytes to duplicate error-handling behavior of old implementation */ + if (p < e) { + unsigned char c2 = *p; + if (!((c == 0xF0 && c2 >= 0x90) || (c == 0xF4 && c2 < 0x90) || (c > 0xF0 && c < 0xF4))) { + if (c2 >= 0x80 && (c2 < 0xC2 || c2 > 0xF4)) + p++; + } else { + while (p < e) { + c = *p; + if ((c & 0xC0) != 0x80) { + if (c >= 0x80 && (c < 0xC2 || c > 0xF4)) + p++; + break; + } + p++; + } + } + } + } + } else { + *out++ = MBFL_BAD_INPUT; + } + } + + *in_len = e - p; + *in = p; + return out - buf; +} + +static void mb_wchar_to_utf8(uint32_t *in, size_t len, mb_convert_buf *buf, bool end) +{ + unsigned char *out, *limit; + MB_CONVERT_BUF_LOAD(buf, out, limit); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + + while (len--) { + uint32_t w = *in++; + if (w < 0x80) { + out = mb_convert_buf_add(out, w & 0xFF); + } else if (w < 0x800) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, len + 2); + out = mb_convert_buf_add2(out, ((w >> 6) & 0x1F) | 0xC0, (w & 0x3F) | 0x80); + } else if (w < 0x10000) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, len + 3); + out = mb_convert_buf_add3(out, ((w >> 12) & 0xF) | 0xE0, ((w >> 6) & 0x3F) | 0x80, (w & 0x3F) | 0x80); + } else if (w < 0x110000) { + MB_CONVERT_BUF_ENSURE(buf, out, limit, len + 4); + out = mb_convert_buf_add4(out, ((w >> 18) & 0x7) | 0xF0, ((w >> 12) & 0x3F) | 0x80, ((w >> 6) & 0x3F) | 0x80, (w & 0x3F) | 0x80); + } else { + MB_CONVERT_ERROR(buf, out, limit, w, mb_wchar_to_utf8); + MB_CONVERT_BUF_ENSURE(buf, out, limit, len); + } + } + + MB_CONVERT_BUF_STORE(buf, out, limit); +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c b/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c index 14b0094592489..04af4c0939e24 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_utf8_mobile.c @@ -49,7 +49,9 @@ const mbfl_encoding mbfl_encoding_utf8_docomo = { mblen_table_utf8, 0, &vtbl_utf8_docomo_wchar, - &vtbl_wchar_utf8_docomo + &vtbl_wchar_utf8_docomo, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_utf8_kddi_a = { @@ -60,7 +62,9 @@ const mbfl_encoding mbfl_encoding_utf8_kddi_a = { mblen_table_utf8, 0, &vtbl_utf8_kddi_a_wchar, - &vtbl_wchar_utf8_kddi_a + &vtbl_wchar_utf8_kddi_a, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_utf8_kddi_b = { @@ -71,7 +75,9 @@ const mbfl_encoding mbfl_encoding_utf8_kddi_b = { mblen_table_utf8, 0, &vtbl_utf8_kddi_b_wchar, - &vtbl_wchar_utf8_kddi_b + &vtbl_wchar_utf8_kddi_b, + NULL, + NULL }; const mbfl_encoding mbfl_encoding_utf8_sb = { @@ -82,7 +88,9 @@ const mbfl_encoding mbfl_encoding_utf8_sb = { mblen_table_utf8, 0, &vtbl_utf8_sb_wchar, - &vtbl_wchar_utf8_sb + &vtbl_wchar_utf8_sb, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_utf8_docomo_wchar = { diff --git a/ext/mbstring/libmbfl/filters/mbfilter_uuencode.c b/ext/mbstring/libmbfl/filters/mbfilter_uuencode.c index d602274db62cf..9571131aad5b9 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_uuencode.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_uuencode.c @@ -38,6 +38,8 @@ const mbfl_encoding mbfl_encoding_uuencode = { NULL, MBFL_ENCTYPE_SBCS, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/mbfl/mbfilter.c b/ext/mbstring/libmbfl/mbfl/mbfilter.c index 508f942b287e8..1c93b77a9533b 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfilter.c +++ b/ext/mbstring/libmbfl/mbfl/mbfilter.c @@ -454,49 +454,39 @@ filter_count_output(int c, void *data) return 0; } -size_t -mbfl_strlen(const mbfl_string *string) +size_t mbfl_strlen(const mbfl_string *string) { - size_t len, n, k; - unsigned char *p; + size_t len = 0; const mbfl_encoding *encoding = string->encoding; - len = 0; if (encoding->flag & MBFL_ENCTYPE_SBCS) { len = string->len; } else if (encoding->flag & MBFL_ENCTYPE_WCS2) { len = string->len/2; } else if (encoding->flag & MBFL_ENCTYPE_WCS4) { len = string->len/4; - } else if (encoding->mblen_table != NULL) { + } else if (encoding->mblen_table) { const unsigned char *mbtab = encoding->mblen_table; - n = 0; - p = string->val; - k = string->len; - /* count */ - if (p != NULL) { - while (n < k) { - unsigned m = mbtab[*p]; - n += m; - p += m; - len++; - } + unsigned char *p = string->val, *e = p + string->len; + while (p < e) { + p += mbtab[*p]; + len++; + } + } else if (encoding->to_wchar) { + uint32_t wchar_buf[128]; + unsigned char *in = string->val; + size_t in_len = string->len; + unsigned int state = 0; + + while (in_len) { + len += encoding->to_wchar(&in, &in_len, wchar_buf, 128, &state); } } else { - /* wchar filter */ - mbfl_convert_filter *filter = mbfl_convert_filter_new( - string->encoding, - &mbfl_encoding_wchar, - filter_count_output, 0, &len); + mbfl_convert_filter *filter = mbfl_convert_filter_new(string->encoding, &mbfl_encoding_wchar, filter_count_output, 0, &len); ZEND_ASSERT(filter); - /* count */ - n = string->len; - p = string->val; - if (p != NULL) { - while (n > 0) { - (*filter->filter_function)(*p++, filter); - n--; - } + unsigned char *p = string->val, *e = p + string->len; + while (p < e) { + (*filter->filter_function)(*p++, filter); } mbfl_convert_filter_delete(filter); } @@ -1232,7 +1222,7 @@ mbfl_strcut( /* Some East Asian characters, when printed at a terminal (or the like), require double * the usual amount of horizontal space. We call these "fullwidth" characters. */ -static size_t character_width(int c) +static size_t character_width(unsigned int c) { if (c < FIRST_DOUBLEWIDTH_CODEPOINT) { return 1; @@ -1260,35 +1250,44 @@ static int filter_count_width(int c, void* data) return 0; } -size_t -mbfl_strwidth(mbfl_string *string) +size_t mbfl_strwidth(mbfl_string *string) { - size_t len, n; - unsigned char *p; - mbfl_convert_filter *filter; + if (!string->len) { + return 0; + } - len = 0; - if (string->len > 0 && string->val != NULL) { - /* wchar filter */ - filter = mbfl_convert_filter_new( - string->encoding, - &mbfl_encoding_wchar, - filter_count_width, 0, &len); + size_t width = 0; + + if (string->encoding->to_wchar) { + uint32_t wchar_buf[128]; + unsigned char *in = string->val; + size_t in_len = string->len; + unsigned int state = 0; + + while (in_len) { + size_t out_len = string->encoding->to_wchar(&in, &in_len, wchar_buf, 128, &state); + while (out_len) { + /* NOTE: 'bad input' marker will be counted as 1 unit of width + * If text conversion is performed with an ordinary ASCII character as + * the 'replacement character', this will give us the correct display width. */ + width += character_width(wchar_buf[--out_len]); + } + } + } else { + mbfl_convert_filter *filter = mbfl_convert_filter_new(string->encoding, &mbfl_encoding_wchar, filter_count_width, 0, &width); ZEND_ASSERT(filter); /* feed data */ - p = string->val; - n = string->len; - while (n > 0) { + unsigned char *p = string->val, *e = p + string->len; + while (p < e) { (*filter->filter_function)(*p++, filter); - n--; } mbfl_convert_filter_flush(filter); mbfl_convert_filter_delete(filter); } - return len; + return width; } diff --git a/ext/mbstring/libmbfl/mbfl/mbfilter_8bit.c b/ext/mbstring/libmbfl/mbfl/mbfilter_8bit.c index e348ddb69cc57..670b23da8cbb5 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfilter_8bit.c +++ b/ext/mbstring/libmbfl/mbfl/mbfilter_8bit.c @@ -47,7 +47,9 @@ const mbfl_encoding mbfl_encoding_8bit = { NULL, MBFL_ENCTYPE_SBCS, &vtbl_8bit_wchar, - &vtbl_wchar_8bit + &vtbl_wchar_8bit, + NULL, + NULL }; const struct mbfl_convert_vtbl vtbl_8bit_wchar = { diff --git a/ext/mbstring/libmbfl/mbfl/mbfilter_pass.c b/ext/mbstring/libmbfl/mbfl/mbfilter_pass.c index e43f746ecca45..3fb7e991141cd 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfilter_pass.c +++ b/ext/mbstring/libmbfl/mbfl/mbfilter_pass.c @@ -42,6 +42,8 @@ const mbfl_encoding mbfl_encoding_pass = { NULL, 0, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/mbfl/mbfilter_wchar.c b/ext/mbstring/libmbfl/mbfl/mbfilter_wchar.c index a2b22c9105acc..5472d792a83cb 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfilter_wchar.c +++ b/ext/mbstring/libmbfl/mbfl/mbfilter_wchar.c @@ -40,5 +40,7 @@ const mbfl_encoding mbfl_encoding_wchar = { NULL, MBFL_ENCTYPE_WCS4, NULL, + NULL, + NULL, NULL }; diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_convert.c b/ext/mbstring/libmbfl/mbfl/mbfl_convert.c index f6e860a35aead..dc76f67964edd 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_convert.c +++ b/ext/mbstring/libmbfl/mbfl/mbfl_convert.c @@ -347,3 +347,96 @@ int mbfl_filt_conv_common_flush(mbfl_convert_filter *filter) } return 0; } + +zend_string* mb_fast_convert(zend_string *str, const mbfl_encoding *from, const mbfl_encoding *to, uint32_t replacement_char, unsigned int error_mode, unsigned int *num_errors) +{ + uint32_t wchar_buf[128]; + unsigned char *in = (unsigned char*)ZSTR_VAL(str); + size_t in_len = ZSTR_LEN(str); + unsigned int state = 0; + + mb_convert_buf buf; + mb_convert_buf_init(&buf, in_len, replacement_char, error_mode); + + while (in_len) { + size_t out_len = from->to_wchar(&in, &in_len, wchar_buf, 128, &state); + to->from_wchar(wchar_buf, out_len, &buf, !in_len); + } + + *num_errors = buf.errors; + return mb_convert_buf_result(&buf); +} + +static uint32_t* convert_cp_to_hex(uint32_t cp, uint32_t *out) +{ + bool nonzero = false; + int shift = 28; + + while (shift >= 0) { + int n = (cp >> shift) & 0xF; + if (n || nonzero) { + nonzero = true; + *out++ = mbfl_hexchar_table[n]; + } + shift -= 4; + } + + if (!nonzero) { + /* No hex digits were output by above loop */ + *out++ = '0'; + } + + return out; +} + +static size_t mb_illegal_marker(uint32_t bad_cp, uint32_t *out, unsigned int err_mode, uint32_t replacement_char) +{ + uint32_t *start = out; + + if (bad_cp == MBFL_BAD_INPUT && err_mode != MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) { + *out++ = replacement_char; + } else { + switch (err_mode) { + case MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR: + *out++ = replacement_char; + break; + + case MBFL_OUTPUTFILTER_ILLEGAL_MODE_LONG: + out[0] = 'U'; + out[1] = '+'; + out = convert_cp_to_hex(bad_cp, &out[2]); + break; + + case MBFL_OUTPUTFILTER_ILLEGAL_MODE_ENTITY: + out[0] = '&'; out[1] = '#'; out[2] = 'x'; + out = convert_cp_to_hex(bad_cp, &out[3]); + *out++ = ';'; + break; + } + } + + return out - start; +} + +void mb_illegal_output(uint32_t bad_cp, mb_from_wchar_fn fn, mb_convert_buf* buf) +{ + buf->errors++; + + uint32_t temp[12]; + uint32_t repl_char = buf->replacement_char; + unsigned int err_mode = buf->error_mode; + + size_t len = mb_illegal_marker(bad_cp, temp, err_mode, repl_char); + + /* Avoid infinite loop if `fn` is not able to handle `repl_char` */ + if (err_mode == MBFL_OUTPUTFILTER_ILLEGAL_MODE_CHAR && repl_char != '?') { + buf->replacement_char = '?'; + } else { + buf->error_mode = MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE; + } + + fn(temp, len, buf, false); + + buf->replacement_char = repl_char; + buf->error_mode = err_mode; +} diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_convert.h b/ext/mbstring/libmbfl/mbfl/mbfl_convert.h index 563cdda1cfa6f..12c12589b0a57 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_convert.h +++ b/ext/mbstring/libmbfl/mbfl/mbfl_convert.h @@ -81,4 +81,7 @@ MBFLAPI extern int mbfl_filt_conv_common_flush(mbfl_convert_filter *filter); MBFLAPI extern void mbfl_convert_filter_devcat(mbfl_convert_filter *filter, mbfl_memory_device *src); MBFLAPI extern int mbfl_convert_filter_strcat(mbfl_convert_filter *filter, const unsigned char *p); +MBFLAPI extern zend_string* mb_fast_convert(zend_string *str, const mbfl_encoding *from, const mbfl_encoding *to, uint32_t replacement_char, unsigned int error_mode, unsigned int *num_errors); +MBFLAPI extern void mb_illegal_output(uint32_t bad_cp, mb_from_wchar_fn fn, mb_convert_buf* buf); + #endif /* MBFL_CONVERT_H */ diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h index daeace934c93b..9f6b1e4bf99f2 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h +++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h @@ -32,6 +32,7 @@ #define MBFL_ENCODING_H #include "mbfl_defs.h" +#include "zend.h" enum mbfl_no_encoding { mbfl_no_encoding_invalid = -1, @@ -129,10 +130,92 @@ struct mbfl_convert_vtbl { void (*filter_copy)(struct _mbfl_convert_filter *src, struct _mbfl_convert_filter *dest); }; -/* - * encoding - */ -typedef struct _mbfl_encoding { +typedef struct { + unsigned char *out; + unsigned char *limit; + uint32_t state; + uint32_t errors; + uint32_t replacement_char; + unsigned int error_mode; + zend_string *str; +} mb_convert_buf; + +typedef size_t (*mb_to_wchar_fn)(unsigned char **in, size_t *in_len, uint32_t *out, size_t out_len, unsigned int *state); +typedef void (*mb_from_wchar_fn)(uint32_t *in, size_t in_len, mb_convert_buf *out, bool end); + +static inline void mb_convert_buf_init(mb_convert_buf *buf, size_t initsize, uint32_t repl_char, unsigned int err_mode) +{ + buf->state = buf->errors = 0; + buf->str = emalloc(_ZSTR_STRUCT_SIZE(initsize)); + buf->out = (unsigned char*)ZSTR_VAL(buf->str); + buf->limit = buf->out + initsize; + buf->replacement_char = repl_char; + buf->error_mode = err_mode; +} + +#define MB_CONVERT_BUF_ENSURE(buf, out, limit, needed) \ + if ((limit - out) < (needed)) { \ + size_t oldsize = limit - (unsigned char*)ZSTR_VAL(buf->str); \ + size_t newsize = oldsize + MAX(oldsize >> 1, needed); \ + zend_string *newstr = erealloc(buf->str, _ZSTR_STRUCT_SIZE(newsize)); \ + out = (unsigned char*)ZSTR_VAL(newstr) + (out - (unsigned char*)ZSTR_VAL(buf->str)); \ + limit = (unsigned char*)ZSTR_VAL(newstr) + newsize; \ + buf->str = newstr; \ + } + +#define MB_CONVERT_BUF_STORE(buf, _out, _limit) buf->out = _out; buf->limit = _limit + +#define MB_CONVERT_BUF_LOAD(buf, _out, _limit) _out = buf->out; _limit = buf->limit + +#define MB_CONVERT_ERROR(buf, out, limit, bad_cp, conv_fn) \ + MB_CONVERT_BUF_STORE(buf, out, limit); \ + mb_illegal_output(bad_cp, conv_fn, buf); \ + MB_CONVERT_BUF_LOAD(buf, out, limit) + +static inline unsigned char* mb_convert_buf_add(unsigned char *out, char c) +{ + *out++ = c; + return out; +} + +static inline unsigned char* mb_convert_buf_add2(unsigned char *out, char c1, char c2) +{ + *out++ = c1; + *out++ = c2; + return out; +} + +static inline unsigned char* mb_convert_buf_add3(unsigned char *out, char c1, char c2, char c3) +{ + *out++ = c1; + *out++ = c2; + *out++ = c3; + return out; +} + +static inline unsigned char* mb_convert_buf_add4(unsigned char *out, char c1, char c2, char c3, char c4) +{ + *out++ = c1; + *out++ = c2; + *out++ = c3; + *out++ = c4; + return out; +} + +static inline zend_string* mb_convert_buf_result(mb_convert_buf *buf) +{ + ZEND_ASSERT(buf->out <= buf->limit); + zend_string *ret = buf->str; + /* See `zend_string_alloc` in zend_string.h */ + GC_SET_REFCOUNT(ret, 1); + GC_TYPE_INFO(ret) = GC_STRING; + ZSTR_H(ret) = 0; + ZSTR_LEN(ret) = buf->out - (unsigned char*)ZSTR_VAL(ret); + *(buf->out) = '\0'; + return ret; +} + +typedef struct { enum mbfl_no_encoding no_encoding; const char *name; const char *mime_name; @@ -141,6 +224,8 @@ typedef struct _mbfl_encoding { unsigned int flag; const struct mbfl_convert_vtbl *input_filter; const struct mbfl_convert_vtbl *output_filter; + mb_to_wchar_fn to_wchar; + mb_from_wchar_fn from_wchar; } mbfl_encoding; MBFLAPI extern const mbfl_encoding *mbfl_name2encoding(const char *name); diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 548ba79bf3fc6..bcf2ee51933db 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -2501,6 +2501,23 @@ MBSTRING_API HashTable *php_mb_convert_encoding_recursive(HashTable *input, cons } /* }}} */ +static void remove_non_encodings_from_elist(const mbfl_encoding **elist, size_t *size) +{ + /* mbstring supports some 'text encodings' which aren't really text encodings + * at all, but really 'byte encodings', like Base64, QPrint, and so on. + * These should never be returned by `mb_detect_encoding`. */ + int shift = 0; + for (int i = 0; i < *size; i++) { + const mbfl_encoding *encoding = elist[i]; + if (encoding->no_encoding <= mbfl_no_encoding_charset_min) { + shift++; /* Remove this encoding from the list */ + } else if (shift) { + elist[i - shift] = encoding; + } + } + *size -= shift; +} + /* {{{ Returns converted string in desired encoding */ PHP_FUNCTION(mb_convert_encoding) { @@ -2541,6 +2558,10 @@ PHP_FUNCTION(mb_convert_encoding) free_from_encodings = 0; } + if (num_from_encodings > 1) { + remove_non_encodings_from_elist(from_encodings, &num_from_encodings); + } + if (!num_from_encodings) { efree(ZEND_VOIDP(from_encodings)); zend_argument_value_error(3, "must specify at least one encoding"); @@ -2548,7 +2569,16 @@ PHP_FUNCTION(mb_convert_encoding) } if (input_str) { - /* new encoding */ + if (num_from_encodings == 1) { + const mbfl_encoding *from_encoding = from_encodings[0]; + if (from_encoding->to_wchar && to_encoding->from_wchar) { + unsigned int num_errors = 0; + RETVAL_STR(mb_fast_convert(input_str, from_encoding, to_encoding, MBSTRG(current_filter_illegal_substchar), MBSTRG(current_filter_illegal_mode), &num_errors)); + MBSTRG(illegalchars) += num_errors; + goto out; + } + } + size_t size; char *ret = php_mb_convert_encoding(ZSTR_VAL(input_str), ZSTR_LEN(input_str), to_encoding, from_encodings, num_from_encodings, &size); @@ -2566,6 +2596,7 @@ PHP_FUNCTION(mb_convert_encoding) RETVAL_ARR(tmp); } +out: if (free_from_encodings) { efree(ZEND_VOIDP(from_encodings)); } @@ -2674,23 +2705,6 @@ PHP_FUNCTION(mb_strtolower) } /* }}} */ -static void remove_non_encodings_from_elist(const mbfl_encoding **elist, size_t *size) -{ - /* mbstring supports some 'text encodings' which aren't really text encodings - * at all, but really 'byte encodings', like Base64, QPrint, and so on. - * These should never be returned by `mb_detect_encoding`. */ - int shift = 0; - for (int i = 0; i < *size; i++) { - const mbfl_encoding *encoding = elist[i]; - if (encoding->no_encoding <= mbfl_no_encoding_charset_min) { - shift++; /* Remove this encoding from the list */ - } else if (shift) { - elist[i - shift] = encoding; - } - } - *size -= shift; -} - /* {{{ Encodings of the given string is returned (as a string) */ PHP_FUNCTION(mb_detect_encoding) { diff --git a/ext/mbstring/tests/armscii8_encoding.phpt b/ext/mbstring/tests/armscii8_encoding.phpt index 37af3820e9206..97006e09a6510 100644 --- a/ext/mbstring/tests/armscii8_encoding.phpt +++ b/ext/mbstring/tests/armscii8_encoding.phpt @@ -33,6 +33,10 @@ mb_substitute_character("long"); convertInvalidString("\xA1", "%", "ARMSCII-8", "UTF-8"); convertInvalidString("\xFF", "%", "ARMSCII-8", "UTF-8"); +// Test replacement character which cannot be encoded in ARMSCII-8 +mb_substitute_character(0x1234); +convertInvalidString("\x23\x45", '?', 'UTF-16BE', 'ARMSCII-8'); + echo "Done!\n"; ?> --EXPECT-- diff --git a/ext/mbstring/tests/bug65045.phpt b/ext/mbstring/tests/bug65045.phpt index cf273627c61da..772533b7e13f8 100644 --- a/ext/mbstring/tests/bug65045.phpt +++ b/ext/mbstring/tests/bug65045.phpt @@ -5,25 +5,21 @@ mbstring --FILE-- --EXPECT-- -bool(true) -bool(true) -bool(true) -bool(true) +efbfbdf0a4ada2f0a4ada2 +f0a4ada2f0a4ada2efbfbd +efbfbdf0a4ada2f0a4ada2 +f0a4ada2f0a4ada2efbfbd diff --git a/ext/mbstring/tests/cp1252_encoding.phpt b/ext/mbstring/tests/cp1252_encoding.phpt index 706e02ceee606..0688fe943134e 100644 --- a/ext/mbstring/tests/cp1252_encoding.phpt +++ b/ext/mbstring/tests/cp1252_encoding.phpt @@ -16,6 +16,10 @@ mb_substitute_character("long"); convertInvalidString("\x81", "%", "CP1252", "UTF-8"); convertInvalidString("\x9D", "%", "CP1252", "UTF-8"); +// Test replacement character which cannot be encoded in CP1252 +mb_substitute_character(0x1234); +convertInvalidString("\x23\x45", '?', 'UTF-16BE', 'CP1252'); + echo "Done!\n"; ?> --EXPECT-- diff --git a/ext/mbstring/tests/cp1254_encoding.phpt b/ext/mbstring/tests/cp1254_encoding.phpt index 7a4f7be2b18ce..08d36ca7e9964 100644 --- a/ext/mbstring/tests/cp1254_encoding.phpt +++ b/ext/mbstring/tests/cp1254_encoding.phpt @@ -16,6 +16,10 @@ mb_substitute_character("long"); convertInvalidString("\x81", "%", "CP1254", "UTF-8"); convertInvalidString("\x9E", "%", "CP1254", "UTF-8"); +// Test replacement character which cannot be encoded in CP1254 +mb_substitute_character(0x1234); +convertInvalidString("\x23\x45", '?', 'UTF-16BE', 'CP1254'); + echo "Done!\n"; ?> --EXPECT-- diff --git a/ext/mbstring/tests/cp850_encoding.phpt b/ext/mbstring/tests/cp850_encoding.phpt index 01e78c4a2a0b3..726e12f2a8479 100644 --- a/ext/mbstring/tests/cp850_encoding.phpt +++ b/ext/mbstring/tests/cp850_encoding.phpt @@ -9,7 +9,11 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); --FILE-- +testEncodingFromUTF16ConversionTable(__DIR__ . '/data/CP850.txt', 'CP850'); +/* Try replacement character which cannot be encoded in CP850; ? will be used instead */ +mb_substitute_character(0x1234); +convertInvalidString("\x23\x45", '?', 'UTF-16BE', 'CP850'); +?> --EXPECT-- Tested CP850 -> UTF-16BE Tested UTF-16BE -> CP850 diff --git a/ext/mbstring/tests/cp866_encoding.phpt b/ext/mbstring/tests/cp866_encoding.phpt index e6454805883e9..67e47ba543595 100644 --- a/ext/mbstring/tests/cp866_encoding.phpt +++ b/ext/mbstring/tests/cp866_encoding.phpt @@ -10,6 +10,9 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); --EXPECT-- Tested CP866 -> UTF-16BE diff --git a/ext/mbstring/tests/encoding_tests.inc b/ext/mbstring/tests/encoding_tests.inc index f6fab024ca3d9..cbf5071456df2 100644 --- a/ext/mbstring/tests/encoding_tests.inc +++ b/ext/mbstring/tests/encoding_tests.inc @@ -102,6 +102,14 @@ function testInvalidString($fromString, $toString, $fromEncoding, $toEncoding) { function testAllValidChars($charMap, $fromEncoding, $toEncoding, $bothWays = true) { $goodChars = array_keys($charMap); shuffle($goodChars); + // Try a long string + $fromString = $toString = ''; + foreach ($goodChars as $goodChar) { + $fromString .= $goodChar; + $toString .= $charMap[$goodChar]; + } + testValidString($fromString, $toString, $fromEncoding, $toEncoding, $bothWays); + // Try various shorter ones while (!empty($goodChars)) { $length = min(rand(5,10), count($goodChars)); $fromString = $toString = ''; diff --git a/ext/mbstring/tests/illformed_utf_sequences.phpt b/ext/mbstring/tests/illformed_utf_sequences.phpt index a9c97cce118f8..b29827be44d6f 100644 --- a/ext/mbstring/tests/illformed_utf_sequences.phpt +++ b/ext/mbstring/tests/illformed_utf_sequences.phpt @@ -16,7 +16,6 @@ function chk_enc($str, $n, $enc = "UTF-8") { mb_substitute_character(0xfffd); - echo "UTF-8 redundancy\n"; var_dump(chk_enc("\x31\x32\x33", 0)); var_dump(chk_enc("\x41\x42\x43", 0)); diff --git a/ext/mbstring/tests/iso2022jp_encoding.phpt b/ext/mbstring/tests/iso2022jp_encoding.phpt index 4858f45e41e5b..ea515cb436f85 100644 --- a/ext/mbstring/tests/iso2022jp_encoding.phpt +++ b/ext/mbstring/tests/iso2022jp_encoding.phpt @@ -204,6 +204,7 @@ foreach (['JIS', 'ISO-2022-JP'] as $encoding) { testValidString("\x00\xA5", "\x1B(J\x5C\x1B(B", 'UTF-16BE', $encoding, false); } +testValidString("\x20\x3E", "\x1B\$B!1\x1B(B", 'UTF-16BE', 'ISO-2022-JP', false); echo "Other mappings from Unicode -> ISO-2022-JP are OK\n"; diff --git a/ext/mbstring/tests/iso8859_encodings.phpt b/ext/mbstring/tests/iso8859_encodings.phpt index fac7ce8c0eac9..7aae113c85717 100644 --- a/ext/mbstring/tests/iso8859_encodings.phpt +++ b/ext/mbstring/tests/iso8859_encodings.phpt @@ -21,6 +21,10 @@ mb_substitute_character("long"); convertInvalidString("\xAE", "%", "ISO8859-7", "UTF-8"); convertInvalidString("\xFF", "%", "ISO8859-8", "UTF-8"); +// Test illegal character marker which can't be represented in target encoding +mb_substitute_character(0x1234); +convertInvalidString("\x23\x45", '?', 'UTF-16BE', 'ISO8859-7'); + echo "Done!\n"; ?> --EXPECT-- diff --git a/ext/mbstring/tests/koi8r_encoding.phpt b/ext/mbstring/tests/koi8r_encoding.phpt index ab5430150cdb1..5c4f1657ee923 100644 --- a/ext/mbstring/tests/koi8r_encoding.phpt +++ b/ext/mbstring/tests/koi8r_encoding.phpt @@ -10,6 +10,9 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); --EXPECT-- Tested KOI8-R -> UTF-16BE diff --git a/ext/mbstring/tests/koi8u_encoding.phpt b/ext/mbstring/tests/koi8u_encoding.phpt index 4bf64ad781750..c28a640c1551e 100644 --- a/ext/mbstring/tests/koi8u_encoding.phpt +++ b/ext/mbstring/tests/koi8u_encoding.phpt @@ -10,6 +10,9 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); --EXPECT-- Tested KOI8-U -> UTF-16BE diff --git a/ext/mbstring/tests/mb_convert_encoding.phpt b/ext/mbstring/tests/mb_convert_encoding.phpt index 33bc50b613c10..bb460961e9ad8 100644 --- a/ext/mbstring/tests/mb_convert_encoding.phpt +++ b/ext/mbstring/tests/mb_convert_encoding.phpt @@ -9,33 +9,24 @@ mbstring.language=Japanese 'JIS', 1=>'UTF-8', 2=>'EUC-JP', 3=>'SJIS'); +$a = ['JIS', 'UTF-8', 'EUC-JP', 'SJIS']; $s = $jis; $s = bin2hex(mb_convert_encoding($s, 'EUC-JP', $a)); print("EUC-JP: $s\n"); // EUC-JP @@ -69,6 +59,8 @@ $s = $euc_jp; $s = mb_convert_encoding($s, 'JIS', $a); print("JIS: ".base64_encode($s)."\n"); // JIS +// Regression test for bug #81676 +echo "UTF-8: " . mb_convert_encoding('test', 'UTF-8', mb_list_encodings()), "\n"; // Using Detect Order echo "== DETECT ORDER ==\n"; @@ -122,6 +114,7 @@ JIS: GyRCRnxLXDhsJUYlLSU5JUgkRyQ5ISMbKEIwMTIzNBskQiM1IzYjNyM4IzkhIxsoQg== EUC-JP: c6fccbdcb8eca5c6a5ada5b9a5c8a4c7a4b9a1a33031323334a3b5a3b6a3b7a3b8a3b9a1a3 SJIS: k/qWe4zqg2WDTINYg2eCxYK3gUIwMTIzNIJUglWCVoJXgliBQg== JIS: GyRCRnxLXDhsJUYlLSU5JUgkRyQ5ISMbKEIwMTIzNBskQiM1IzYjNyM4IzkhIxsoQg== +UTF-8: test == DETECT ORDER == EUC-JP: c6fccbdcb8eca5c6a5ada5b9a5c8a4c7a4b9a1a33031323334a3b5a3b6a3b7a3b8a3b9a1a3 SJIS: k/qWe4zqg2WDTINYg2eCxYK3gUIwMTIzNIJUglWCVoJXgliBQg== diff --git a/ext/mbstring/tests/mb_substitute_character_variation2.phpt b/ext/mbstring/tests/mb_substitute_character_variation2.phpt index 02d7a37e567e4..c4e0665992b51 100644 --- a/ext/mbstring/tests/mb_substitute_character_variation2.phpt +++ b/ext/mbstring/tests/mb_substitute_character_variation2.phpt @@ -5,7 +5,7 @@ mbstring --FILE-- 0xDFFF) // surrogates; included in UnicodeData.txt + if (($cp < 0xD800 || $cp > 0xDFFF) && $cp !== 0xFEFF) $validCodepoints[pack('N', $cp)] = true; } } @@ -858,7 +858,20 @@ testInvalidCodepoints($invalid, 'UTF-16LE'); testInvalidString("\x00", "\x00\x00\x00%", 'UTF-16LE', 'UTF-32BE'); testInvalidString("A\x00\x01", "\x00\x00\x00A\x00\x00\x00%", 'UTF-16LE', 'UTF-32BE'); -// TODO: test handling of UTF-16 BOM +// Test treatment of BOM +testValidString("\xFE\xFF\x12\x34", "\x00\x00\x12\x34", 'UTF-16', 'UTF-32BE', false); +testValidString("\xFF\xFE\x12\x34", "\x00\x00\x34\x12", 'UTF-16', 'UTF-32BE', false); + +// Test treatment of (illegal) codepoints between U+D800 and U+DFFF +testValidString("\xD8\x00", "\xD8\x00", 'UCS-2BE', 'UTF-16BE', false); +testValidString("\xDB\xFF", "\xDB\xFF", 'UCS-2BE', 'UTF-16BE', false); +testValidString("\xDC\x00", "\xDC\x00", 'UCS-2BE', 'UTF-16BE', false); +testValidString("\xD8\x00", "\x00\xD8", 'UCS-2BE', 'UTF-16LE', false); +testValidString("\xDC\x00", "\x00\xDC", 'UCS-2BE', 'UTF-16LE', false); + +// Try codepoint over U+10FFFF +convertInvalidString("\x00\x11\x56\x78", "\x00%", 'UCS-4BE', 'UTF-16BE'); +convertInvalidString("\x00\x11\x56\x78", "%\x00", 'UCS-4BE', 'UTF-16LE'); echo "== UTF-32 ==\n"; @@ -914,7 +927,16 @@ testInvalidString("\x00\x01\x01", "\x00\x00\x00%", 'UTF-32LE', 'UTF-32BE'); testInvalidString("\x00\x01", "\x00\x00\x00%", 'UTF-32LE', 'UTF-32BE'); testInvalidString("\x00", "\x00\x00\x00%", 'UTF-32LE', 'UTF-32BE'); -// TODO: test handling of UTF-32 BOM +// Test treatment of BOM +testValidString("\x00\x00\xFE\xFF\x00\x00\x12\x34", "\x00\x00\x12\x34", 'UTF-32', 'UTF-32BE', false); +testValidString("\xFF\xFE\x00\x00\x12\x34\x00\x00", "\x00\x00\x34\x12", 'UTF-32', 'UTF-32BE', false); + +// Test treatment of (illegal) codepoints between U+D800 and U+DFFF +testValidString("\xD8\x00", "\x00\x00\xD8\x00", 'UCS-2BE', 'UTF-32BE', false); +testValidString("\xDB\xFF", "\x00\x00\xDB\xFF", 'UCS-2BE', 'UTF-32BE', false); +testValidString("\xDC\x00", "\x00\x00\xDC\x00", 'UCS-2BE', 'UTF-32BE', false); +testValidString("\xD8\x00", "\x00\xD8\x00\x00", 'UCS-2BE', 'UTF-32LE', false); +testValidString("\xDC\x00", "\x00\xDC\x00\x00", 'UCS-2BE', 'UTF-32LE', false); echo "== UTF-7 ==\n"; @@ -1031,6 +1053,7 @@ testInvalidString('+' . rawEncode("\xD8\x01"), "\x00\x00\x00%", 'UTF-7', 'UTF-32 // Truncated string testInvalidString('+' . rawEncode("\x01") . '-', "\x00\x00\x00%", 'UTF-7', 'UTF-32BE'); +testInvalidString('+l', "\x00\x00\x00%", 'UTF-7', 'UTF-32BE'); // And then, messed up Base64 encoding @@ -1042,6 +1065,15 @@ testInvalidString('+' . $corrupted . '-', "\x00\x00\x12\x34\x00\x00\x00%", 'UTF- // Characters which are not Base64 (and not even ASCII) appearing in Base64 section testInvalidString("+\x80", "\x00\x00\x00%", 'UTF-7', 'UTF-32BE'); +// Try codepoint over U+10FFFF; '+ACU-' is the error marker '%' +convertInvalidString("\x12\x34\x56\x78", "+ACU-", 'UCS-4BE', 'UTF-7'); +convertInvalidString("\x00\x11\x56\x78", "+ACU-", 'UCS-4BE', 'UTF-7'); + +// If error marker character needs to be ASCII-encoded but is able to serve as an +// ending character for a Base64 section, no need to add an additional dash +mb_substitute_character(0x3F); // ? +convertInvalidString("\x1E\xBE", '+AB4?', 'UTF-7', 'UTF-7'); + echo "Done!\n"; ?> diff --git a/ext/oci8/config.m4 b/ext/oci8/config.m4 index 89da625e27b0e..714c655be439d 100644 --- a/ext/oci8/config.m4 +++ b/ext/oci8/config.m4 @@ -211,7 +211,13 @@ if test "$PHP_OCI8" != "no"; then oci8_php_version=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3` if test "$oci8_php_version" -lt "5002000"; then - AC_MSG_ERROR([You need at least PHP 5.2.0 to be able to use this version of OCI8. PHP $php_version found]) + AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 1.4 for PHP $php_version]) + elif test "$oci8_php_version" -lt "7000000"; then + AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 2.0 for PHP $php_version]) + elif test "$oci8_php_version" -lt "8000000"; then + AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 2.2 for PHP $php_version]) + elif test "$oci8_php_version" -lt "8001000"; then + AC_MSG_ERROR([You need at least PHP 8.1.0 to be able to use this version of OCI8. Use OCI8 3.0 for PHP $php_version]) else AC_MSG_RESULT([$php_version, ok]) fi @@ -334,8 +340,8 @@ if test "$PHP_OCI8" != "no"; then AC_OCI8_ORACLE_VERSION($OCI8_DIR) case $OCI8_ORACLE_VERSION in - 7.3|8.0|8.1|9.0) - AC_MSG_ERROR([Oracle client libraries < 10 are not supported]) + 7.3|8.0|8.1|9.0|10.1) + AC_MSG_ERROR([Oracle Client libraries < 11.2 are not supported]) ;; esac @@ -407,6 +413,11 @@ if test "$PHP_OCI8" != "no"; then fi AC_OCI8IC_VERSION($PHP_OCI8_INSTANT_CLIENT) + case $OCI8_ORACLE_VERSION in + 10.1) + AC_MSG_ERROR([Oracle Client libraries < 11.2 are not supported]) + ;; + esac PHP_ADD_LIBRARY(clntsh, 1, OCI8_SHARED_LIBADD) PHP_ADD_LIBPATH($PHP_OCI8_INSTANT_CLIENT, OCI8_SHARED_LIBADD) diff --git a/ext/oci8/config.w32 b/ext/oci8/config.w32 index 5f0da8f33d2d8..8d17878c77644 100644 --- a/ext/oci8/config.w32 +++ b/ext/oci8/config.w32 @@ -1,19 +1,5 @@ // vim:ft=javascript -if (PHP_OCI8 != "no" && PHP_OCI8_11G != "no") { - if (!PHP_OCI8_SHARED && !PHP_OCI8_11G_SHARED) { - WARNING("oci8 and oci8-11g provide the same extension and cannot both be built statically"); - PHP_OCI8 = "no" - } -} - -if (PHP_OCI8 != "no" && PHP_OCI8_12C != "no") { - if (!PHP_OCI8_SHARED && !PHP_OCI8_12C_SHARED) { - WARNING("oci8 and oci8-12c provide the same extension and cannot both be built statically"); - PHP_OCI8 = "no" - } -} - if (PHP_OCI8_11G != "no" && PHP_OCI8_12C != "no") { if (!PHP_OCI8_11G_SHARED && !PHP_OCI8_12C_SHARED) { WARNING("oci8-11g and oci8-12c provide the same extension and cannot both be built statically"); @@ -21,13 +7,6 @@ if (PHP_OCI8_11G != "no" && PHP_OCI8_12C != "no") { } } -if (PHP_OCI8 != "no" && PHP_OCI8_19 != "no") { - if (!PHP_OCI8_SHARED && !PHP_OCI8_19_SHARED) { - WARNING("oci8 and oci8-19 provide the same extension and cannot both be built statically"); - PHP_OCI8 = "no" - } -} - if (PHP_OCI8_11G != "no" && PHP_OCI8_19 != "no") { if (!PHP_OCI8_11G_SHARED && !PHP_OCI8_19_SHARED) { WARNING("oci8-11g and oci8-19 provide the same extension and cannot both be built statically"); @@ -42,41 +21,6 @@ if (PHP_OCI8_12C != "no" && PHP_OCI8_19 != "no") { } } -ARG_WITH("oci8", "OCI8 support", "no"); - -if (PHP_OCI8 != "no") { - - oci8_dirs = new Array( - PHP_OCI8 - ); - - oci8_lib_paths = ""; - oci8_inc_paths = ""; - - // find the Oracle install - for (i = 0; i < oci8_dirs.length; i++) { - oci8_lib_paths += oci8_dirs[i] + "\\lib;"; - oci8_lib_paths += oci8_dirs[i] + "\\lib\\msvc;"; - oci8_inc_paths += oci8_dirs[i] + "\\include;"; - } - - oci8_inc_paths += PHP_PHP_BUILD + "\\include\\instantclient;" - oci8_lib_paths += PHP_PHP_BUILD + "\\lib\\instantclient;"; - - if (CHECK_HEADER_ADD_INCLUDE("oci.h", "CFLAGS_OCI8", oci8_inc_paths) && - CHECK_LIB("oci.lib", "oci8", oci8_lib_paths)) - { - EXTENSION('oci8', 'oci8.c oci8_lob.c oci8_statement.c oci8_collection.c oci8_interface.c oci8_failover.c'); - - AC_DEFINE('HAVE_OCI8', 1); - AC_DEFINE('HAVE_OCI_INSTANT_CLIENT', 1); - - } else { - WARNING("oci8 not enabled: Oracle Database client libraries or Oracle 10g Instant Client not found"); - PHP_OCI8 = "no" - } -} - ARG_WITH("oci8-11g", "OCI8 support using Oracle 11g Instant Client", "no"); if (PHP_OCI8_11G != "no") { @@ -164,8 +108,8 @@ if (PHP_OCI8_19 != "no") { oci8_19_inc_paths += oci8_19_dirs[i] + "\\include;"; } - oci8_19_inc_paths += PHP_PHP_BUILD + "\\include\\instantclient_12;" - oci8_19_lib_paths += PHP_PHP_BUILD + "\\lib\\instantclient_12;"; + oci8_19_inc_paths += PHP_PHP_BUILD + "\\include\\instantclient_19;" + oci8_19_lib_paths += PHP_PHP_BUILD + "\\lib\\instantclient_19;"; if (CHECK_HEADER_ADD_INCLUDE("oci.h", "CFLAGS_OCI8_19", oci8_19_inc_paths) && CHECK_LIB("oci.lib", "oci8_19", oci8_19_lib_paths)) diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index 8efb04ea0f629..ac79c57f2b2a9 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -35,6 +35,10 @@ #ifdef HAVE_OCI8 +/* Note to maintainers: config.m4 should/does check the minimum PHP version so + * the below checks are obsolete - but are kept 'just in case'. Also bump the + * minimum version in package.xml, as appropriate. */ + /* PHP 5.2 is the minimum supported version for OCI8 2.0 */ #if PHP_MAJOR_VERSION < 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1) #error Use PHP OCI8 1.4 for your version of PHP @@ -42,8 +46,10 @@ /* PHP 7 is the minimum supported version for OCI8 2.1 */ #error Use PHP OCI8 2.0 for your version of PHP #elif PHP_MAJOR_VERSION < 8 -/* PHP 8 is the minimum supported version for OCI8 3.0 */ +/* PHP 8 is the minimum supported version for OCI8 3 */ #error Use PHP OCI8 2.2 for your version of PHP +#elif PHP_MAJOR_VERSION == 8 && PHP_MINOR_VERSION < 1 +#error Use PHP OCI8 3.0 for your version of PHP #endif #include "php_oci8.h" @@ -168,13 +174,9 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY( "oci8.statement_cache_size", "20", PHP_INI_SYSTEM, OnUpdateLong, statement_cache_size, zend_oci_globals, oci_globals) STD_PHP_INI_ENTRY( "oci8.default_prefetch", "100", PHP_INI_SYSTEM, OnUpdateLong, default_prefetch, zend_oci_globals, oci_globals) STD_PHP_INI_BOOLEAN("oci8.old_oci_close_semantics", "0", PHP_INI_SYSTEM, OnUpdateOldCloseSemantics, old_oci_close_semantics,zend_oci_globals, oci_globals) -#if (OCI_MAJOR_VERSION >= 11) STD_PHP_INI_ENTRY( "oci8.connection_class", "", PHP_INI_ALL, OnUpdateString, connection_class, zend_oci_globals, oci_globals) -#endif -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) STD_PHP_INI_BOOLEAN("oci8.events", "0", PHP_INI_SYSTEM, OnUpdateBool, events, zend_oci_globals, oci_globals) -#endif - STD_PHP_INI_ENTRY( "oci8.prefetch_lob_size", "0", PHP_INI_ALL, OnUpdateLong, prefetch_lob_size, zend_oci_globals, oci_globals) + STD_PHP_INI_ENTRY( "oci8.prefetch_lob_size", "0", PHP_INI_SYSTEM, OnUpdateLong, prefetch_lob_size, zend_oci_globals, oci_globals) PHP_INI_END() /* }}} */ @@ -211,23 +213,7 @@ static void php_oci_init_global_handles(void) errstatus = OCIHandleAlloc (OCI_G(env), (dvoid **)&OCI_G(err), OCI_HTYPE_ERROR, 0, NULL); - if (errstatus == OCI_SUCCESS) { -#if !defined(OCI_MAJOR_VERSION) || (OCI_MAJOR_VERSION < 11) - /* This fixes PECL bug 15988 (sqlnet.ora not being read). The - * root cause was fixed in Oracle 10.2.0.4 but there is no - * compile time method to check for that precise patch level, - * nor can it be guaranteed that runtime will use the same - * patch level the code was compiled with. So, we do this - * code for all non 11g versions. - */ - OCICPool *cpoolh; - ub4 cpoolmode = 0x80000000; /* Pass invalid mode to OCIConnectionPoolCreate */ - PHP_OCI_CALL(OCIHandleAlloc, (OCI_G(env), (dvoid **) &cpoolh, OCI_HTYPE_CPOOL, (size_t) 0, (dvoid **) 0)); - PHP_OCI_CALL(OCIConnectionPoolCreate, (OCI_G(env), OCI_G(err), cpoolh, NULL, 0, NULL, 0, 0, 0, 0, NULL, 0, NULL, 0, cpoolmode)); - PHP_OCI_CALL(OCIConnectionPoolDestroy, (cpoolh, OCI_G(err), OCI_DEFAULT)); - PHP_OCI_CALL(OCIHandleFree, (cpoolh, OCI_HTYPE_CPOOL)); -#endif - } else { + if (errstatus != OCI_SUCCESS) { OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ERROR); if (ora_error_code) { @@ -252,24 +238,6 @@ static void php_oci_init_global_handles(void) } /* }}} */ -/* {{{ php_oci_cleanup_global_handles() - * - * Free global handles (if they were initialized before) - */ -static void php_oci_cleanup_global_handles(void) -{ - if (OCI_G(err)) { - PHP_OCI_CALL(OCIHandleFree, ((dvoid *) OCI_G(err), OCI_HTYPE_ERROR)); - OCI_G(err) = NULL; - } - - if (OCI_G(env)) { - PHP_OCI_CALL(OCIHandleFree, ((dvoid *) OCI_G(env), OCI_HTYPE_ENV)); - OCI_G(env) = NULL; - } -} -/* }}} */ - /* {{{ PHP_GINIT_FUNCTION * * Zerofill globals during module init @@ -283,10 +251,23 @@ static PHP_GINIT_FUNCTION(oci) /* {{{ PHP_GSHUTDOWN_FUNCTION * * Called for thread shutdown in ZTS, after module shutdown for non-ZTS + * Free global handles (if they were initialized before) */ static PHP_GSHUTDOWN_FUNCTION(oci) { - php_oci_cleanup_global_handles(); + if (oci_globals->err) { + oci_globals->in_call = 1; + OCIHandleFree((dvoid *) oci_globals->err, OCI_HTYPE_ERROR); + oci_globals->in_call = 0; + oci_globals->err = NULL; + } + + if (oci_globals->env) { + oci_globals->in_call = 1; + OCIHandleFree((dvoid *) oci_globals->env, OCI_HTYPE_ENV); + oci_globals->in_call = 0; + oci_globals->env = NULL; + } } /* }}} */ @@ -343,11 +324,9 @@ PHP_MINIT_FUNCTION(oci) REGISTER_LONG_CONSTANT("SQLT_LBI",SQLT_LBI, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SQLT_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SQLT_ODT",SQLT_ODT, CONST_CS | CONST_PERSISTENT); -#if defined(HAVE_OCI_INSTANT_CLIENT) || (defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 10) REGISTER_LONG_CONSTANT("SQLT_BDOUBLE",SQLT_BDOUBLE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SQLT_BFLOAT",SQLT_BFLOAT, CONST_CS | CONST_PERSISTENT); -#endif -#if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12 +#if OCI_MAJOR_VERSION >= 12 REGISTER_LONG_CONSTANT("SQLT_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT); #endif @@ -364,7 +343,7 @@ PHP_MINIT_FUNCTION(oci) REGISTER_LONG_CONSTANT("OCI_B_BIN",SQLT_BIN, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("OCI_B_INT",SQLT_INT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("OCI_B_NUM",SQLT_NUM, CONST_CS | CONST_PERSISTENT); -#if defined(OCI_MAJOR_VERSION) && OCI_MAJOR_VERSION >= 12 +#if OCI_MAJOR_VERSION >= 12 REGISTER_LONG_CONSTANT("OCI_B_BOL",SQLT_BOL, CONST_CS | CONST_PERSISTENT); #endif @@ -445,9 +424,7 @@ PHP_RSHUTDOWN_FUNCTION(oci) PHP_MINFO_FUNCTION(oci) { char buf[32]; -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) char ver[256]; -#endif php_info_print_table_start(); php_info_print_table_row(2, "OCI8 Support", "enabled"); @@ -458,19 +435,9 @@ PHP_MINFO_FUNCTION(oci) #endif php_info_print_table_row(2, "OCI8 Version", PHP_OCI8_VERSION); -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) php_oci_client_get_version(ver, sizeof(ver)); php_info_print_table_row(2, "Oracle Run-time Client Library Version", ver); -#else - php_info_print_table_row(2, "Oracle Run-time Client Library Version", "Unknown"); -#endif -#if defined(OCI_MAJOR_VERSION) && defined(OCI_MINOR_VERSION) snprintf(buf, sizeof(buf), "%d.%d", OCI_MAJOR_VERSION, OCI_MINOR_VERSION); -#elif defined(PHP_OCI8_ORACLE_VERSION) - snprintf(buf, sizeof(buf), "%s", PHP_OCI8_ORACLE_VERSION); -#else - snprintf(buf, sizeof(buf), "Unknown"); -#endif #if defined(HAVE_OCI_INSTANT_CLIENT) php_info_print_table_row(2, "Oracle Compile-time Instant Client Version", buf); #else @@ -1337,23 +1304,14 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char static int php_oci_connection_ping(php_oci_connection *connection) { sword errstatus; -#if (!((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2)))) - char version[256]; -#endif OCI_G(errcode) = 0; /* assume ping is successful */ - /* Use OCIPing instead of OCIServerVersion. If OCIPing returns ORA-1010 (invalid OCI operation) - * such as from Pre-10.1 servers, the error is still from the server and we would have - * successfully performed a roundtrip and validated the connection. Use OCIServerVersion for - * Pre-10.2 clients + /* If OCIPing returns ORA-1010 (invalid OCI operation) such as from + * pre-10.1 servers, the error is still from the server and we would have + * successfully performed a roundtrip and validated the connection. */ -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIPing available 10.2 onwards */ PHP_OCI_CALL_RETURN(errstatus, OCIPing, (connection->svc, OCI_G(err), OCI_DEFAULT)); -#else - /* use good old OCIServerVersion() */ - PHP_OCI_CALL_RETURN(errstatus, OCIServerVersion, (connection->svc, OCI_G(err), (text *)version, sizeof(version), OCI_HTYPE_SVCCTX)); -#endif if (errstatus == OCI_SUCCESS) { return 1; @@ -1578,16 +1536,6 @@ int php_oci_connection_release(php_oci_connection *connection) rlsMode |= OCI_SESSRLS_DROPSESS; } - /* Sessions for non-persistent connections should be dropped. For 11 and above, the session - * pool has its own mechanism for doing so for purity NEW connections. We need to do so - * explicitly for 10.2 and earlier. - */ -#if (!(OCI_MAJOR_VERSION >= 11)) - if (!connection->is_persistent) { - rlsMode |= OCI_SESSRLS_DROPSESS; - } -#endif - if (connection->svc) { PHP_OCI_CALL(OCISessionRelease, (connection->svc, connection->err, NULL, 0, rlsMode)); @@ -1649,7 +1597,6 @@ int php_oci_password_change(php_oci_connection *connection, char *user, int user */ void php_oci_client_get_version(char *version, size_t version_size) { -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIClientVersion only available 10.2 onwards */ sword major_version = 0; sword minor_version = 0; sword update_num = 0; @@ -1658,9 +1605,6 @@ void php_oci_client_get_version(char *version, size_t version_size) PHP_OCI_CALL(OCIClientVersion, (&major_version, &minor_version, &update_num, &patch_num, &port_update_num)); snprintf(version, version_size, "%d.%d.%d.%d.%d", major_version, minor_version, update_num, patch_num, port_update_num); -#else - memcpy(version, "Unknown", sizeof("Unknown")); -#endif } /* }}} */ @@ -2045,13 +1989,8 @@ static php_oci_spool *php_oci_create_spool(char *username, int username_len, cha } /* Disable RLB as we mostly have single-connection pools */ -#if (OCI_MAJOR_VERSION > 10) poolmode = OCI_SPC_NO_RLB | OCI_SPC_HOMOGENEOUS; -#else - poolmode = OCI_SPC_HOMOGENEOUS; -#endif -#if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2))) /* {{{ Allocate auth handle for session pool */ PHP_OCI_CALL_RETURN(errstatus, OCIHandleAlloc, (session_pool->env, (dvoid **)&(spoolAuth), OCI_HTYPE_AUTHINFO, 0, NULL)); @@ -2093,7 +2032,6 @@ static php_oci_spool *php_oci_create_spool(char *username, int username_len, cha goto exit_create_spool; } /* }}} */ -#endif /* Create the homogeneous session pool - We have different session pools for every different * username, password, charset and dbname. @@ -2322,7 +2260,6 @@ static int php_oci_old_create_session(php_oci_connection *connection, char *dbna /* }}} */ /* {{{ Set the edition attribute on the session handle */ -#if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2))) if (OCI_G(edition)) { PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(edition), (ub4) (strlen(OCI_G(edition))), (ub4) OCI_ATTR_EDITION, OCI_G(err))); @@ -2331,18 +2268,15 @@ static int php_oci_old_create_session(php_oci_connection *connection, char *dbna return 1; } } -#endif /* }}} */ /* {{{ Set the driver name attribute on the session handle */ -#if (OCI_MAJOR_VERSION >= 11) PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->session, (ub4) OCI_HTYPE_SESSION, (dvoid *) PHP_OCI8_DRIVER_NAME, (ub4) sizeof(PHP_OCI8_DRIVER_NAME)-1, (ub4) OCI_ATTR_DRIVER_NAME, OCI_G(err))); if (OCI_G(errcode) != OCI_SUCCESS) { php_oci_error(OCI_G(err), OCI_G(errcode)); return 1; } -#endif /* }}} */ /* {{{ Set the server handle in the service handle */ @@ -2430,9 +2364,7 @@ static int php_oci_old_create_session(php_oci_connection *connection, char *dbna static int php_oci_create_session(php_oci_connection *connection, php_oci_spool *session_pool, char *dbname, int dbname_len, char *username, int username_len, char *password, int password_len, char *new_password, int new_password_len, int session_mode) { php_oci_spool *actual_spool = NULL; -#if (OCI_MAJOR_VERSION > 10) ub4 purity = -2; /* Illegal value to initialize */ -#endif time_t timestamp = time(NULL); ub4 statement_cache_size = 0; @@ -2488,7 +2420,6 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool } /* Set the Connection class and purity if OCI client version >= 11g */ -#if (OCI_MAJOR_VERSION > 10) PHP_OCI_CALL_RETURN(OCI_G(errcode), OCIAttrSet, ((dvoid *) connection->authinfo,(ub4) OCI_HTYPE_SESSION, (dvoid *) OCI_G(connection_class), (ub4)(strlen(OCI_G(connection_class))), (ub4)OCI_ATTR_CONNECTION_CLASS, OCI_G(err))); if (OCI_G(errcode) != OCI_SUCCESS) { @@ -2507,7 +2438,6 @@ static int php_oci_create_session(php_oci_connection *connection, php_oci_spool php_oci_error(OCI_G(err), OCI_G(errcode)); return 1; } -#endif } /* }}} */ diff --git a/ext/oci8/oci8.stub.php b/ext/oci8/oci8.stub.php index cf24b20a74a5e..efedc3eb9063c 100644 --- a/ext/oci8/oci8.stub.php +++ b/ext/oci8/oci8.stub.php @@ -381,6 +381,9 @@ function oci_set_prefetch($statement, int $rows): bool {} */ function ocisetprefetch($statement, int $rows): bool {} +/** @param resource $statement */ +function oci_set_prefetch_lob($statement, int $prefetch_lob_size): bool {} + /** @param resource $connection */ function oci_set_client_identifier($connection, string $client_id): bool {} diff --git a/ext/oci8/oci8_arginfo.h b/ext/oci8/oci8_arginfo.h index 26bc20bcc70fe..b6916e49c37c5 100644 --- a/ext/oci8/oci8_arginfo.h +++ b/ext/oci8/oci8_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 46ee8ce62b36639636b4f5126e20a4b4e1df2e25 */ + * Stub hash: 9db587b5d431b9dfe7178fd843ae8907db737a04 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_define_by_name, 0, 3, _IS_BOOL, 0) ZEND_ARG_INFO(0, statement) @@ -299,6 +299,11 @@ ZEND_END_ARG_INFO() #define arginfo_ocisetprefetch arginfo_oci_set_prefetch +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_set_prefetch_lob, 0, 2, _IS_BOOL, 0) + ZEND_ARG_INFO(0, statement) + ZEND_ARG_TYPE_INFO(0, prefetch_lob_size, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_oci_set_client_identifier, 0, 2, _IS_BOOL, 0) ZEND_ARG_INFO(0, connection) ZEND_ARG_TYPE_INFO(0, client_id, IS_STRING, 0) @@ -596,6 +601,7 @@ ZEND_FUNCTION(oci_num_fields); ZEND_FUNCTION(oci_parse); ZEND_FUNCTION(oci_get_implicit_resultset); ZEND_FUNCTION(oci_set_prefetch); +ZEND_FUNCTION(oci_set_prefetch_lob); ZEND_FUNCTION(oci_set_client_identifier); ZEND_FUNCTION(oci_set_edition); ZEND_FUNCTION(oci_set_module_name); @@ -710,6 +716,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(oci_get_implicit_resultset, arginfo_oci_get_implicit_resultset) ZEND_FE(oci_set_prefetch, arginfo_oci_set_prefetch) ZEND_DEP_FALIAS(ocisetprefetch, oci_set_prefetch, arginfo_ocisetprefetch) + ZEND_FE(oci_set_prefetch_lob, arginfo_oci_set_prefetch_lob) ZEND_FE(oci_set_client_identifier, arginfo_oci_set_client_identifier) ZEND_FE(oci_set_edition, arginfo_oci_set_edition) ZEND_FE(oci_set_module_name, arginfo_oci_set_module_name) diff --git a/ext/oci8/oci8_interface.c b/ext/oci8/oci8_interface.c index 7668e102ba5a8..2b1117dd6515f 100644 --- a/ext/oci8/oci8_interface.c +++ b/ext/oci8/oci8_interface.c @@ -1633,6 +1633,39 @@ PHP_FUNCTION(oci_set_prefetch) } /* }}} */ +/* {{{ Sets the amount of LOB data to be prefetched when OCI LOB locators are fetched */ +PHP_FUNCTION(oci_set_prefetch_lob) +{ + zval *z_statement; + zend_long prefetch_lob_size; +#if (OCI_MAJOR_VERSION > 12 || (OCI_MAJOR_VERSION == 12 && OCI_MINOR_VERSION >= 2)) + php_oci_statement *statement; +#endif + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_RESOURCE(z_statement) + Z_PARAM_LONG(prefetch_lob_size) + ZEND_PARSE_PARAMETERS_END(); + +#if (OCI_MAJOR_VERSION > 12 || (OCI_MAJOR_VERSION == 12 && OCI_MINOR_VERSION >= 2)) + PHP_OCI_ZVAL_TO_STATEMENT(z_statement, statement); + + if (prefetch_lob_size < 0) { + zend_argument_value_error(2, "must be greater than or equal to 0"); + RETURN_THROWS(); + } + + statement->prefetch_lob_size = (ub4) prefetch_lob_size; + RETURN_TRUE; +#else + /* Although the LOB prefetch feature was available in some earlier Oracle + * version, I don't consider it stable until 12.2 */ + php_error_docref(NULL, E_NOTICE, "Unsupported with this version of Oracle Client"); + RETURN_FALSE; +#endif +} +/* }}} */ + /* {{{ Sets the client identifier attribute on the connection */ PHP_FUNCTION(oci_set_client_identifier) { @@ -1694,7 +1727,6 @@ PHP_FUNCTION(oci_set_edition) Z_PARAM_STRING(edition, edition_len) ZEND_PARSE_PARAMETERS_END(); -#if ((OCI_MAJOR_VERSION > 11) || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION >= 2))) if (OCI_G(edition)) { efree(OCI_G(edition)); } @@ -1708,10 +1740,6 @@ PHP_FUNCTION(oci_set_edition) } RETURN_TRUE; -#else - php_error_docref(NULL, E_NOTICE, "Unsupported attribute type"); - RETURN_FALSE; -#endif } /* }}} */ @@ -1727,8 +1755,6 @@ PHP_FUNCTION(oci_set_module_name) Z_PARAM_STRING(module, module_len) ZEND_PARSE_PARAMETERS_END(); -#if (OCI_MAJOR_VERSION >= 10) - php_oci_connection *connection; sword errstatus; @@ -1742,10 +1768,6 @@ PHP_FUNCTION(oci_set_module_name) } RETURN_TRUE; -#else - php_error_docref(NULL, E_NOTICE, "Unsupported attribute type"); - RETURN_FALSE; -#endif } /* }}} */ @@ -1761,8 +1783,6 @@ PHP_FUNCTION(oci_set_action) Z_PARAM_STRING(action, action_len) ZEND_PARSE_PARAMETERS_END(); -#if (OCI_MAJOR_VERSION >= 10) - php_oci_connection *connection; sword errstatus; @@ -1776,10 +1796,6 @@ PHP_FUNCTION(oci_set_action) } RETURN_TRUE; -#else - php_error_docref(NULL, E_NOTICE, "Unsupported attribute type"); - RETURN_FALSE; -#endif } /* }}} */ @@ -1795,8 +1811,6 @@ PHP_FUNCTION(oci_set_client_info) Z_PARAM_STRING(client_info, client_info_len) ZEND_PARSE_PARAMETERS_END(); -#if (OCI_MAJOR_VERSION >= 10) - php_oci_connection *connection; sword errstatus; @@ -1810,10 +1824,6 @@ PHP_FUNCTION(oci_set_client_info) } RETURN_TRUE; -#else - php_error_docref(NULL, E_NOTICE, "Unsupported attribute type"); - RETURN_FALSE; -#endif } /* }}} */ diff --git a/ext/oci8/oci8_statement.c b/ext/oci8/oci8_statement.c index 5ee04a6168e9e..ca7ef1c8cd50f 100644 --- a/ext/oci8/oci8_statement.c +++ b/ext/oci8/oci8_statement.c @@ -36,8 +36,7 @@ #include "php_oci8.h" #include "php_oci8_int.h" -#if defined(OCI_MAJOR_VERSION) && (OCI_MAJOR_VERSION > 10) && \ - (defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)) +#if defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64) typedef ub8 oci_phpsized_int; #else typedef ub4 oci_phpsized_int; @@ -117,6 +116,12 @@ php_oci_statement *php_oci_statement_create(php_oci_connection *connection, char php_oci_statement_set_prefetch(statement, (ub4)100); /* semi-arbitrary, "sensible default" */ } + if (OCI_G(prefetch_lob_size) > 0) { + statement->prefetch_lob_size = (ub4)OCI_G(prefetch_lob_size); + } else { + statement->prefetch_lob_size = 0; + } + PHP_OCI_REGISTER_RESOURCE(statement, le_statement); OCI_G(num_statements)++; @@ -173,6 +178,7 @@ php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement) GC_ADDREF(statement2->connection->id); php_oci_statement_set_prefetch(statement2, statement->prefetch_count); + statement2->prefetch_lob_size = statement->prefetch_lob_size; PHP_OCI_REGISTER_RESOURCE(statement2, le_statement); @@ -186,7 +192,7 @@ php_oci_statement *php_oci_get_implicit_resultset(php_oci_statement *statement) /* {{{ php_oci_statement_set_prefetch() Set prefetch buffer size for the statement */ -int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch ) +int php_oci_statement_set_prefetch(php_oci_statement *statement, ub4 prefetch) { sword errstatus; @@ -260,11 +266,7 @@ int php_oci_statement_fetch(php_oci_statement *statement, ub4 nrows) zend_hash_apply(statement->columns, php_oci_cleanup_pre_fetch); } -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch2, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, 0, OCI_DEFAULT)); -#else - PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT)); -#endif if (errstatus == OCI_NO_DATA || nrows == 0) { if (statement->last_query == NULL) { @@ -340,11 +342,7 @@ int php_oci_statement_fetch(php_oci_statement *statement, ub4 nrows) } } -#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch2, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, 0, OCI_DEFAULT)); -#else - PHP_OCI_CALL_RETURN(errstatus, OCIStmtFetch, (statement->stmt, statement->err, nrows, OCI_FETCH_NEXT, OCI_DEFAULT)); -#endif if (piecewisecols) { for (i = 0; i < statement->ncolumns; i++) { @@ -820,18 +818,32 @@ int php_oci_statement_execute(php_oci_statement *statement, ub4 mode) return 1; } + /* Enable LOB data prefetching. */ +#if (OCI_MAJOR_VERSION > 12 || (OCI_MAJOR_VERSION == 12 && OCI_MINOR_VERSION >= 2)) + if ((outcol->data_type == SQLT_CLOB || outcol->data_type == SQLT_BLOB) && statement->prefetch_lob_size > 0) { + int get_lob_len = 1; /* == true */ + + PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &get_lob_len, 0, OCI_ATTR_LOBPREFETCH_LENGTH, statement->err)); + if (errstatus != OCI_SUCCESS) { + statement->errcode = php_oci_error(statement->err, errstatus); + PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode); + return 1; + } + + PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &(statement->prefetch_lob_size), 0, OCI_ATTR_LOBPREFETCH_SIZE, statement->err)); + if (errstatus != OCI_SUCCESS) { + statement->errcode = php_oci_error(statement->err, errstatus); + PHP_OCI_HANDLE_ERROR(statement->connection, statement->errcode); + return 1; + } + } +#endif + /* additional define setup */ switch (outcol->data_type) { + case SQLT_RSET: case SQLT_BLOB: case SQLT_CLOB: - if (OCI_G(prefetch_lob_size) > 0) { - int get_lob_len = 1; /* == true */ - ub4 prefetch_size = OCI_G(prefetch_lob_size); - PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &get_lob_len, 0, OCI_ATTR_LOBPREFETCH_LENGTH, statement->err)); - PHP_OCI_CALL_RETURN(errstatus, OCIAttrSet, (outcol->oci_define, OCI_HTYPE_DEFINE, &prefetch_size, 0, OCI_ATTR_LOBPREFETCH_SIZE, statement->err)); - } - ZEND_FALLTHROUGH; - case SQLT_RSET: case SQLT_RDD: case SQLT_BFILE: PHP_OCI_CALL_RETURN(errstatus, diff --git a/ext/oci8/package.xml b/ext/oci8/package.xml index 25de0a9867d40..f5ba6005b61c3 100644 --- a/ext/oci8/package.xml +++ b/ext/oci8/package.xml @@ -10,7 +10,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> The OCI8 extension lets you access Oracle Database. -Use 'pecl install oci8' to install for PHP 8. +Use 'pecl install oci8' to install for PHP 8.1. + +Use 'pecl install oci8-3.0.1' to install for PHP 8.0. Use 'pecl install oci8-2.2.0' to install for PHP 7. @@ -18,9 +20,9 @@ Use 'pecl install oci8-2.0.12' to install for PHP 5.2 - PHP 5.6. Use 'pecl install oci8-1.4.10' to install for PHP 4.3.9 - PHP 5.1. -The OCI8 extension can be linked with Oracle client libraries from Oracle Database 10.2 or later. These libraries are found in your database installation, or in the free Oracle Instant Client from https://www.oracle.com/database/technologies/instant-client.html. +The current OCI8 extension can be linked with Oracle Client libraries from Oracle Database 11.2 or later. (OCI8 3.0 and earlier can be linked with 10g or later). The Oracle Client libraries are in the free Oracle Instant Client from https://www.oracle.com/database/technologies/instant-client.html. They are also included in your database installation. -Oracle's standard cross-version connectivity applies. For example, PHP OCI8 linked with Instant Client 19c can connect to Oracle Database 11.2 onward. See Oracle's note "Oracle Client / Server Interoperability Support" (ID 207303.1) for details. +Oracle's standard cross-version connectivity applies. For example, PHP OCI8 linked with Oracle Client 19c can connect to Oracle Database 11.2 onward. See Oracle's note "Oracle Client / Server Interoperability Support" (ID 207303.1) for details. Christopher Jones @@ -53,12 +55,12 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin no - 2021-11-15 + 2021-12-12 - 3.2.0 - 3.2.0 + 3.2.1 + 3.2.1 stable @@ -66,9 +68,9 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin PHP - This version is for PHP 8 only. + This version is for PHP 8.1 only. - Added oci8.prefetch_lob_size directive to improve LOB query performance. + Requires Oracle Client libraries from 11.2 or later. @@ -373,6 +375,8 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin + + @@ -435,7 +439,7 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin - 8.0.0 + 8.1.0 1.4.0b1 @@ -450,8 +454,8 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin - 3.1.0 - 3.1.0 + 3.2.0 + 3.2.0 stable @@ -459,16 +463,16 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin PHP - This version is for PHP 8 only. + This version is for PHP 8.1 only. - Deprecated directive oci8.old_oci_close_semantics + Added oci8.prefetch_lob_size directive and oci_set_prefetch_lob() function to improve LOB query performance. - + - 3.0.1 - 3.0.1 + 3.1.0 + 3.1.0 stable @@ -476,16 +480,16 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin PHP - This version is for PHP 8 only. + This version is for PHP 8.0 only. - Updated Windows build environment to build with newer Oracle Client libraries. (cmb) + Deprecated directive oci8.old_oci_close_semantics - + - 2.2.0 - 2.2.0 + 3.0.1 + 3.0.1 stable @@ -493,15 +497,12 @@ Oracle's standard cross-version connectivity applies. For example, PHP OCI8 lin PHP -This version is for PHP 7 only. -Added oci_set_call_timeout() for call timeouts. (Requires Oracle client libraries 18c or later) -Added oci_set_db_operation() for the Oracle Database 'DBOP' end-to-end-tracing attribute. (Requires Oracle 12.2 or later) -Fixed bug #76804 (oci_pconnect with OCI_CRED_EXT not working). (KoenigsKind) -Fixed installation on PHP 7.3. -Internal change: Convert some parameter parsing to the Fast Parameter Parsing API. + This version is for PHP 8.0 only. + + Updated Windows build environment to build with newer Oracle Client libraries. (cmb) - + 3.0.0 @@ -513,7 +514,7 @@ Internal change: Convert some parameter parsing to the Fast Parameter Parsing AP PHP - This version is for PHP 8 only. + This version is for PHP 8.0 only. Deprecated old OCI8 function aliases. (Jens de Nies) @@ -527,6 +528,26 @@ Internal change: Convert some parameter parsing to the Fast Parameter Parsing AP + + + 2.2.0 + 2.2.0 + + + stable + stable + + PHP + +This version is for PHP 7 only. +Added oci_set_call_timeout() for call timeouts. (Requires Oracle client libraries 18c or later) +Added oci_set_db_operation() for the Oracle Database 'DBOP' end-to-end-tracing attribute. (Requires Oracle 12.2 or later) +Fixed bug #76804 (oci_pconnect with OCI_CRED_EXT not working). (KoenigsKind) +Fixed installation on PHP 7.3. +Internal change: Convert some parameter parsing to the Fast Parameter Parsing API. + + + 2.1.8 diff --git a/ext/oci8/php_oci8.h b/ext/oci8/php_oci8.h index e447600441655..5ed8c7bbe72c5 100644 --- a/ext/oci8/php_oci8.h +++ b/ext/oci8/php_oci8.h @@ -31,7 +31,6 @@ # include "TSRM.h" #endif - /* * The version of the OCI8 extension. */ @@ -41,7 +40,7 @@ */ #undef PHP_OCI8_VERSION #endif -#define PHP_OCI8_VERSION "3.2.0" +#define PHP_OCI8_VERSION "3.2.1" extern zend_module_entry oci8_module_entry; #define phpext_oci8_ptr &oci8_module_entry diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index b2e5f88c26b87..9b34fc922cbb9 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -55,6 +55,10 @@ #include "ext/standard/php_string.h" #include +#if !defined(OCI_MAJOR_VERSION) || OCI_MAJOR_VERSION < 11 || ((OCI_MAJOR_VERSION == 11) && (OCI_MINOR_VERSION < 2)) +#error This version of PHP OCI8 requires Oracle Client libraries from 11.2 or later. +#endif + extern int le_connection; extern int le_pconnection; extern int le_statement; @@ -235,7 +239,8 @@ typedef struct { unsigned has_data:1; /* statement has more data flag */ unsigned has_descr:1; /* statement has at least one descriptor or cursor column */ ub2 stmttype; /* statement type */ - ub4 prefetch_count; /* current prefetch count */ + ub4 prefetch_count; /* row prefetch count */ + ub4 prefetch_lob_size; /* LOB prefetch size */ } php_oci_statement; /* }}} */ diff --git a/ext/oci8/tests/driver_name.phpt b/ext/oci8/tests/driver_name.phpt index 0b32c4bd1f6be..80b4740bc140b 100644 --- a/ext/oci8/tests/driver_name.phpt +++ b/ext/oci8/tests/driver_name.phpt @@ -57,11 +57,11 @@ function get_attr($conn) ?> --EXPECT-- **Test 1.1 - Default values for the attribute ************** -The value of DRIVER_NAME is PHP OCI8 : 3.2.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.1 ***Test 1.2 - Get the values from different connections ************** Testing with oci_pconnect() -The value of DRIVER_NAME is PHP OCI8 : 3.2.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.1 Testing with oci_new_connect() -The value of DRIVER_NAME is PHP OCI8 : 3.2.0 +The value of DRIVER_NAME is PHP OCI8 : 3.2.1 Done diff --git a/ext/oci8/tests/lob_prefetch.phpt b/ext/oci8/tests/lob_prefetch.phpt index 0fbdb1603300a..94a85585a8e8a 100755 --- a/ext/oci8/tests/lob_prefetch.phpt +++ b/ext/oci8/tests/lob_prefetch.phpt @@ -13,14 +13,14 @@ require(__DIR__.'/skipif.inc'); require __DIR__.'/connect.inc'; require __DIR__.'/create_table.inc'; -define("NUMROWS", 500); +define("NUMROWS", 200); define("LOBSIZE", 64000); $ora_sql = "declare c clob; b blob; - numrows number := 500; + numrows number := " . NUMROWS . "; dest_offset integer := 1; src_offset integer := 1; warn integer; @@ -44,8 +44,10 @@ $statement = oci_parse($c,$ora_sql); oci_execute($statement); -function get_clob_loc($c, $sql) { +function get_clob_loc($c, $sql, $pfl) { $stid = oci_parse($c, $sql); + if ($pfl >= 0) + oci_set_prefetch_lob($stid, $pfl); oci_execute($stid); $l = []; while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) { @@ -56,8 +58,10 @@ function get_clob_loc($c, $sql) { return($l); } -function get_clob_inline($c, $sql) { +function get_clob_inline($c, $sql, $pfl) { $stid = oci_parse($c, $sql); + if ($pfl >= 0) + oci_set_prefetch_lob($stid, $pfl); oci_execute($stid); $l = []; while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) != false) { @@ -81,8 +85,10 @@ function check_clobs($locarr, $inlinearr) { } } -function get_blob_loc($c, $sql) { +function get_blob_loc($c, $sql, $pfl) { $stid = oci_parse($c, $sql); + if ($pfl >= 0) + oci_set_prefetch_lob($stid, $pfl); oci_execute($stid); $l = []; while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) { @@ -94,22 +100,28 @@ function get_blob_loc($c, $sql) { } -print("Test 1\n"); +print("Test 1 - Default prefetch_lob_size\n"); -$ig = ini_get("oci8.prefetch_lob_size"); -var_dump($ig); +$r = ini_get("oci8.prefetch_lob_size"); +var_dump($r); -$ig = ini_set("oci8.prefetch_lob_size", "100000"); -var_dump($ig); +print("Test 2\n"); -$ig = ini_get("oci8.prefetch_lob_size"); -var_dump($ig); +$s = oci_parse($c, 'select * from dual'); +$r = oci_set_prefetch_lob($s, 0); +var_dump($r); -print("Test 2 - CLOB prefetch_lob_size 100000\n"); +try { + oci_set_prefetch_lob($s, -1); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +print("Test 3 - CLOB prefetch_lob_size 100000\n"); $sql = "select clob from ${schema}${table_name}" . " order by id"; -$locarr = get_clob_loc($c, $sql); -$inlinearr = get_clob_inline($c, $sql); +$locarr = get_clob_loc($c, $sql, 100000); +$inlinearr = get_clob_inline($c, $sql, 100000); print(count($locarr) . "\n"); print(count($inlinearr) . "\n"); @@ -117,12 +129,8 @@ check_clobs($locarr, $inlinearr); print("Test 3 - CLOB prefetch_lob_size 100\n"); -ini_set("oci8.prefetch_lob_size", "100"); -$ig = ini_get("oci8.prefetch_lob_size"); -var_dump($ig); - -$locarr = get_clob_loc($c, $sql); -$inlinearr = get_clob_inline($c, $sql); +$locarr = get_clob_loc($c, $sql, 100); +$inlinearr = get_clob_inline($c, $sql, 100); print(count($locarr) . "\n"); print(count($inlinearr) . "\n"); @@ -130,12 +138,8 @@ check_clobs($locarr, $inlinearr); print("Test 4 - BLOB prefetch_lob_size 100000\n"); -ini_set("oci8.prefetch_lob_size", "100000"); -$ig = ini_get("oci8.prefetch_lob_size"); -var_dump($ig); - $sql = "select blob from ${schema}${table_name}" . " order by id"; -$locarr = get_blob_loc($c, $sql); +$locarr = get_blob_loc($c, $sql, 100000); print(count($locarr) . "\n"); @@ -144,20 +148,19 @@ require __DIR__.'/drop_table.inc'; ?> DONE --EXPECTF-- -Test 1 -string(1) "0" +Test 1 - Default prefetch_lob_size string(1) "0" -string(6) "100000" -Test 2 - CLOB prefetch_lob_size 100000 -500 -500 +Test 2 +bool(true) +oci_set_prefetch_lob(): Argument #2 ($prefetch_lob_size) must be greater than or equal to 0 +Test 3 - CLOB prefetch_lob_size 100000 +200 +200 Comparing CLOBS Test 3 - CLOB prefetch_lob_size 100 -string(3) "100" -500 -500 +200 +200 Comparing CLOBS Test 4 - BLOB prefetch_lob_size 100000 -string(6) "100000" -500 +200 DONE diff --git a/ext/oci8/tests/lob_prefetch_ini.phpt b/ext/oci8/tests/lob_prefetch_ini.phpt new file mode 100755 index 0000000000000..2142a8e0ece01 --- /dev/null +++ b/ext/oci8/tests/lob_prefetch_ini.phpt @@ -0,0 +1,165 @@ +--TEST-- +LOB prefetching with oci8. +--EXTENSIONS-- +oci8 +--SKIPIF-- + true, 'timesten' => false); // test runs on these DBs +require(__DIR__.'/skipif.inc'); +?> +--INI-- +oci8.prefetch_lob_size=100000 +--FILE-- += 0) + oci_set_prefetch_lob($stid, $pfl); + oci_execute($stid); + $l = []; + while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) { + $l[] = $row['CLOB']->load(); + $row['CLOB']->free(); + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + +function get_clob_inline($c, $sql, $pfl) { + $stid = oci_parse($c, $sql); + if ($pfl >= 0) + oci_set_prefetch_lob($stid, $pfl); + oci_execute($stid); + $l = []; + while (($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) != false) { + $l[] = $row['CLOB']; + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + +function check_clobs($locarr, $inlinearr) { + print("Comparing CLOBS\n"); + for ($i = 0; $i < NUMROWS; ++$i) { + if (strlen($locarr[$i]) != LOBSIZE) { + trigger_error("size mismatch at $i " . strlen($locarr[$i]), E_USER_ERROR); + exit; + } + if (strcmp($locarr[$i], $inlinearr[$i])) { + trigger_error("data mismatch at $i " . strlen($locarr[$i]) . " " . strlen($inlinearr[$i]), E_USER_ERROR); + exit; + } + } +} + +function get_blob_loc($c, $sql, $pfl) { + $stid = oci_parse($c, $sql); + if ($pfl >= 0) + oci_set_prefetch_lob($stid, $pfl); + oci_execute($stid); + $l = []; + while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) { + $l[] = $row['BLOB']->load(); + $row['BLOB']->free(); + if (strlen($l[0]) != LOBSIZE) { print("strlen(l) failure" . strlen($l)); exit; } + } + return($l); +} + + +print("Test 1 - prefetch_lob_size\n"); + +$r = ini_get("oci8.prefetch_lob_size"); +var_dump($r); + +print("Test 2 - CLOB with current oci8.prefetch_lob_size\n"); + +$sql = "select clob from ${schema}${table_name}" . " order by id"; +$locarr = get_clob_loc($c, $sql, -1); +$inlinearr = get_clob_inline($c, $sql, -1); + +print(count($locarr) . "\n"); +print(count($inlinearr) . "\n"); +check_clobs($locarr, $inlinearr); + +print("Test 3 - CLOB override prefetch_lob_size 0\n"); + +$locarr = get_clob_loc($c, $sql, 0); +$inlinearr = get_clob_inline($c, $sql, 0); + +print(count($locarr) . "\n"); +print(count($inlinearr) . "\n"); +check_clobs($locarr, $inlinearr); + +print("Test 4 - CLOB override prefetch_lob_size 1000\n"); + +$locarr = get_clob_loc($c, $sql, 1000); +$inlinearr = get_clob_inline($c, $sql, 1000); + +print(count($locarr) . "\n"); +print(count($inlinearr) . "\n"); +check_clobs($locarr, $inlinearr); + +print("Test 5 - BLOB with current ocig8.prefetch_lob_size \n"); + +$sql = "select blob from ${schema}${table_name}" . " order by id"; +$locarr = get_blob_loc($c, $sql, -1); + +print(count($locarr) . "\n"); + +require __DIR__.'/drop_table.inc'; + +?> +DONE +--EXPECTF-- +Test 1 - prefetch_lob_size +string(6) "100000" +Test 2 - CLOB with current oci8.prefetch_lob_size +200 +200 +Comparing CLOBS +Test 3 - CLOB override prefetch_lob_size 0 +200 +200 +Comparing CLOBS +Test 4 - CLOB override prefetch_lob_size 1000 +200 +200 +Comparing CLOBS +Test 5 - BLOB with current ocig8.prefetch_lob_size +200 +DONE diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 91e42c821ee58..1b976d8656ab4 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2349,7 +2349,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, zend_map_ptr_extend(ZCSG(map_ptr_last)); return entry->ce; } - ZEND_ASSERT(0); // entry = entry->next; // This shouldn't be posible ??? + ZEND_ASSERT(0); // entry = entry->next; // This shouldn't be possible ??? } } @@ -3515,7 +3515,7 @@ static void preload_restart(void) } static size_t preload_try_strip_filename(zend_string *filename) { - /*FIXME: better way to hanlde eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */ + /*FIXME: better way to handle eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */ if (ZSTR_LEN(filename) > sizeof(" eval()'d code") && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') { const char *cfilename = ZSTR_VAL(filename); @@ -4079,7 +4079,8 @@ static void preload_remove_empty_includes(void) if (opline->opcode == ZEND_INCLUDE_OR_EVAL && opline->extended_value != ZEND_EVAL && opline->op1_type == IS_CONST && - Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) { + Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING && + opline->result_type == IS_UNUSED) { zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1))); @@ -4125,7 +4126,7 @@ static void preload_remove_empty_includes(void) if (resolved_path) { zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path); - if (incl && incl->empty) { + if (incl && incl->empty && opline->result_type == IS_UNUSED) { MAKE_NOP(opline); } else { if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) { diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 090d8f41f35a8..9d18493c7d52a 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -278,6 +278,10 @@ typedef struct _zend_accel_shared_globals { /* uninitialized HashTable Support */ uint32_t uninitialized_bucket[-HT_MIN_MASK]; + /* Tracing JIT */ + void *jit_traces; + const void **jit_exit_groups; + /* Interned Strings Support (must be the last element) */ zend_string_table interned_strings; } zend_accel_shared_globals; diff --git a/ext/opcache/jit/Dockerfile.arm64.example b/ext/opcache/jit/Dockerfile.arm64.example new file mode 100644 index 0000000000000..e7b6a03b2db89 --- /dev/null +++ b/ext/opcache/jit/Dockerfile.arm64.example @@ -0,0 +1,15 @@ +# Force this to build with arm64 even when the host architecture is different. +# This requires that cross-compilation support be enabled with the steps in https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ +FROM --platform=arm64 ubuntu:20.04 +RUN apt-get update -y +# DEBIAN_FRONTEND=noninteractive is needed to stop the tzdata installation from hanging. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get install -y tzdata +RUN apt-get install -y pkg-config build-essential autoconf bison re2c \ + libxml2-dev libsqlite3-dev + +ADD . /php-src/ +WORKDIR /php-src +RUN ./buildconf +# Compile a minimal debug build. --enable-debug adds runtime assertions and is slower than regular builds. +RUN ./configure --enable-debug --disable-all --enable-opcache && make clean && make -j$(nproc) diff --git a/ext/opcache/jit/README.md b/ext/opcache/jit/README.md index 2cc08d2827a23..3119f37811cf8 100644 --- a/ext/opcache/jit/README.md +++ b/ext/opcache/jit/README.md @@ -24,5 +24,119 @@ was almost 100 times slower, making it prohibitively expensive to use. [The unofficial DynASM Documentation](https://corsix.github.io/dynasm-doc/tutorial.html) has a tutorial, reference, and instruction listing. -`zend_jit_x86.dasc` gets automatically converted to `zend_jit_x86.c` by the bundled +In x86 builds, `zend_jit_x86.dasc` gets automatically converted to `zend_jit_x86.c` by the bundled `dynasm` during `make`. + +In arm64 builds, `zend_jit_arm64.dasc` gets automatically converted to `zend_jit_arm64.c` by the bundled +`dynasm` during `make`. + +Running tests of the JIT +------------------------ + +Then, to test the JIT, e.g. with opcache.jit=tracing, an example command +based on what is used to test in Azure CI: + +``` +make test TESTS="-d opcache.jit_buffer_size=16M -d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit=tracing --repeat 2 --show-diff -j$(nproc) ext/opcache Zend" +``` + +- `opcache.jit_buffer_size=16M` enables the JIT in tests by providing 16 megabytes of + memory to use with the JIT to test with. +- `opcache.protect_memory=1` will detect writing to memory that is meant to be + read-only, which is sometimes the cause of opcache bugs. +- `--repeat 2` is optional, but used in CI since some JIT bugs only show up after processing a + request multiple times (the first request compiles the trace and the second executes it) +- `-j$(nproc)` runs as many workers to run tests as there are CPUs. +- `ext/opcache/` and `Zend` are the folders with the tests to run, in this case opcache + and the Zend engine itself. If no folders are provided, all tests are run. + +When investigating test failures such as segmentation faults, +configuring the build of php with `--enable-address-sanitizer` to enable +[AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) is often useful. + +Some of the time, adding `-m --show-mem` to the `TESTS` configuration is also useful to test with [valgrind](https://valgrind.org/) to detect out of bounds memory accesses. +Using valgrind is slower at detecting invalid memory read/writes than AddressSanitizer when running large numbers of tests, but does not require rebuilding php. + +Note that the JIT supports 3 different architectures: `X86_64`, `i386`, and `arm64`. + +Miscellaneous +------------- + +### Checking dasc files for in a different architecture + +The following command can be run to manually check if the modified `.dasc code` is at least transpilable +for an architecture you're not using, e.g.: + +For arm64: `ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -D ARM64=1 -o ext/opcache/jit/zend_jit_arm64.ignored.c ext/opcache/jit/zend_jit_arm64.dasc` + +For x86_64: `ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -D X64=1 -o ext/opcache/jit/zend_jit_x86.ignored.c ext/opcache/jit/zend_jit_x86.dasc` + +For i386 (i.e. 32-bit): `ext/opcache/minilua ext/opcache/jit/dynasm/dynasm.lua -o ext/opcache/jit/zend_jit_x86.ignored.c ext/opcache/jit/zend_jit_x86.dasc` + +### How to build 32-bit builds on x86_64 environments + +Refer to [../../../azure/i386](../../../azure/i386/apt.yml) for examples of +dependencies to install. + +If you are running this natively (outside of Docker or a VM): + +- Consider running in docker/a VM instead if you are unfamiliar with this. +- Avoid purging packages. +- Avoid `-y` - if the package manager warns you that the dependencies conflict + then **don't** try to force install them. + +#### Prerequisites for 32-bit builds + +This assumes you are using a Debian-based Linux distribution and have already +set up prerequisites for regular development. + +``` +sudo dpkg --add-architecture i386 +sudo apt-get update -y +# As well as anything else from azure/i386/apt.yml that you're testing locally +sudo apt-get install \ + gcc-multilib g++-multilib \ + libxml2-dev:i386 \ + libc6:i386 +``` + +#### Compiling 32-bit builds + +This assumes you are using a Debian-based Linux distribution and have already +set up prerequisites for 32-bit development. + +``` +export LDFLAGS=-L/usr/lib/i386-linux-gnu +export CFLAGS='-m32' +export CXXFLAGS='-m32' +export PKG_CONFIG=/usr/bin/i686-linux-gnu-pkg-config +./configure --disable-all --enable-opcache --build=i686-pc-linux-gnu +make -j$(nproc) +``` + +#### Running tests of the JIT on 32-bit builds + +See the section "Running tests of the JIT". + +### Testing the jit with arm64 on x86 computers + +https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ +may be useful for local development. + +Note that this is slower than compiling and testing natively. + +``` +# After following steps in https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/ +cp .gitignore .dockerignore +echo .git >> .dockerignore + +docker build --network=host -t php-src-arm64-example -f ext/opcache/jit/Dockerfile.arm64.example . +docker run -it --rm php-src-arm64-example +``` + +Then, the docker image can be used to run tests with `make test`. +For example, to test `ext/opcache` in parallel with the tracing JIT enabled: + +``` +docker run -it php-src-arms-example make test TESTS="-d opcache.jit_buffer_size=16M -d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit=tracing --repeat 2 --show-diff -j$(nproc) ext/opcache" +``` diff --git a/ext/opcache/jit/vtune/jitprofiling.h b/ext/opcache/jit/vtune/jitprofiling.h index 3e166c04d4359..21d23f8ec7d99 100644 --- a/ext/opcache/jit/vtune/jitprofiling.h +++ b/ext/opcache/jit/vtune/jitprofiling.h @@ -316,7 +316,7 @@ typedef enum _iJIT_IsProfilingActiveFlags */ typedef struct _LineNumberInfo { - unsigned int Offset; /**<\brief Offset from the begining of the code region. */ + unsigned int Offset; /**<\brief Offset from the beginning of the code region. */ unsigned int LineNumber; /**<\brief Matching source line number offset (from beginning of source file). */ } *pLineNumberInfo, LineNumberInfo; @@ -670,7 +670,7 @@ unsigned int JITAPI iJIT_GetNewMethodID(void); iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive(void); /** - * @brief Reports infomation about JIT-compiled code to the agent. + * @brief Reports information about JIT-compiled code to the agent. * * The reported information is used to attribute samples obtained from any * Intel(R) VTune(TM) Amplifier collector. This API needs to be called diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 90c3f20f2ee88..5d365504454a6 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -540,7 +540,7 @@ static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, u return info; } -static bool zend_jit_may_avoid_refcounting(const zend_op *opline) +static bool zend_jit_may_avoid_refcounting(const zend_op *opline, uint32_t op1_info) { switch (opline->opcode) { case ZEND_FETCH_OBJ_FUNC_ARG: @@ -552,7 +552,8 @@ static bool zend_jit_may_avoid_refcounting(const zend_op *opline) /* break missing intentionally */ case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: - if (opline->op2_type == IS_CONST + if ((op1_info & MAY_BE_OBJECT) + && opline->op2_type == IS_CONST && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING && Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') { return 1; @@ -2879,7 +2880,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1; for (i = ssa->cfg.blocks[b].start; i <= end; i++) { - zend_ssa_op *ssa_op = &ssa->ops[i]; + zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; opline = op_array->opcodes + i; switch (opline->opcode) { case ZEND_INIT_FCALL: @@ -2909,7 +2910,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->result_type != IS_UNUSED) { res_use_info = -1; - if (opline->result_type == IS_CV) { + if (opline->result_type == IS_CV + && ssa->vars + && ssa_op->result_use >= 0 + && !ssa->vars[ssa_op->result_use].no_val) { zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); if (Z_MODE(res_use_addr) != IS_REG @@ -2967,7 +2971,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } else { res_use_info = -1; - if (opline->result_type == IS_CV) { + if (opline->result_type == IS_CV + && ssa->vars + && ssa_op->result_use >= 0 + && !ssa->vars[ssa_op->result_use].no_val) { zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); if (Z_MODE(res_use_addr) != IS_REG @@ -3018,7 +3025,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } else { res_use_info = -1; - if (opline->result_type == IS_CV) { + if (opline->result_type == IS_CV + && ssa->vars + && ssa_op->result_use >= 0 + && !ssa->vars[ssa_op->result_use].no_val) { zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); if (Z_MODE(res_use_addr) != IS_REG @@ -4973,7 +4983,7 @@ ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached) #endif } - if (zend_jit_trace_startup() != SUCCESS) { + if (zend_jit_trace_startup(reattached) != SUCCESS) { return FAILURE; } diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index 21d1774c111b6..d22422181af9c 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -75,7 +75,7 @@ #define ZEND_JIT_DEBUG_TRACE_TSSA (1<<19) #define ZEND_JIT_DEBUG_TRACE_EXIT_INFO (1<<20) -#define ZEND_JIT_DEBUG_PERSISTENT 0x1f0 /* profile and debbuger flags can't be changed at run-time */ +#define ZEND_JIT_DEBUG_PERSISTENT 0x1f0 /* profile and debugger flags can't be changed at run-time */ #define ZEND_JIT_TRACE_MAX_LENGTH 1024 /* max length of single trace */ #define ZEND_JIT_TRACE_MAX_EXITS 512 /* max number of side exits per trace */ diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 5a3f8e0b86c68..7bae3752d7137 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -1106,7 +1106,7 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size) | // In x64, if the range of this LONG value can be represented via INT type, only move the low 32 bits into dst_addr. | // Note that imm32 is signed extended to 64 bits during mov. | // In aarch64, we choose to handle both cases in the same way. Even though 4 mov's are used for 64-bit value and 2 mov's are -| // needed for 32-bit value, an extra ext insn is needed for 32-bit vlaue. +| // needed for 32-bit value, an extra ext insn is needed for 32-bit value. | SET_ZVAL_LVAL dst_addr, Z_LVAL_P(zv), Rx(tmp_reg1), Rx(tmp_reg2) || } || } @@ -1794,7 +1794,7 @@ static int zend_jit_interrupt_handler_stub(dasm_State **Dst) | ldp x29, x30, [sp], # SPAD // stack alignment | JMP_IP TMP1 } else { - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #1 // ZEND_VM_ENTER | ret @@ -1821,7 +1821,7 @@ static int zend_jit_exception_handler_stub(dasm_State **Dst) } else { | mov FCARG1x, FP | EXT_CALL handler, REG0 - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | tbnz RETVALw, #31, >1 | mov RETVALw, #1 // ZEND_VM_ENTER @@ -1909,7 +1909,7 @@ static int zend_jit_leave_function_stub(dasm_State **Dst) | ldp x29, x30, [sp], # SPAD // stack alignment } else { | mov FCARG2x, FP - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment } | TST_32_WITH_CONST FCARG1w, ZEND_CALL_TOP, TMP1w @@ -1947,7 +1947,7 @@ static int zend_jit_leave_throw_stub(dasm_State **Dst) |5: | // opline = EG(exception_op); | LOAD_IP_ADDR_ZTS executor_globals, exception_op, TMP2 - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #2 // ZEND_VM_LEAVE | ret @@ -2418,7 +2418,7 @@ static int zend_jit_trace_halt_stub(dasm_State **Dst) | mov IP, xzr // PC must be zero | ret } else { - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | movn RETVALx, #0 // ZEND_VM_RETURN (-1) | ret @@ -2492,7 +2492,7 @@ static int zend_jit_trace_exit_stub(dasm_State **Dst) | ldp x29, x30, [sp], # SPAD // stack alignment | JMP_IP TMP1 } else { - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #1 // ZEND_VM_ENTER | ret @@ -2536,7 +2536,7 @@ static int zend_jit_trace_exit_stub(dasm_State **Dst) | tst RETVALw, RETVALw | blt ->trace_halt | - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #1 // ZEND_VM_ENTER | ret @@ -2556,7 +2556,7 @@ static int zend_jit_trace_escape_stub(dasm_State **Dst) | ldp x29, x30, [sp], # SPAD // stack alignment | JMP_IP, TMP1 } else { - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #1 // ZEND_VM_ENTER | ret @@ -3038,7 +3038,7 @@ static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size if (((ins ^ (uint32_t)delta) & 0x01ffffffu) == 0) { delta = (uint32_t*)to_addr - ins_ptr; if (((delta + 0x02000000) >> 26) != 0) { - abort(); // brnach target out of range + abort(); // branch target out of range } *ins_ptr = (ins & 0xfc000000u) | ((uint32_t)delta & 0x03ffffffu); ret++; @@ -3056,10 +3056,10 @@ static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size if (veneer) { delta = (uint32_t*)veneer - ins_ptr; if (((delta + 0x40000) >> 19) != 0) { - abort(); // brnach target out of range + abort(); // branch target out of range } } else { - abort(); // brnach target out of range + abort(); // branch target out of range } } *ins_ptr = (ins & 0xff00001fu) | (((uint32_t)delta & 0x7ffffu) << 5); @@ -3074,10 +3074,10 @@ static int zend_jit_patch(const void *code, size_t size, uint32_t jmp_table_size if (veneer) { delta = (uint32_t*)veneer - ins_ptr; if (((delta + 0x2000) >> 14) != 0) { - abort(); // brnach target out of range + abort(); // branch target out of range } } else { - abort(); // brnach target out of range + abort(); // branch target out of range } } *ins_ptr = (ins & 0xfff8001fu) | (((uint32_t)delta & 0x3fffu) << 5); @@ -3169,7 +3169,7 @@ static int zend_jit_trace_return(dasm_State **Dst, bool original_handler, const | ldr REG0, [IP, REG0] | blr REG0 } - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment if (!original_handler || !opline || (opline->opcode != ZEND_RETURN @@ -3452,7 +3452,7 @@ static int zend_jit_tail_handler(dasm_State **Dst, const zend_op *opline) | ldp x29, x30, [sp], # SPAD // stack alignment } else { | mov FCARG1x, FP - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment } | EXT_JMP handler, REG0 @@ -4972,7 +4972,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, } | LOAD_ZVAL_ADDR FCARG2x, op2_addr | EXT_CALL zend_jit_fast_assign_concat_helper, REG0 - /* concatination with itself may reduce refcount */ + /* concatenation with itself may reduce refcount */ op2_info |= MAY_BE_RC1; } else { if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { @@ -4986,7 +4986,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, | EXT_CALL zend_jit_fast_concat_tmp_helper, REG0 } } - /* concatination with empty string may increase refcount */ + /* concatenation with empty string may increase refcount */ op2_info |= MAY_BE_RCN; | FREE_OP op2_type, op2, op2_info, 0, opline, ZREG_TMP1, ZREG_TMP2 |5: @@ -5011,7 +5011,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, | LOAD_ZVAL_ADDR CARG3, op2_addr | SET_EX_OPLINE opline, REG0 | EXT_CALL concat_function, REG0 - /* concatination with empty string may increase refcount */ + /* concatenation with empty string may increase refcount */ op1_info |= MAY_BE_RCN; op2_info |= MAY_BE_RCN; | FREE_OP op1_type, op1, op1_info, 0, NULL, ZREG_TMP1, ZREG_TMP2 @@ -9528,7 +9528,7 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend | ldp x29, x30, [sp], # SPAD // stack alignment | JMP_IP TMP1 } else { - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #1 // ZEND_VM_ENTER | ret @@ -10865,7 +10865,7 @@ static int zend_jit_leave_func(dasm_State **Dst, // the value of execute_data in execute_ex() | NIY // TODO #else - | ldp FP, RX, T2 // retore FP and IP + | ldp FP, RX, T2 // restore FP and IP | ldp x29, x30, [sp], # NR_SPAD // stack alignment | mov RETVALx, #2 // ZEND_VM_LEAVE ???? | ret @@ -11118,7 +11118,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) && (ssa_op+1)->op1_use == ssa_op->result_def && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG))) - && zend_jit_may_avoid_refcounting(opline+1)) { + && zend_jit_may_avoid_refcounting(opline+1, res_info)) { result_avoid_refcounting = 1; ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; } @@ -12398,7 +12398,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, && !(flags & ZEND_JIT_EXIT_FREE_OP1) && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) && (ssa_op+1)->op1_use == ssa_op->result_def - && zend_jit_may_avoid_refcounting(opline+1)) { + && zend_jit_may_avoid_refcounting(opline+1, res_info)) { result_avoid_refcounting = 1; ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; } @@ -14832,7 +14832,7 @@ static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, | IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr, ZREG_TMP1 | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 } else { - /* May be already loaded into FCARG1a or RAX by previus FETCH_OBJ_W/DIM_W */ + /* May be already loaded into FCARG1a or RAX by previous FETCH_OBJ_W/DIM_W */ if (opline->op1_type != IS_VAR || (opline-1)->result_type != IS_VAR || (opline-1)->result.var != opline->op1.var || diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index ec47aa6ef7783..cd0743df514d2 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -730,10 +730,61 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim offset_key = ZSTR_EMPTY_ALLOC(); goto str_index; case IS_DOUBLE: - hval = zend_dval_to_lval_safe(Z_DVAL_P(dim)); + hval = zend_dval_to_lval(Z_DVAL_P(dim)); + if (!zend_is_long_compatible(Z_DVAL_P(dim), hval)) { + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); + zend_incompatible_double_to_long_error(Z_DVAL_P(dim)); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return NULL; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return NULL; + } + } goto num_index; case IS_RESOURCE: + /* The array may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(ht); + } + execute_data = EG(current_execute_data); + opline = EX(opline); zend_use_resource_as_offset(dim); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) && !GC_DELREF(ht)) { + zend_array_destroy(ht); + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + if (EG(exception)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } else { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + } + return NULL; + } + if (EG(exception)) { + if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return NULL; + } hval = Z_RES_HANDLE_P(dim); goto num_index; case IS_FALSE: @@ -904,13 +955,15 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_is_helper(zend_string *str, zva static void ZEND_FASTCALL zend_jit_fetch_dim_obj_r_helper(zval *container, zval *dim, zval *result) { zval *retval; + zend_object *obj = Z_OBJ_P(container); + GC_ADDREF(obj); if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var); dim = &EG(uninitialized_zval); } - retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, BP_VAR_R, result); + retval = obj->handlers->read_dimension(obj, dim, BP_VAR_R, result); if (retval) { if (result != retval) { @@ -921,18 +974,23 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_r_helper(zval *container, zval } else { ZVAL_NULL(result); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } static void ZEND_FASTCALL zend_jit_fetch_dim_obj_is_helper(zval *container, zval *dim, zval *result) { zval *retval; + zend_object *obj = Z_OBJ_P(container); + GC_ADDREF(obj); if (UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { zend_jit_undefined_op_helper(EG(current_execute_data)->opline->op2.var); dim = &EG(uninitialized_zval); } - retval = Z_OBJ_HT_P(container)->read_dimension(Z_OBJ_P(container), dim, BP_VAR_IS, result); + retval = obj->handlers->read_dimension(obj, dim, BP_VAR_IS, result); if (retval) { if (result != retval) { @@ -943,6 +1001,9 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_is_helper(zval *container, zval } else { ZVAL_NULL(result); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result) @@ -1103,16 +1164,11 @@ static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { zend_object *obj = Z_OBJ_P(object_ptr); + GC_ADDREF(obj); if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) { const zend_op *opline = EG(current_execute_data)->opline; - GC_ADDREF(obj); zend_jit_undefined_op_helper(opline->op2.var); dim = &EG(uninitialized_zval); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - ZVAL_NULL(result); - return; - } } retval = obj->handlers->read_dimension(obj, dim, type, result); @@ -1141,6 +1197,9 @@ static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception"); ZVAL_UNDEF(result); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { if (!dim) { zend_throw_error(NULL, "[] operator not supported for strings"); @@ -1152,9 +1211,15 @@ static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval } ZVAL_UNDEF(result); } else if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); zend_array *arr = zend_new_array(0); ZVAL_ARR(object_ptr, arr); + GC_ADDREF(arr); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(arr) == 0)) { + zend_array_destroy(arr); + ZVAL_NULL(result); + return; + } zval *var; if (dim) { if (type == BP_VAR_W) { @@ -1202,38 +1267,23 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { zend_object *obj = Z_OBJ_P(object_ptr); + GC_ADDREF(obj); if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { const zend_op *opline = EG(current_execute_data)->opline; - GC_ADDREF(obj); zend_jit_undefined_op_helper(opline->op2.var); dim = &EG(uninitialized_zval); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - if (result) { - ZVAL_NULL(result); - } - return; - } } if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { const zend_op *op_data = EG(current_execute_data)->opline + 1; ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV); - GC_ADDREF(obj); zend_jit_undefined_op_helper(op_data->op1.var); value = &EG(uninitialized_zval); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); - if (result) { - ZVAL_NULL(result); - } - return; - } } else { ZVAL_DEREF(value); } - Z_OBJ_HT_P(object_ptr)->write_dimension(obj, dim, value); + obj->handlers->write_dimension(obj, dim, value); if (result) { if (EXPECTED(!EG(exception))) { ZVAL_COPY(result, value); @@ -1241,18 +1291,15 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim ZVAL_UNDEF(result); } } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); + } return; } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) && EXPECTED(dim != NULL)) { zend_assign_to_string_offset(object_ptr, dim, value, result); return; } - if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - const zend_op *opline = EG(current_execute_data)->opline; - zend_jit_undefined_op_helper(opline->op2.var); - dim = &EG(uninitialized_zval); - } - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { const zend_op *op_data = EG(current_execute_data)->opline + 1; ZEND_ASSERT(op_data->opcode == ZEND_OP_DATA && op_data->op1_type == IS_CV); @@ -1266,9 +1313,17 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim ZVAL_UNDEF(result); } } else if (Z_TYPE_P(object_ptr) == IS_FALSE) { - zend_false_to_array_deprecated(); zend_array *arr = zend_new_array(0); ZVAL_ARR(object_ptr, arr); + GC_ADDREF(arr); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(arr) == 0)) { + zend_array_destroy(arr); + if (result) { + ZVAL_NULL(result); + } + return; + } zval *var = dim ? zend_jit_fetch_dim_w_helper(arr, dim) : zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval)); @@ -1284,6 +1339,11 @@ static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim ZVAL_COPY(result, var); } } else { + if (dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + const zend_op *opline = EG(current_execute_data)->opline; + zend_jit_undefined_op_helper(opline->op2.var); + dim = &EG(uninitialized_zval); + } zend_throw_error(NULL, "Cannot use a scalar value as an array"); if (result) { ZVAL_UNDEF(result); @@ -1298,18 +1358,11 @@ static void ZEND_FASTCALL zend_jit_assign_dim_op_helper(zval *container, zval *d zval *z; zval rv, res; + GC_ADDREF(obj); if (dim && UNEXPECTED(Z_ISUNDEF_P(dim))) { const zend_op *opline = EG(current_execute_data)->opline; - GC_ADDREF(obj); zend_jit_undefined_op_helper(opline->op2.var); dim = &EG(uninitialized_zval); - if (UNEXPECTED(GC_DELREF(obj) == 0)) { - zend_objects_store_del(obj); -//??? if (retval) { -//??? ZVAL_NULL(retval); -//??? } - return; - } } z = obj->handlers->read_dimension(obj, dim, BP_VAR_R, &rv); @@ -1325,6 +1378,12 @@ static void ZEND_FASTCALL zend_jit_assign_dim_op_helper(zval *container, zval *d } else { zend_error(E_WARNING, "Attempt to assign property of non-object"); } + if (UNEXPECTED(GC_DELREF(obj) == 0)) { + zend_objects_store_del(obj); +//??? if (retval) { +//??? ZVAL_NULL(retval); +//??? } + } } else if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { if (!dim) { zend_throw_error(NULL, "[] operator not supported for strings"); @@ -1335,9 +1394,14 @@ static void ZEND_FASTCALL zend_jit_assign_dim_op_helper(zval *container, zval *d zend_wrong_string_offset_error(); } } else if (Z_TYPE_P(container) == IS_FALSE) { - zend_false_to_array_deprecated(); zend_array *arr = zend_new_array(0); ZVAL_ARR(container, arr); + GC_ADDREF(arr); + zend_false_to_array_deprecated(); + if (UNEXPECTED(GC_DELREF(arr) == 0)) { + zend_array_destroy(arr); + return; + } zval *var = dim ? zend_jit_fetch_dim_rw_helper(arr, dim) : zend_hash_next_index_insert_new(arr, &EG(uninitialized_zval)); @@ -1488,11 +1552,10 @@ static zend_reference* ZEND_FASTCALL zend_jit_fetch_global_helper(zend_string *v if (EXPECTED(idx < EG(symbol_table).nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)EG(symbol_table).arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == varname) || - (EXPECTED(p->h == ZSTR_H(varname)) && - EXPECTED(p->key != NULL) && - EXPECTED(zend_string_equal_content(p->key, varname))))) { + if (EXPECTED(p->key == varname) || + (EXPECTED(p->h == ZSTR_H(varname)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, varname)))) { value = (zval*)p; /* value = &p->val; */ goto check_indirect; @@ -1585,12 +1648,10 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_r_dynamic(zend_object *zobj, intptr if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(name)) && - EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { ZVAL_COPY_DEREF(result, &p->val); return; } @@ -1643,12 +1704,10 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) { Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx); - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(p->key == name) || - (EXPECTED(p->h == ZSTR_H(name)) && - EXPECTED(p->key != NULL) && - EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(name)) && - EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)))) { + if (EXPECTED(p->key == name) || + (EXPECTED(p->h == ZSTR_H(name)) && + EXPECTED(p->key != NULL) && + EXPECTED(zend_string_equal_content(p->key, name)))) { ZVAL_COPY_DEREF(result, &p->val); return; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 39770e11fb136..b941447f86103 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -46,20 +46,33 @@ static zend_always_inline const char *zend_jit_trace_star_desc(uint8_t trace_fla } } -static int zend_jit_trace_startup(void) +static int zend_jit_trace_startup(zend_bool reattached) { - zend_jit_traces = (zend_jit_trace_info*)zend_shared_alloc(sizeof(zend_jit_trace_info) * JIT_G(max_root_traces)); - if (!zend_jit_traces) { - return FAILURE; - } - zend_jit_exit_groups = (const void**)zend_shared_alloc(sizeof(void*) * (ZEND_JIT_TRACE_MAX_EXITS/ZEND_JIT_EXIT_POINTS_PER_GROUP)); - if (!zend_jit_exit_groups) { - return FAILURE; + if (!reattached) { + zend_jit_traces = (zend_jit_trace_info*)zend_shared_alloc(sizeof(zend_jit_trace_info) * JIT_G(max_root_traces)); + if (!zend_jit_traces) { + return FAILURE; + } + zend_jit_exit_groups = (const void**)zend_shared_alloc(sizeof(void*) * (ZEND_JIT_TRACE_MAX_EXITS/ZEND_JIT_EXIT_POINTS_PER_GROUP)); + if (!zend_jit_exit_groups) { + return FAILURE; + } + ZEND_JIT_TRACE_NUM = 1; + ZEND_JIT_COUNTER_NUM = 0; + ZEND_JIT_EXIT_NUM = 0; + ZEND_JIT_EXIT_COUNTERS = 0; + ZCSG(jit_traces) = zend_jit_traces; + ZCSG(jit_exit_groups) = zend_jit_exit_groups; + } else { + zend_jit_traces = ZCSG(jit_traces); + if (!zend_jit_traces) { + return FAILURE; + } + zend_jit_exit_groups = ZCSG(jit_exit_groups); + if (!zend_jit_exit_groups) { + return FAILURE; + } } - ZEND_JIT_TRACE_NUM = 1; - ZEND_JIT_COUNTER_NUM = 0; - ZEND_JIT_EXIT_NUM = 0; - ZEND_JIT_EXIT_COUNTERS = 0; memset(&dummy_op_array, 0, sizeof(dummy_op_array)); dummy_op_array.fn_flags = ZEND_ACC_DONE_PASS_TWO; @@ -4852,15 +4865,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); op1_info = OP1_INFO(); - if (ssa->vars[ssa_op->op1_use].no_val) { - if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG - || (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_DOUBLE) { - if (STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) != IS_LONG - && STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) != IS_DOUBLE) { - /* type may be not set */ - op1_info |= MAY_BE_NULL; - } - } + if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_LONG + || (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_GUARD)) == MAY_BE_DOUBLE) { + if (STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) != IS_LONG + && STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)) != IS_DOUBLE) { + /* type may be not set */ + op1_info |= MAY_BE_NULL; + } } op1_def_info = OP1_DEF_INFO(); if (op1_type != IS_UNKNOWN && (op1_info & MAY_BE_GUARD)) { @@ -7843,7 +7854,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf zend_jit_trace_info *t = &zend_jit_traces[trace_num]; int repeat_last_opline = 0; - /* Deoptimizatoion of VM stack state */ + /* Deoptimization of VM stack state */ uint32_t i; uint32_t stack_size = t->exit_info[exit_num].stack_size; zend_jit_trace_stack *stack = t->stack_map + t->exit_info[exit_num].stack_offset; diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 9ea1bbf17878a..5f09e41eaed3e 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -763,17 +763,12 @@ static size_t tsrm_tls_offset; || } |.endmacro -|.macro LONG_OP, long_ins, reg, addr +|.macro LONG_OP, long_ins, reg, addr, tmp_reg || if (Z_MODE(addr) == IS_CONST_ZVAL) { | .if X64 || if (!IS_SIGNED_32BIT(Z_LVAL_P(Z_ZV(addr)))) { -|| if (reg != ZREG_R0) { -| mov64 r0, Z_LVAL_P(Z_ZV(addr)) -| long_ins Ra(reg), r0 -|| } else { -| mov64 r1, Z_LVAL_P(Z_ZV(addr)) -| long_ins Ra(reg), r1 -|| } +| mov64 tmp_reg, Z_LVAL_P(Z_ZV(addr)) +| long_ins Ra(reg), tmp_reg || } else { | long_ins Ra(reg), Z_LVAL_P(Z_ZV(addr)) || } @@ -853,25 +848,25 @@ static size_t tsrm_tls_offset; || } |.endmacro -|.macro LONG_MATH, opcode, reg, addr +|.macro LONG_MATH, opcode, reg, addr, tmp_reg || switch (opcode) { || case ZEND_ADD: -| LONG_OP add, reg, addr +| LONG_OP add, reg, addr, Ra(tmp_reg) || break; || case ZEND_SUB: -| LONG_OP sub, reg, addr +| LONG_OP sub, reg, addr, Ra(tmp_reg) || break; || case ZEND_MUL: -| LONG_OP imul, reg, addr +| LONG_OP imul, reg, addr, Ra(tmp_reg) || break; || case ZEND_BW_OR: -| LONG_OP or, reg, addr +| LONG_OP or, reg, addr, Ra(tmp_reg) || break; || case ZEND_BW_AND: -| LONG_OP and, reg, addr +| LONG_OP and, reg, addr, Ra(tmp_reg) || break; || case ZEND_BW_XOR: -| LONG_OP xor, reg, addr +| LONG_OP xor, reg, addr, Ra(tmp_reg) || break; || default: || ZEND_UNREACHABLE(); @@ -3458,7 +3453,7 @@ static int zend_jit_trace_link_to_root(dasm_State **Dst, zend_jit_trace_info *t, #if defined(__x86_64__) || defined(_M_X64) prologue_size = 17; #else - prologue_size = 12; + prologue_size = 13; #endif } link_addr = (const void*)((const char*)t->code_start + prologue_size); @@ -4423,7 +4418,17 @@ static int zend_jit_math_long_long(dasm_State **Dst, } else if (same_ops && opcode != ZEND_DIV) { | LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg) } else { - | LONG_MATH opcode, result_reg, op2_addr + zend_reg tmp_reg; + + if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) { + tmp_reg = ZREG_R1; + } else if (result_reg != ZREG_R0) { + tmp_reg = ZREG_R0; + } else { + tmp_reg = ZREG_R1; + } + | LONG_MATH opcode, result_reg, op2_addr, tmp_reg + (void)tmp_reg; } } if (may_overflow) { @@ -5174,12 +5179,21 @@ static int zend_jit_long_math_helper(dasm_State **Dst, } else if (zend_long_is_power_of_two(op2_lval) && op1_range && op1_range->min >= 0) { zval tmp; zend_jit_addr tmp_addr; + zend_reg tmp_reg; /* Optimisation for mod of power of 2 */ ZVAL_LONG(&tmp, op2_lval - 1); tmp_addr = ZEND_ADDR_CONST_ZVAL(&tmp); + if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) { + tmp_reg = ZREG_R1; + } else if (result_reg != ZREG_R0) { + tmp_reg = ZREG_R0; + } else { + tmp_reg = ZREG_R1; + } | GET_ZVAL_LVAL result_reg, op1_addr - | LONG_MATH ZEND_BW_AND, result_reg, tmp_addr + | LONG_MATH ZEND_BW_AND, result_reg, tmp_addr, tmp_reg + (void)tmp_reg; } else { if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RAX) { | mov aword T1, r0 // save @@ -5267,8 +5281,18 @@ static int zend_jit_long_math_helper(dasm_State **Dst, | GET_ZVAL_LVAL result_reg, op1_addr | LONG_MATH_REG opcode, Ra(result_reg), Ra(result_reg) } else { + zend_reg tmp_reg; + + if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) { + tmp_reg = ZREG_R1; + } else if (result_reg != ZREG_R0) { + tmp_reg = ZREG_R0; + } else { + tmp_reg = ZREG_R1; + } | GET_ZVAL_LVAL result_reg, op1_addr - | LONG_MATH opcode, result_reg, op2_addr + | LONG_MATH opcode, result_reg, op2_addr, tmp_reg + (void)tmp_reg; } if (Z_MODE(res_addr) != IS_REG || Z_REG(res_addr) != result_reg) { @@ -5425,7 +5449,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, } | LOAD_ZVAL_ADDR FCARG2a, op2_addr | EXT_CALL zend_jit_fast_assign_concat_helper, r0 - /* concatination with itself may reduce refcount */ + /* concatenation with itself may reduce refcount */ op2_info |= MAY_BE_RC1; } else { if (Z_REG(res_addr) != ZREG_FCARG1 || Z_OFFSET(res_addr) != 0) { @@ -5447,7 +5471,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, | add r4, 12 |.endif } - /* concatination with empty string may increase refcount */ + /* concatenation with empty string may increase refcount */ op2_info |= MAY_BE_RCN; | FREE_OP op2_type, op2, op2_info, 0, opline |5: @@ -5481,7 +5505,7 @@ static int zend_jit_concat_helper(dasm_State **Dst, |.if not(X64) | add r4, 12 |.endif - /* concatination with empty string may increase refcount */ + /* concatenation with empty string may increase refcount */ op1_info |= MAY_BE_RCN; op2_info |= MAY_BE_RCN; | FREE_OP op1_type, op1, op1_info, 0, NULL @@ -7047,13 +7071,13 @@ static int zend_jit_cmp_long_long(dasm_State **Dst, if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { | test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr)) } else { - | LONG_OP cmp, Z_REG(op1_addr), op2_addr + | LONG_OP cmp, Z_REG(op1_addr), op2_addr, r0 } } else if (Z_MODE(op2_addr) == IS_REG) { if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op1_addr)) == 0) { | test Ra(Z_REG(op2_addr)), Ra(Z_REG(op2_addr)) } else { - | LONG_OP cmp, Z_REG(op2_addr), op1_addr + | LONG_OP cmp, Z_REG(op2_addr), op1_addr, r0 } swap = 1; } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL) { @@ -7066,7 +7090,7 @@ static int zend_jit_cmp_long_long(dasm_State **Dst, if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { | test r0, r0 } else { - | LONG_OP cmp, ZREG_R0, op2_addr + | LONG_OP cmp, ZREG_R0, op2_addr, r0 } } @@ -11775,7 +11799,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) && (ssa_op+1)->op1_use == ssa_op->result_def && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF) - (MAY_BE_STRING|MAY_BE_LONG))) - && zend_jit_may_avoid_refcounting(opline+1)) { + && zend_jit_may_avoid_refcounting(opline+1, res_info)) { result_avoid_refcounting = 1; ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; } @@ -13107,7 +13131,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, && !(flags & ZEND_JIT_EXIT_FREE_OP1) && (res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) && (ssa_op+1)->op1_use == ssa_op->result_def - && zend_jit_may_avoid_refcounting(opline+1)) { + && zend_jit_may_avoid_refcounting(opline+1, res_info)) { result_avoid_refcounting = 1; ssa->var_info[ssa_op->result_def].avoid_refcounting = 1; } @@ -15700,7 +15724,7 @@ static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, | IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr | GET_ZVAL_PTR FCARG1a, var_addr } else { - /* May be already loaded into FCARG1a or RAX by previus FETCH_OBJ_W/DIM_W */ + /* May be already loaded into FCARG1a or RAX by previous FETCH_OBJ_W/DIM_W */ if (opline->op1_type != IS_VAR || (opline-1)->result_type != IS_VAR || (opline-1)->result.var != opline->op1.var || diff --git a/ext/opcache/tests/jit/add_012.phpt b/ext/opcache/tests/jit/add_012.phpt new file mode 100644 index 0000000000000..645f8f4bdd9b5 --- /dev/null +++ b/ext/opcache/tests/jit/add_012.phpt @@ -0,0 +1,19 @@ +--TEST-- +JIT ADD: 012 register allocation for 64-bit constant +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--SKIPIF-- + +--FILE-- + +DONE +--EXPECT-- +DONE \ No newline at end of file diff --git a/ext/opcache/tests/jit/and_001.phpt b/ext/opcache/tests/jit/and_001.phpt index f44bb4e13fb87..273310f1f5efd 100644 --- a/ext/opcache/tests/jit/and_001.phpt +++ b/ext/opcache/tests/jit/and_001.phpt @@ -1,5 +1,5 @@ --TEST-- -JIT BW_AND: 001 (emty string) +JIT BW_AND: 001 (empty string) --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/jit/assign_045.phpt b/ext/opcache/tests/jit/assign_045.phpt index 6c5c204f66f4f..f9e7d31db5d2f 100644 --- a/ext/opcache/tests/jit/assign_045.phpt +++ b/ext/opcache/tests/jit/assign_045.phpt @@ -1,5 +1,5 @@ --TEST-- -JIT ASSIGN: incorrect assumption about in-memeory zval type +JIT ASSIGN: incorrect assumption about in-memory zval type --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/jit/assign_046.phpt b/ext/opcache/tests/jit/assign_046.phpt index fe27bdd3a2933..201f619f11bdf 100644 --- a/ext/opcache/tests/jit/assign_046.phpt +++ b/ext/opcache/tests/jit/assign_046.phpt @@ -1,5 +1,5 @@ --TEST-- -JIT ASSIGN: incorrect assumption about in-memeory zval type +JIT ASSIGN: incorrect assumption about in-memory zval type --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/jit/assign_048.phpt b/ext/opcache/tests/jit/assign_048.phpt new file mode 100644 index 0000000000000..a6bcf14933a78 --- /dev/null +++ b/ext/opcache/tests/jit/assign_048.phpt @@ -0,0 +1,43 @@ +--TEST-- +JIT ASSIGN: incorrect type store elimination +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +opcache.optimization_level=0x7FFEBFFF +--FILE-- + +DONE +--EXPECTF-- +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 + +Warning: Undefined variable $a in %sassign_048.php on line 7 +DONE diff --git a/ext/opcache/tests/jit/assign_dim_op_006.phpt b/ext/opcache/tests/jit/assign_dim_op_006.phpt index 075a6c34e95d9..0ed14cf9ae8ed 100644 --- a/ext/opcache/tests/jit/assign_dim_op_006.phpt +++ b/ext/opcache/tests/jit/assign_dim_op_006.phpt @@ -1,5 +1,5 @@ --TEST-- -JIT ASSIGN_DIM_OP 006: Cloberring array be user error handler +JIT ASSIGN_DIM_OP 006: Clobbering array be user error handler --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/jit/assign_op_008.phpt b/ext/opcache/tests/jit/assign_op_008.phpt new file mode 100644 index 0000000000000..efd9418741fc0 --- /dev/null +++ b/ext/opcache/tests/jit/assign_op_008.phpt @@ -0,0 +1,22 @@ +--TEST-- +JIT ASSIGN_OP: 008 Arrays merging with itself +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +DONE +--EXPECTF-- +Warning: Undefined array key "b" in %sassign_op_008.php on line 6 +DONE diff --git a/ext/opcache/tests/jit/cmp_008.phpt b/ext/opcache/tests/jit/cmp_008.phpt new file mode 100644 index 0000000000000..6216de5527726 --- /dev/null +++ b/ext/opcache/tests/jit/cmp_008.phpt @@ -0,0 +1,26 @@ +--TEST-- +JIT CMP: 008 Wrong range inference for comparison between IS_LONG and IS_FALSE/IS_TRUE +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $a in %scmp_008.php on line 3 + +Warning: Undefined variable $a in %scmp_008.php on line 3 + +Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %scmp_008.php:3 +Stack trace: +#0 %scmp_008.php(6): test() +#1 {main} + thrown in %scmp_008.php on line 3 diff --git a/ext/opcache/tests/jit/fetch_dim_w_003.phpt b/ext/opcache/tests/jit/fetch_dim_w_003.phpt new file mode 100644 index 0000000000000..33fc2ccbb8459 --- /dev/null +++ b/ext/opcache/tests/jit/fetch_dim_w_003.phpt @@ -0,0 +1,17 @@ +--TEST-- +JIT FETCH_DIM_W: 003 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/jit/fetch_obj_008.phpt b/ext/opcache/tests/jit/fetch_obj_008.phpt new file mode 100644 index 0000000000000..38c5b8a539889 --- /dev/null +++ b/ext/opcache/tests/jit/fetch_obj_008.phpt @@ -0,0 +1,31 @@ +--TEST-- +JIT: FETCH_OBJ 008 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- +prop = "A $e B"; + $a->prop->prop . $a->prop = "C"; + return "test"; + } +} + +$a = new A; +$a->prop = new B; +?> +DONE +--EXPECTF-- +Warning: Undefined variable $e in %sfetch_obj_008.php on line 9 + +Warning: Attempt to read property "prop" on string in %sfetch_obj_008.php on line 10 +DONE diff --git a/ext/opcache/tests/jit/fetch_r_001.phpt b/ext/opcache/tests/jit/fetch_r_001.phpt index db5b7ba42bd74..53b49a98d8922 100644 --- a/ext/opcache/tests/jit/fetch_r_001.phpt +++ b/ext/opcache/tests/jit/fetch_r_001.phpt @@ -1,5 +1,5 @@ --TEST-- -FETCH_R: 001 result reference counter may be decremented befor use +FETCH_R: 001 result reference counter may be decremented before use --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/jit/mod_006.phpt b/ext/opcache/tests/jit/mod_006.phpt new file mode 100644 index 0000000000000..b13ef68a8e1ba --- /dev/null +++ b/ext/opcache/tests/jit/mod_006.phpt @@ -0,0 +1,19 @@ +--TEST-- +JIT MOD: 005 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +--FILE-- + +DONE +--EXPECT-- +DONE \ No newline at end of file diff --git a/ext/opcache/tests/jit/mul_008.phpt b/ext/opcache/tests/jit/mul_008.phpt new file mode 100644 index 0000000000000..d890a86c8f485 --- /dev/null +++ b/ext/opcache/tests/jit/mul_008.phpt @@ -0,0 +1,26 @@ +--TEST-- +JIT MUL: 008 incorrect elimination of type store +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %smul_008.php:6 +Stack trace: +#0 %smul_008.php(8): foo(%d) +#1 {main} + thrown in %smul_008.php on line 6 diff --git a/ext/opcache/tests/opt/assign_op_001.phpt b/ext/opcache/tests/opt/assign_op_001.phpt new file mode 100644 index 0000000000000..b9db4202b46c0 --- /dev/null +++ b/ext/opcache/tests/opt/assign_op_001.phpt @@ -0,0 +1,30 @@ +--TEST-- +ASSIGN_OP 001: Incrrect optimization of ASSIGN_OP may lead to memory leak +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $a in %sassign_op_001.php on line 4 + +Warning: Undefined variable $a in %sassign_op_001.php on line 4 + +Warning: Undefined array key "b" in %sassign_op_001.php on line 7 + +Fatal error: Uncaught TypeError: Unsupported operand types: array + bool in %sassign_op_001.php:4 +Stack trace: +#0 %sassign_op_001.php(10): test() +#1 {main} + thrown in %sassign_op_001.php on line 4 \ No newline at end of file diff --git a/ext/opcache/tests/opt/dce_012.phpt b/ext/opcache/tests/opt/dce_012.phpt new file mode 100644 index 0000000000000..49cde14b33c33 --- /dev/null +++ b/ext/opcache/tests/opt/dce_012.phpt @@ -0,0 +1,26 @@ +--TEST-- +Incorrect DCE of constructor DO_FCALL +--FILE-- +orop1 = 'abc'; + } + + foreach (range(0, 6) as $levels) { + print "$levels level" . ($levels == C ? "" : "s") . "aaa"; + } + + $obj->prop1 = null; +} +test(); +?> +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "C" in %sdce_012.php:11 +Stack trace: +#0 %sdce_012.php(16): test() +#1 {main} + thrown in %sdce_012.php on line 11 diff --git a/ext/opcache/tests/opt/inference_002.phpt b/ext/opcache/tests/opt/inference_002.phpt new file mode 100644 index 0000000000000..70412426c2fcc --- /dev/null +++ b/ext/opcache/tests/opt/inference_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Type inference 002: Type inference for INIT_ARRAY with invalid index +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +&$x]); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: Illegal offset type in %sinference_002.php:2 +Stack trace: +#0 {main} + thrown in %sinference_002.php on line 2 \ No newline at end of file diff --git a/ext/opcache/tests/opt/jmp_002.phpt b/ext/opcache/tests/opt/jmp_002.phpt new file mode 100644 index 0000000000000..cd7f05b225dac --- /dev/null +++ b/ext/opcache/tests/opt/jmp_002.phpt @@ -0,0 +1,13 @@ +--TEST-- +JMP 002: JMP around unreachable FREE +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/opt/sccp_034.phpt b/ext/opcache/tests/opt/sccp_034.phpt new file mode 100644 index 0000000000000..8fafc89178a64 --- /dev/null +++ b/ext/opcache/tests/opt/sccp_034.phpt @@ -0,0 +1,16 @@ +--TEST-- +SCCP 034: memory leak +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + +DONE +--EXPECTF-- +Warning: Undefined variable $y in %ssccp_034.php on line 2 + +Warning: Undefined variable $y in %ssccp_034.php on line 2 +DONE diff --git a/ext/openssl/tests/openssl_x509_checkpurpose_basic.phpt b/ext/openssl/tests/openssl_x509_checkpurpose_basic.phpt index 7071677e947b7..d09317b1d22f7 100644 --- a/ext/openssl/tests/openssl_x509_checkpurpose_basic.phpt +++ b/ext/openssl/tests/openssl_x509_checkpurpose_basic.phpt @@ -9,10 +9,14 @@ openssl ?> --FILE-- saveCaCert(__DIR__ . "/san-cert.pem"); + $cert = "file://" . __DIR__ . "/cert.crt"; $bert = "file://" . __DIR__ . "/bug41033.pem"; $sert = "file://" . __DIR__ . "/san-cert.pem"; -$cpca = __DIR__ . "/san-ca.pem"; +$cpca = __DIR__ . "/san-cert.pem"; $utfl = __DIR__ . "/sni_server_uk.pem"; $rcrt = openssl_x509_read($cert); @@ -85,6 +89,10 @@ var_dump(openssl_x509_checkpurpose($sert, X509_PURPOSE_SMIME_ENCRYPT, array($cpc var_dump(openssl_x509_checkpurpose($sert, X509_PURPOSE_CRL_SIGN, array($cpca), $utfl)); var_dump(openssl_x509_checkpurpose($sert, X509_PURPOSE_ANY, array($cpca), $utfl)); ?> +--CLEAN-- + --EXPECT-- bool(false) bool(false) diff --git a/ext/openssl/tests/san-ca.pem b/ext/openssl/tests/san-ca.pem deleted file mode 100644 index 88682ba2dcf65..0000000000000 --- a/ext/openssl/tests/san-ca.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV -BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y -MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE -BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl -ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m -dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9 -clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3 -Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC -D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF -AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k -rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X -pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg== ------END CERTIFICATE----- diff --git a/ext/openssl/tests/san-cert.pem b/ext/openssl/tests/san-cert.pem deleted file mode 100644 index 923d490e72fd3..0000000000000 --- a/ext/openssl/tests/san-cert.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICYTCCAcqgAwIBAgIJAIaqxtY5dwjtMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV -BAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UEBxMLTWlubmVhcG9saXMxITAfBgNV -BAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRlZDAeFw0xMzA5MjQwODA1NTFaFw0y -MTEyMTEwODA1NTFaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJNTjEUMBIGA1UE -BxMLTWlubmVhcG9saXMxITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl -ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsFGqfbU/8D+KjroQl4XMyt9m -dcSP7iZtqphOu9nVZxYAAqfaqj8FnC/pwYV3TU6ZHndLTQAllwYT3sQBQPPGmZQ9 -clSIMEL003t3pi4ZVXkttG6Vvr+Z9PBcHhlKLQ7WMHnn4qctllWXTSoyTQpkETF3 -Fc3mrG5G37BhoUno7NECAwEAAaM9MDswOQYDVR0RBDIwMIILZXhhbXBsZS5vcmeC -D3d3dy5leGFtcGxlLm9yZ4IQdGVzdC5leGFtcGxlLm9yZzANBgkqhkiG9w0BAQUF -AAOBgQBf/FZhzheIcQJ+dyTk8xQ/nJLvpmBhbd1LNtfwk/MsC9UHsz4QXs9sBw1k -rH0FjoqgM6avj7zKHJFTj6q7Rd+OX5V4HynYPhX67sWbN3KWEHffL98nGGd/bo3X -pSjNk5vnyKYiwdUUe11Ac9csh0HcSBbhOYjy0T/i9AlQcKbuCg== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBRqn21P/A/io66 -EJeFzMrfZnXEj+4mbaqYTrvZ1WcWAAKn2qo/BZwv6cGFd01OmR53S00AJZcGE97E -AUDzxpmUPXJUiDBC9NN7d6YuGVV5LbRulb6/mfTwXB4ZSi0O1jB55+KnLZZVl00q -Mk0KZBExdxXN5qxuRt+wYaFJ6OzRAgMBAAECgYB11e5iWvqjPmQEZRdnnJU0VD8u -n7ItT+Nk6qtb4gY8Abj6DWIW+01th5vqqJ8FvGyartFVYa69kuM+srG/zevAZWeu -fGZtwiwZR4DRSyRcPp4rnNiksK3dkAZA6UewmRDPv8uyHJlXc5i+Ft1ILJ5Q5jgn -UkC4z3EJP5Se9KZywQJBAOO4lRq42wLsYr2SDrQDSs4leie3FKc2bgvjF7Djosh1 -ZYbf55F5b9w1zgnccmni2HkqOnyFu4SKarmXyCsYxrkCQQDGNvnUh7/zZswrdWZ/ -PMp9zVDTh/5Oc2B4ByNLw1ERDwYhjchKgPRlQvn4cp3Pwf3UYPQ/8XGXzzEJey3A -r0rZAkBf/tDEOgcBPXsGZQrTscuYCU5sbY5ESvqrAilbhSp7DJom+D5bIfEYyIm5 -uHd20Yzlzvpmwc1huyPwZt6X5FLpAkATDReoGMAXSesXxjnqwtIHk2NQYYLM0YQV -JUJ8NrKk/Bevw+vbVVeoH+7ctU97t36JGiR/vNoZKD3jVmaIXZDJAkEA4wJbwzIo -L32mu9VmZa7wjmfkraQEmXTPaA5D9lNC0AwRTgkj+x2Qe1vawNblNK9PPLBDdplQ -L//53ADq/wv5rA== ------END PRIVATE KEY----- diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 645b211952b9f..463dca61891cf 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -446,6 +446,9 @@ static void php_pcntl_register_errno_constants(INIT_FUNC_ARGS) #ifdef EUSERS REGISTER_PCNTL_ERRNO_CONSTANT(EUSERS); #endif +#ifdef ECAPMODE + REGISTER_PCNTL_ERRNO_CONSTANT(ECAPMODE); +#endif } static PHP_GINIT_FUNCTION(pcntl) @@ -521,9 +524,7 @@ PHP_FUNCTION(pcntl_fork) { pid_t id; - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); id = fork(); if (id == -1) { @@ -540,9 +541,9 @@ PHP_FUNCTION(pcntl_alarm) { zend_long seconds; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &seconds) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(seconds); + ZEND_PARSE_PARAMETERS_END(); RETURN_LONG((zend_long) alarm(seconds)); } @@ -592,9 +593,13 @@ PHP_FUNCTION(pcntl_waitpid) struct rusage rusage; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|lz", &pid, &z_status, &options, &z_rusage) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_LONG(pid) + Z_PARAM_ZVAL(z_status) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(z_rusage) + ZEND_PARSE_PARAMETERS_END(); status = zval_get_long(z_status); @@ -641,9 +646,12 @@ PHP_FUNCTION(pcntl_wait) struct rusage rusage; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|lz", &z_status, &options, &z_rusage) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(z_status) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(options) + Z_PARAM_ZVAL(z_rusage) + ZEND_PARSE_PARAMETERS_END(); status = zval_get_long(z_status); #ifdef HAVE_WAIT3 @@ -689,9 +697,9 @@ PHP_FUNCTION(pcntl_wifexited) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WIFEXITED int int_status_word = (int) status_word; @@ -709,9 +717,9 @@ PHP_FUNCTION(pcntl_wifstopped) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WIFSTOPPED int int_status_word = (int) status_word; @@ -729,9 +737,9 @@ PHP_FUNCTION(pcntl_wifsignaled) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WIFSIGNALED int int_status_word = (int) status_word; @@ -743,14 +751,15 @@ PHP_FUNCTION(pcntl_wifsignaled) RETURN_FALSE; } /* }}} */ + /* {{{ Returns true if the child status code represents a process that was resumed due to a SIGCONT signal */ PHP_FUNCTION(pcntl_wifcontinued) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef HAVE_WCONTINUED int int_status_word = (int) status_word; @@ -768,9 +777,9 @@ PHP_FUNCTION(pcntl_wexitstatus) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WEXITSTATUS int int_status_word = (int) status_word; @@ -786,9 +795,9 @@ PHP_FUNCTION(pcntl_wtermsig) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WTERMSIG int int_status_word = (int) status_word; @@ -804,9 +813,9 @@ PHP_FUNCTION(pcntl_wstopsig) { zend_long status_word; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &status_word) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(status_word) + ZEND_PARSE_PARAMETERS_END(); #ifdef WSTOPSIG int int_status_word = (int) status_word; @@ -833,9 +842,12 @@ PHP_FUNCTION(pcntl_exec) size_t path_len; zend_ulong key_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|aa", &path, &path_len, &args, &envs) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_PATH(path, path_len) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY(args) + Z_PARAM_ARRAY(envs) + ZEND_PARSE_PARAMETERS_END(); if (ZEND_NUM_ARGS() > 1) { /* Build argument list */ @@ -932,9 +944,12 @@ PHP_FUNCTION(pcntl_signal) bool restart_syscalls_is_null = 1; char *error = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz|b!", &signo, &handle, &restart_syscalls, &restart_syscalls_is_null) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_LONG(signo) + Z_PARAM_ZVAL(handle) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL_OR_NULL(restart_syscalls, restart_syscalls_is_null) + ZEND_PARSE_PARAMETERS_END(); if (signo < 1) { zend_argument_value_error(1, "must be greater than or equal to 1"); @@ -1011,9 +1026,9 @@ PHP_FUNCTION(pcntl_signal_get_handler) zval *prev_handle; zend_long signo; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &signo) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(signo) + ZEND_PARSE_PARAMETERS_END(); if (signo < 1 || signo > 32) { zend_argument_value_error(1, "must be between 1 and 32"); @@ -1030,9 +1045,7 @@ PHP_FUNCTION(pcntl_signal_get_handler) /* {{{ Dispatch signals to signal handlers */ PHP_FUNCTION(pcntl_signal_dispatch) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); pcntl_signal_dispatch(); RETURN_TRUE; @@ -1047,9 +1060,12 @@ PHP_FUNCTION(pcntl_sigprocmask) zval *user_set, *user_oldset = NULL, *user_signo; sigset_t set, oldset; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "la|z", &how, &user_set, &user_oldset) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_LONG(how) + Z_PARAM_ARRAY(user_set) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(user_oldset) + ZEND_PARSE_PARAMETERS_END(); if (sigemptyset(&set) != 0 || sigemptyset(&oldset) != 0) { PCNTL_G(last_error) = errno; @@ -1103,13 +1119,19 @@ static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{ struct timespec timeout; if (timedwait) { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|zll", &user_set, &user_siginfo, &tv_sec, &tv_nsec) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_ARRAY(user_set) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(user_siginfo) + Z_PARAM_LONG(tv_sec) + Z_PARAM_LONG(tv_nsec) + ZEND_PARSE_PARAMETERS_END(); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|z", &user_set, &user_siginfo) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(user_set) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(user_siginfo) + ZEND_PARSE_PARAMETERS_END(); } if (sigemptyset(&set) != 0) { @@ -1231,9 +1253,11 @@ PHP_FUNCTION(pcntl_getpriority) bool pid_is_null = 1; int pri; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!l", &pid, &pid_is_null, &who) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pid, pid_is_null) + Z_PARAM_LONG(who) + ZEND_PARSE_PARAMETERS_END(); /* needs to be cleared, since any returned value is valid */ errno = 0; @@ -1270,9 +1294,12 @@ PHP_FUNCTION(pcntl_setpriority) bool pid_is_null = 1; zend_long pri; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l!l", &pri, &pid, &pid_is_null, &who) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_LONG(pri) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pid, pid_is_null) + Z_PARAM_LONG(who) + ZEND_PARSE_PARAMETERS_END(); if (setpriority(who, pid_is_null ? getpid() : pid, pri)) { PCNTL_G(last_error) = errno; @@ -1304,9 +1331,7 @@ PHP_FUNCTION(pcntl_setpriority) /* {{{ Retrieve the error number set by the last pcntl function which failed. */ PHP_FUNCTION(pcntl_get_last_error) { - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(PCNTL_G(last_error)); } @@ -1317,9 +1342,9 @@ PHP_FUNCTION(pcntl_strerror) { zend_long error; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &error) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(error) + ZEND_PARSE_PARAMETERS_END(); RETURN_STRING(strerror(error)); } @@ -1437,9 +1462,10 @@ PHP_FUNCTION(pcntl_async_signals) { bool on, on_is_null = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b!", &on, &on_is_null) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL_OR_NULL(on, on_is_null) + ZEND_PARSE_PARAMETERS_END(); if (on_is_null) { RETURN_BOOL(PCNTL_G(async_signals)); diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index d668b4d06627a..d89854256e1b6 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -83,7 +83,7 @@ function pcntl_unshare(int $flags): bool {} #ifdef HAVE_RFORK function pcntl_rfork(int $flags, int $signal = 0): int{} #endif -# -#ifdef HAVE_RFORK + +#ifdef HAVE_FORKX function pcntl_forkx(int $flags): int{} #endif diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index 4caaff2bcffcb..91147e70ea9da 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb7d243e0a1b5e85e0bfc0b2add5145cee134b25 */ + * Stub hash: e9f831c37e960f9b0db7561faefe61d49dd7df21 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -125,9 +125,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_rfork, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, signal, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #endif - + #if defined(HAVE_FORKX) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_forkx, 0, 0, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_forkx, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) ZEND_END_ARG_INFO() #endif @@ -174,6 +174,9 @@ ZEND_FUNCTION(pcntl_unshare); #if defined(HAVE_RFORK) ZEND_FUNCTION(pcntl_rfork); #endif +#if defined(HAVE_FORKX) +ZEND_FUNCTION(pcntl_forkx); +#endif static const zend_function_entry ext_functions[] = { @@ -218,6 +221,9 @@ static const zend_function_entry ext_functions[] = { #endif #if defined(HAVE_RFORK) ZEND_FE(pcntl_rfork, arginfo_pcntl_rfork) +#endif +#if defined(HAVE_FORKX) + ZEND_FE(pcntl_forkx, arginfo_pcntl_forkx) #endif ZEND_FE_END }; diff --git a/ext/pcre/php_pcre.stub.php b/ext/pcre/php_pcre.stub.php index 498219b3c41e5..006e740008745 100644 --- a/ext/pcre/php_pcre.stub.php +++ b/ext/pcre/php_pcre.stub.php @@ -35,6 +35,7 @@ function preg_replace_callback_array(array $pattern, string|array $subject, int */ function preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array|false {} +/** @compile-time-eval */ function preg_quote(string $str, ?string $delimiter = null): string {} /** @refcount 1 */ diff --git a/ext/pcre/php_pcre_arginfo.h b/ext/pcre/php_pcre_arginfo.h index 96c8331c0d611..bb16a6079a2b5 100644 --- a/ext/pcre/php_pcre_arginfo.h +++ b/ext/pcre/php_pcre_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: bc6f31ac17d4f5d1a60dd3dad5f671058f40a224 */ + * Stub hash: 39a19378fb1f1aca34bfdd483f5d3095558f0e09 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) @@ -84,7 +84,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(preg_replace_callback, arginfo_preg_replace_callback) ZEND_FE(preg_replace_callback_array, arginfo_preg_replace_callback_array) ZEND_FE(preg_split, arginfo_preg_split) - ZEND_FE(preg_quote, arginfo_preg_quote) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(preg_quote, arginfo_preg_quote) ZEND_FE(preg_grep, arginfo_preg_grep) ZEND_FE(preg_last_error, arginfo_preg_last_error) ZEND_FE(preg_last_error_msg, arginfo_preg_last_error_msg) diff --git a/ext/pdo/tests/bug_36798.phpt b/ext/pdo/tests/bug_36798.phpt index bfea814de9664..dea1a5b92a23c 100644 --- a/ext/pdo/tests/bug_36798.phpt +++ b/ext/pdo/tests/bug_36798.phpt @@ -11,7 +11,8 @@ PDOTest::skip(); if (!strncasecmp(getenv('PDOTEST_DSN'), 'oci', strlen('oci'))){ if (!strpos(strtolower(getenv('PDOTEST_DSN')), 'charset=we8mswin1252')) die('skip expected output valid for Oracle with WE8MSWIN1252 character set'); - +} elseif (!strncasecmp(getenv('PDOTEST_DSN'), 'dblib', strlen('dblib'))) { + die('skip not for pdo_dblib'); } ?> diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index f415a8d7f44be..2a946c81dc242 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -21,7 +21,6 @@ #include "php.h" #include "php_ini.h" -#include "ext/standard/php_string.h" #include "ext/standard/info.h" #include "pdo/php_pdo.h" #include "pdo/php_pdo_driver.h" diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index be9e8c012252e..54bf7ede6bfaa 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -87,7 +87,7 @@ int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char * } if (msg) { - einfo->errmsg = estrdup(msg); + einfo->errmsg = pestrdup(msg, dbh->is_persistent); } else if (errmsg) { einfo->errmsg = _pdo_pgsql_trim_message(errmsg, dbh->is_persistent); diff --git a/ext/pdo_pgsql/tests/gh7723.phpt b/ext/pdo_pgsql/tests/gh7723.phpt new file mode 100644 index 0000000000000..bbbf5fd5bc21c --- /dev/null +++ b/ext/pdo_pgsql/tests/gh7723.phpt @@ -0,0 +1,30 @@ +--TEST-- +GitHub #7723 (Fix error message allocation of PDO PgSQL) +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_PERSISTENT, true); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + +$st = $db->prepare('select 1'); +for ($i = 0; ++$i <= 2;) { + try { + $st->bindValue(':invalid', $i); + } catch (PDOException $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +SQLSTATE[HY093]: Invalid parameter number: :invalid +SQLSTATE[HY093]: Invalid parameter number: :invalid diff --git a/ext/simplexml/simplexml.stub.php b/ext/simplexml/simplexml.stub.php index a115c8b2defeb..4735573521587 100644 --- a/ext/simplexml/simplexml.stub.php +++ b/ext/simplexml/simplexml.stub.php @@ -44,7 +44,7 @@ public function __construct(string $data, int $options = 0, bool $dataIsURL = fa public function addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null): ?SimpleXMLElement {} /** @tentative-return-type */ - public function addAttribute(string $qualifiedName, ?string $value = null, ?string $namespace = null): void {} + public function addAttribute(string $qualifiedName, string $value, ?string $namespace = null): void {} /** @tentative-return-type */ public function getName(): string {} diff --git a/ext/simplexml/simplexml_arginfo.h b/ext/simplexml/simplexml_arginfo.h index 881051514c595..f6100eb243e31 100644 --- a/ext/simplexml/simplexml_arginfo.h +++ b/ext/simplexml/simplexml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 46da4d24787a5f975eebeaab3872eb29273a9625 */ + * Stub hash: 06c88dc2fb5582a6d21c11aee6ac0a0538e70cbc */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_simplexml_load_file, 0, 1, SimpleXMLElement, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -67,9 +67,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_SimpleXMLElement_ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, namespace, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement_addAttribute, 0, 1, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement_addAttribute, 0, 2, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, qualifiedName, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, namespace, IS_STRING, 1, "null") ZEND_END_ARG_INFO() diff --git a/ext/sockets/config.m4 b/ext/sockets/config.m4 index b78186ee11ba6..d4f92082c3b2d 100644 --- a/ext/sockets/config.m4 +++ b/ext/sockets/config.m4 @@ -62,6 +62,34 @@ if test "$PHP_SOCKETS" != "no"; then AC_DEFINE(HAVE_AI_IDN,1,[Whether you have AI_IDN]) fi + dnl Check for struct ucred + dnl checking the header is not enough (eg DragonFlyBSD) + AC_CACHE_CHECK([if ancillary credentials uses ucred],[ac_cv_ucred], + [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include + ]], [[struct ucred u = {.gid = 0};]])], + [ac_cv_ucred=yes], [ac_cv_ucred=no]) + ]) + + if test "$ac_cv_ucred" = yes; then + AC_DEFINE(ANC_CREDS_UCRED,1,[Uses ucred struct]) + fi + + dnl Check for struct cmsgcred + AC_CACHE_CHECK([if ancillary credentials uses cmsgcred],[ac_cv_cmsgcred], + [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include + ]], [[struct cmsgcred c = {0};]])], + [ac_cv_cmsgcred=yes], [ac_cv_cmsgcred=no]) + ]) + + if test "$ac_cv_cmsgcred" = yes; then + AC_DEFINE(ANC_CREDS_CMSGCRED,1,[Uses cmsgcred struct]) + fi + + PHP_SOCKETS_CFLAGS=-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 case $host_alias in *darwin*) PHP_SOCKETS_CFLAGS="$PHP_SOCKETS_CFLAGS -D__APPLE_USE_RFC_3542" diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index ba5b1d8a5ad67..773ac5f3e02cd 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -1297,13 +1297,20 @@ void to_zval_read_in6_pktinfo(const char *data, zval *zv, res_context *ctx) } #endif -/* CONVERSIONS for struct ucred */ +/* CONVERSIONS for struct ucred/cmsgcred */ #ifdef SO_PASSCRED static const field_descriptor descriptors_ucred[] = { +#if defined(ANC_CREDS_UCRED) {"pid", sizeof("pid"), 1, offsetof(struct ucred, pid), from_zval_write_pid_t, to_zval_read_pid_t}, {"uid", sizeof("uid"), 1, offsetof(struct ucred, uid), from_zval_write_uid_t, to_zval_read_uid_t}, /* assume the type gid_t is the same as uid_t: */ {"gid", sizeof("gid"), 1, offsetof(struct ucred, gid), from_zval_write_uid_t, to_zval_read_uid_t}, +#elif defined(ANC_CREDS_CMSGCRED) + {"pid", sizeof("pid"), 1, offsetof(struct cmsgcred, cmcred_pid), from_zval_write_pid_t, to_zval_read_pid_t}, + {"uid", sizeof("uid"), 1, offsetof(struct cmsgcred, cmcred_uid), from_zval_write_uid_t, to_zval_read_uid_t}, + /* assume the type gid_t is the same as uid_t: */ + {"gid", sizeof("gid"), 1, offsetof(struct cmsgcred, cmcred_gid), from_zval_write_uid_t, to_zval_read_uid_t}, +#endif {0} }; void from_zval_write_ucred(const zval *container, char *ucred_c, ser_context *ctx) diff --git a/ext/sockets/multicast.c b/ext/sockets/multicast.c index 0b2f10d09fb6f..f713d44ff8813 100644 --- a/ext/sockets/multicast.c +++ b/ext/sockets/multicast.c @@ -784,7 +784,7 @@ int php_add4_to_if_index(struct in_addr *addr, php_socket *php_sock, unsigned *i } for (p = if_conf.ifc_buf; - p < if_conf.ifc_buf + if_conf.ifc_len; + p < ((char *)if_conf.ifc_buf) + if_conf.ifc_len; p += entry_len) { /* p may be misaligned on macos. */ struct ifreq cur_req; diff --git a/ext/sockets/sendrecvmsg.c b/ext/sockets/sendrecvmsg.c index bd24eae9418a1..7029478486cb3 100644 --- a/ext/sockets/sendrecvmsg.c +++ b/ext/sockets/sendrecvmsg.c @@ -121,8 +121,13 @@ static void init_ancillary_registry(void) #endif #ifdef SO_PASSCRED +#ifdef ANC_CREDS_UCRED PUT_ENTRY(sizeof(struct ucred), 0, 0, from_zval_write_ucred, to_zval_read_ucred, SOL_SOCKET, SCM_CREDENTIALS); +#else + PUT_ENTRY(sizeof(struct cmsgcred), 0, 0, from_zval_write_ucred, + to_zval_read_ucred, SOL_SOCKET, SCM_CREDS); +#endif #endif #ifdef SCM_RIGHTS @@ -436,7 +441,11 @@ void php_socket_sendrecvmsg_init(INIT_FUNC_ARGS) REGISTER_LONG_CONSTANT("SCM_RIGHTS", SCM_RIGHTS, CONST_CS | CONST_PERSISTENT); #endif #ifdef SO_PASSCRED +#ifdef SCM_CREDENTIALS REGISTER_LONG_CONSTANT("SCM_CREDENTIALS", SCM_CREDENTIALS, CONST_CS | CONST_PERSISTENT); +#else + REGISTER_LONG_CONSTANT("SCM_CREDS", SCM_CREDS, CONST_CS | CONST_PERSISTENT); +#endif REGISTER_LONG_CONSTANT("SO_PASSCRED", SO_PASSCRED, CONST_CS | CONST_PERSISTENT); #endif diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 1df6eaa09d659..6b52bb5c7cc56 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -545,6 +545,9 @@ static PHP_MINIT_FUNCTION(sockets) #ifdef SO_MARK REGISTER_LONG_CONSTANT("SO_MARK", SO_MARK, CONST_CS | CONST_PERSISTENT); #endif +#ifdef SO_INCOMING_CPU + REGISTER_LONG_CONSTANT("SO_INCOMING_CPU", SO_INCOMING_CPU, CONST_CS | CONST_PERSISTENT); +#endif #ifdef TCP_NODELAY REGISTER_LONG_CONSTANT("TCP_NODELAY", TCP_NODELAY, CONST_CS | CONST_PERSISTENT); #endif diff --git a/ext/sockets/tests/socket_cmsg_credentials.phpt b/ext/sockets/tests/socket_cmsg_credentials.phpt index aaeff989fc37b..7402c9437c2b2 100644 --- a/ext/sockets/tests/socket_cmsg_credentials.phpt +++ b/ext/sockets/tests/socket_cmsg_credentials.phpt @@ -14,10 +14,6 @@ die('skip not for AIX'); if (!defined('SO_PASSCRED')) { die('skip SO_PASSCRED is not defined'); } ---CLEAN-- - +--CLEAN-- +type) { case SPL_FS_INFO: - intern->path = zend_string_copy(source->path); - intern->file_name = zend_string_copy(source->file_name); + if (source->path != NULL) { + intern->path = zend_string_copy(source->path); + } + if (source->file_name != NULL) { + intern->file_name = zend_string_copy(source->file_name); + } break; case SPL_FS_DIR: spl_filesystem_dir_open(intern, source->path); @@ -1936,6 +1940,8 @@ static int spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_obje /* 1) use fgetcsv? 2) overloaded call the function, 3) do it directly */ if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) { + spl_filesystem_file_free_line(intern); + if (php_stream_eof(intern->u.file.stream)) { if (!silent) { zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name)); diff --git a/ext/spl/tests/bug75917.phpt b/ext/spl/tests/bug75917.phpt new file mode 100644 index 0000000000000..c9bc02869c2d1 --- /dev/null +++ b/ext/spl/tests/bug75917.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #75917 (SplFileObject::seek broken with CSV flags) +--FILE-- +fputcsv($row); +} +$tmp->setFlags(0); +$tmp->seek(23); +var_dump($tmp->current()); + +$tmp->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY); +$tmp->seek(23); +var_dump($tmp->current()); +?> +--EXPECT-- +bool(false) +bool(false) diff --git a/ext/spl/tests/gh7809.phpt b/ext/spl/tests/gh7809.phpt new file mode 100644 index 0000000000000..6406bef6c1438 --- /dev/null +++ b/ext/spl/tests/gh7809.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug GH-7809 (Cloning a faked SplFileInfo object may segfault) +--FILE-- + +--EXPECT-- diff --git a/ext/standard/array.c b/ext/standard/array.c index c3d4c3d7a78a3..aa89e3755b123 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2915,7 +2915,6 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ uint32_t i = hash->nNumUsed; for (; i > 0; p++, i--) { - if (Z_ISUNDEF(p->val)) continue; if (p->key) { zend_string_release(p->key); p->key = NULL; diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 1719b1453795a..e5483cf23b644 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -126,8 +126,10 @@ function pos(array|object $array): mixed {} function key(array|object $array): int|string|null {} +/** @compile-time-eval */ function min(mixed $value, mixed ...$values): mixed {} +/** @compile-time-eval */ function max(mixed $value, mixed ...$values): mixed {} /** @return true */ @@ -136,8 +138,14 @@ function array_walk(array|object &$array, callable $callback, mixed $arg = UNKNO /** @return true */ function array_walk_recursive(array|object &$array, callable $callback, mixed $arg = UNKNOWN): bool {} +/** + * @compile-time-eval + */ function in_array(mixed $needle, array $haystack, bool $strict = false): bool {} +/** + * @compile-time-eval + */ function array_search(mixed $needle, array $haystack, bool $strict = false): int|string|false {} /** @prefer-ref $array */ @@ -176,24 +184,48 @@ function array_splice(array &$array, int $offset, ?int $length = null, mixed $re function array_slice(array $array, int $offset, ?int $length = null, bool $preserve_keys = false): array {} +/** + * @compile-time-eval + */ function array_merge(array ...$arrays): array {} +/** + * @compile-time-eval + */ function array_merge_recursive(array ...$arrays): array {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function array_replace(array $array, array ...$replacements): array {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function array_replace_recursive(array $array, array ...$replacements): array {} -/** @return array */ +/** + * @return array + * @compile-time-eval + */ function array_keys(array $array, mixed $filter_value = UNKNOWN, bool $strict = false): array {} +/** + * @compile-time-eval + */ function array_key_first(array $array): int|string|null {} +/** + * @compile-time-eval + */ function array_key_last(array $array): int|string|null {} -/** @return array */ +/** + * @return array + * @compile-time-eval + */ function array_values(array $array): array {} /** @@ -213,12 +245,19 @@ function array_pad(array $array, int $length, mixed $value): array {} /** * @return array * @refcount 1 + * @compile-time-eval */ function array_flip(array $array): array {} -/** @refcount 1 */ +/** + * @refcount 1 + * @compile-time-eval + */ function array_change_key_case(array $array, int $case = CASE_LOWER): array {} +/** + * @compile-time-eval + */ function array_unique(array $array, int $flags = SORT_STRING): array {} /** @refcount 1 */ @@ -260,15 +299,22 @@ function array_intersect_uassoc(array $array, ...$rest): array {} */ function array_uintersect_uassoc(array $array, ...$rest): array {} -/** @refcount 1 */ +/** + * @refcount 1 + * @compile-time-eval + */ function array_diff_key(array $array, array ...$arrays): array {} /** * @param array|callable $rest * @refcount 1 + * @compile-time-eval */ function array_diff_ukey(array $array, ...$rest): array {} +/** + * @compile-time-eval + */ function array_diff(array $array, array ...$arrays): array {} /** @@ -277,7 +323,10 @@ function array_diff(array $array, array ...$arrays): array {} */ function array_udiff(array $array, ...$rest): array {} -/** @refcount 1 */ +/** + * @refcount 1 + * @compile-time-eval + */ function array_diff_assoc(array $array, array ...$arrays): array {} /** @@ -336,10 +385,16 @@ function array_is_list(array $array): bool {} /* base64.c */ -/** @refcount 1 */ +/** + * @refcount 1 + * @compile-time-eval + */ function base64_encode(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function base64_decode(string $string, bool $strict = false): string|false {} /* basic_functions.c */ @@ -517,6 +572,7 @@ function get_browser(?string $user_agent = null, bool $return_array = false): ob /* crc32.c */ +/** @compile-time-eval */ function crc32(string $string): int {} /* crypt.c */ @@ -708,10 +764,16 @@ function assert_options(int $option, mixed $value = UNKNOWN): mixed {} /* string.c */ -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function bin2hex(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function hex2bin(string $string): string|false {} function strspn(string $string, string $characters, int $offset = 0, ?int $length = null): int {} @@ -725,13 +787,16 @@ function nl_langinfo(int $item): string|false {} function strcoll(string $string1, string $string2): int {} +/** @compile-time-eval */ function trim(string $string, string $characters = " \n\r\t\v\0"): string {} +/** @compile-time-eval */ function rtrim(string $string, string $characters = " \n\r\t\v\0"): string {} /** @alias rtrim */ function chop(string $string, string $characters = " \n\r\t\v\0"): string {} +/** @compile-time-eval */ function ltrim(string $string, string $characters = " \n\r\t\v\0"): string {} /** @refcount 1 */ @@ -740,9 +805,13 @@ function wordwrap(string $string, int $width = 75, string $break = "\n", bool $c /** * @return array * @refcount 1 + * @compile-time-eval */ function explode(string $separator, string $string, int $limit = PHP_INT_MAX): array {} +/** + * @compile-time-eval + */ function implode(string|array $separator, ?array $array = null): string {} /** @alias implode */ @@ -751,8 +820,10 @@ function join(string|array $separator, ?array $array = null): string {} /** @refcount 1 */ function strtok(string $string, ?string $token = null): string|false {} +/** @compile-time-eval */ function strtoupper(string $string): string {} +/** @compile-time-eval */ function strtolower(string $string): string {} /** @refcount 1 */ @@ -776,26 +847,34 @@ function strstr(string $haystack, string $needle, bool $before_needle = false): /** @alias strstr */ function strchr(string $haystack, string $needle, bool $before_needle = false): string|false {} +/** @compile-time-eval */ function strpos(string $haystack, string $needle, int $offset = 0): int|false {} +/** @compile-time-eval */ function stripos(string $haystack, string $needle, int $offset = 0): int|false {} +/** @compile-time-eval */ function strrpos(string $haystack, string $needle, int $offset = 0): int|false {} +/** @compile-time-eval */ function strripos(string $haystack, string $needle, int $offset = 0): int|false {} /** @refcount 1 */ function strrchr(string $haystack, string $needle): string|false {} +/** @compile-time-eval */ function str_contains(string $haystack, string $needle): bool {} +/** @compile-time-eval */ function str_starts_with(string $haystack, string $needle): bool {} +/** @compile-time-eval */ function str_ends_with(string $haystack, string $needle): bool {} /** @refcount 1 */ function chunk_split(string $string, int $length = 76, string $separator = "\r\n"): string {} +/** @compile-time-eval */ function substr(string $string, int $offset, ?int $length = null): string {} /** @return string|array */ @@ -804,21 +883,33 @@ function substr_replace(array|string $string, array|string $replace, array|int $ /** @refcount 1 */ function quotemeta(string $string): string {} +/** @compile-time-eval */ function ord(string $character): int {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function chr(int $codepoint): string {} +/** @compile-time-eval */ function ucfirst(string $string): string {} +/** @compile-time-eval */ function lcfirst(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function ucwords(string $string, string $separators = " \t\r\n\f\v"): string {} function strtr(string $string, string|array $from, ?string $to = null): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function strrev(string $string): string {} /** @param float $percent */ @@ -837,12 +928,14 @@ function stripslashes(string $string): string {} /** * @param int $count * @return string|array + * @compile-time-eval */ function str_replace(array|string $search, array|string $replace, string|array $subject, &$count = null): string|array {} /** * @param int $count * @return string|array + * @compile-time-eval */ function str_ireplace(array|string $search, array|string $replace, string|array $subject, &$count = null): string|array {} @@ -913,6 +1006,7 @@ function str_word_count(string $string, int $format = 0, ?string $characters = n /** * @return array * @refcount 1 + * @compile-time-eval */ function str_split(string $string, int $length = 1): array {} @@ -1324,16 +1418,25 @@ function getimagesizefromstring(string $string, &$image_info = null): array|fals /** @return true */ function phpinfo(int $flags = INFO_ALL): bool {} // make return type void -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function phpversion(?string $extension = null): string|false {} /** @return true */ -function phpcredits(int $flags = CREDITS_ALL): bool {} // make return type void +function phpcredits(int $flags = CREDITS_ALL): bool {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function php_sapi_name(): string|false {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function php_uname(string $mode = "a"): string {} /** @refcount 1 */ @@ -1414,16 +1517,22 @@ function expm1(float $num): float {} function log1p(float $num): float {} +/** @compile-time-eval */ function pi(): float {} +/** @compile-time-eval */ function is_finite(float $num): bool {} +/** @compile-time-eval */ function is_nan(float $num): bool {} +/** @compile-time-eval */ function intdiv(int $num1, int $num2): int {} +/** @compile-time-eval */ function is_infinite(float $num): bool {} +/** @compile-time-eval */ function pow(mixed $num, mixed $exponent): int|float|object {} function exp(float $num): float {} @@ -1440,19 +1549,31 @@ function deg2rad(float $num): float {} function rad2deg(float $num): float {} +/** @compile-time-eval */ function bindec(string $binary_string): int|float {} +/** @compile-time-eval */ function hexdec(string $hex_string): int|float {} +/** @compile-time-eval */ function octdec(string $octal_string): int|float {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function decbin(int $num): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function decoct(int $num): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function dechex(int $num): string {} /** @refcount 1 */ @@ -1847,20 +1968,32 @@ function uniqid(string $prefix = "", bool $more_entropy = false): string {} */ function parse_url(string $url, int $component = -1): int|string|array|null|false {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function urlencode(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function urldecode(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function rawurlencode(string $string): string {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function rawurldecode(string $string): string {} /** - * @param resource $context + * @param resource|null $context * @return array|false * @refcount 1 */ @@ -1911,7 +2044,10 @@ function var_export(mixed $value, bool $return = false): ?string {} function debug_zval_dump(mixed $value, mixed ...$values): void {} -/** @refcount 1 */ +/** + * @compile-time-eval + * @refcount 1 + */ function serialize(mixed $value): string {} function unserialize(string $data, array $options = []): mixed {} @@ -1922,6 +2058,7 @@ function memory_get_peak_usage(bool $real_usage = false): int {} /* versioning.c */ +/** @compile-time-eval */ function version_compare(string $version1, string $version2, ?string $operator = null): int|bool {} /* win32/codepage.c */ diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index ab7776d630919..1f37885813c09 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 96393b26861733e5c7fc9a12095a29c07ed73928 */ + * Stub hash: cbba5dd593bba640750378c7d668b9e4ea6c979d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -2888,12 +2888,12 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(current, arginfo_current) ZEND_FALIAS(pos, current, arginfo_pos) ZEND_FE(key, arginfo_key) - ZEND_FE(min, arginfo_min) - ZEND_FE(max, arginfo_max) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(min, arginfo_min) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(max, arginfo_max) ZEND_FE(array_walk, arginfo_array_walk) ZEND_FE(array_walk_recursive, arginfo_array_walk_recursive) - ZEND_FE(in_array, arginfo_in_array) - ZEND_FE(array_search, arginfo_array_search) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(in_array, arginfo_in_array) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_search, arginfo_array_search) ZEND_FE(extract, arginfo_extract) ZEND_FE(compact, arginfo_compact) ZEND_FE(array_fill, arginfo_array_fill) @@ -2905,21 +2905,21 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(array_unshift, arginfo_array_unshift) ZEND_FE(array_splice, arginfo_array_splice) ZEND_FE(array_slice, arginfo_array_slice) - ZEND_FE(array_merge, arginfo_array_merge) - ZEND_FE(array_merge_recursive, arginfo_array_merge_recursive) - ZEND_FE(array_replace, arginfo_array_replace) - ZEND_FE(array_replace_recursive, arginfo_array_replace_recursive) - ZEND_FE(array_keys, arginfo_array_keys) - ZEND_FE(array_key_first, arginfo_array_key_first) - ZEND_FE(array_key_last, arginfo_array_key_last) - ZEND_FE(array_values, arginfo_array_values) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_merge, arginfo_array_merge) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_merge_recursive, arginfo_array_merge_recursive) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_replace, arginfo_array_replace) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_replace_recursive, arginfo_array_replace_recursive) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_keys, arginfo_array_keys) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_key_first, arginfo_array_key_first) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_key_last, arginfo_array_key_last) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_values, arginfo_array_values) ZEND_FE(array_count_values, arginfo_array_count_values) ZEND_FE(array_column, arginfo_array_column) ZEND_FE(array_reverse, arginfo_array_reverse) ZEND_FE(array_pad, arginfo_array_pad) - ZEND_FE(array_flip, arginfo_array_flip) - ZEND_FE(array_change_key_case, arginfo_array_change_key_case) - ZEND_FE(array_unique, arginfo_array_unique) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_flip, arginfo_array_flip) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_change_key_case, arginfo_array_change_key_case) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_unique, arginfo_array_unique) ZEND_FE(array_intersect_key, arginfo_array_intersect_key) ZEND_FE(array_intersect_ukey, arginfo_array_intersect_ukey) ZEND_FE(array_intersect, arginfo_array_intersect) @@ -2928,11 +2928,11 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(array_uintersect_assoc, arginfo_array_uintersect_assoc) ZEND_FE(array_intersect_uassoc, arginfo_array_intersect_uassoc) ZEND_FE(array_uintersect_uassoc, arginfo_array_uintersect_uassoc) - ZEND_FE(array_diff_key, arginfo_array_diff_key) - ZEND_FE(array_diff_ukey, arginfo_array_diff_ukey) - ZEND_FE(array_diff, arginfo_array_diff) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_diff_key, arginfo_array_diff_key) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_diff_ukey, arginfo_array_diff_ukey) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_diff, arginfo_array_diff) ZEND_FE(array_udiff, arginfo_array_udiff) - ZEND_FE(array_diff_assoc, arginfo_array_diff_assoc) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(array_diff_assoc, arginfo_array_diff_assoc) ZEND_FE(array_diff_uassoc, arginfo_array_diff_uassoc) ZEND_FE(array_udiff_assoc, arginfo_array_udiff_assoc) ZEND_FE(array_udiff_uassoc, arginfo_array_udiff_uassoc) @@ -2948,8 +2948,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(array_chunk, arginfo_array_chunk) ZEND_FE(array_combine, arginfo_array_combine) ZEND_FE(array_is_list, arginfo_array_is_list) - ZEND_FE(base64_encode, arginfo_base64_encode) - ZEND_FE(base64_decode, arginfo_base64_decode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(base64_encode, arginfo_base64_encode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(base64_decode, arginfo_base64_decode) ZEND_FE(constant, arginfo_constant) ZEND_FE(ip2long, arginfo_ip2long) ZEND_FE(long2ip, arginfo_long2ip) @@ -3017,7 +3017,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(sys_getloadavg, arginfo_sys_getloadavg) #endif ZEND_FE(get_browser, arginfo_get_browser) - ZEND_FE(crc32, arginfo_crc32) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(crc32, arginfo_crc32) ZEND_FE(crypt, arginfo_crypt) #if HAVE_STRPTIME ZEND_DEP_FE(strptime, arginfo_strptime) @@ -3090,57 +3090,57 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(get_html_translation_table, arginfo_get_html_translation_table) ZEND_FE(assert, arginfo_assert) ZEND_FE(assert_options, arginfo_assert_options) - ZEND_FE(bin2hex, arginfo_bin2hex) - ZEND_FE(hex2bin, arginfo_hex2bin) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(bin2hex, arginfo_bin2hex) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(hex2bin, arginfo_hex2bin) ZEND_FE(strspn, arginfo_strspn) ZEND_FE(strcspn, arginfo_strcspn) #if HAVE_NL_LANGINFO ZEND_FE(nl_langinfo, arginfo_nl_langinfo) #endif ZEND_FE(strcoll, arginfo_strcoll) - ZEND_FE(trim, arginfo_trim) - ZEND_FE(rtrim, arginfo_rtrim) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(trim, arginfo_trim) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(rtrim, arginfo_rtrim) ZEND_FALIAS(chop, rtrim, arginfo_chop) - ZEND_FE(ltrim, arginfo_ltrim) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(ltrim, arginfo_ltrim) ZEND_FE(wordwrap, arginfo_wordwrap) - ZEND_FE(explode, arginfo_explode) - ZEND_FE(implode, arginfo_implode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(explode, arginfo_explode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(implode, arginfo_implode) ZEND_FALIAS(join, implode, arginfo_join) ZEND_FE(strtok, arginfo_strtok) - ZEND_FE(strtoupper, arginfo_strtoupper) - ZEND_FE(strtolower, arginfo_strtolower) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strtoupper, arginfo_strtoupper) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strtolower, arginfo_strtolower) ZEND_FE(basename, arginfo_basename) ZEND_FE(dirname, arginfo_dirname) ZEND_FE(pathinfo, arginfo_pathinfo) ZEND_FE(stristr, arginfo_stristr) ZEND_FE(strstr, arginfo_strstr) ZEND_FALIAS(strchr, strstr, arginfo_strchr) - ZEND_FE(strpos, arginfo_strpos) - ZEND_FE(stripos, arginfo_stripos) - ZEND_FE(strrpos, arginfo_strrpos) - ZEND_FE(strripos, arginfo_strripos) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strpos, arginfo_strpos) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(stripos, arginfo_stripos) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strrpos, arginfo_strrpos) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strripos, arginfo_strripos) ZEND_FE(strrchr, arginfo_strrchr) - ZEND_FE(str_contains, arginfo_str_contains) - ZEND_FE(str_starts_with, arginfo_str_starts_with) - ZEND_FE(str_ends_with, arginfo_str_ends_with) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_contains, arginfo_str_contains) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_starts_with, arginfo_str_starts_with) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_ends_with, arginfo_str_ends_with) ZEND_FE(chunk_split, arginfo_chunk_split) - ZEND_FE(substr, arginfo_substr) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(substr, arginfo_substr) ZEND_FE(substr_replace, arginfo_substr_replace) ZEND_FE(quotemeta, arginfo_quotemeta) - ZEND_FE(ord, arginfo_ord) - ZEND_FE(chr, arginfo_chr) - ZEND_FE(ucfirst, arginfo_ucfirst) - ZEND_FE(lcfirst, arginfo_lcfirst) - ZEND_FE(ucwords, arginfo_ucwords) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(ord, arginfo_ord) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(chr, arginfo_chr) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(ucfirst, arginfo_ucfirst) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(lcfirst, arginfo_lcfirst) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(ucwords, arginfo_ucwords) ZEND_FE(strtr, arginfo_strtr) - ZEND_FE(strrev, arginfo_strrev) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strrev, arginfo_strrev) ZEND_FE(similar_text, arginfo_similar_text) ZEND_FE(addcslashes, arginfo_addcslashes) ZEND_FE(addslashes, arginfo_addslashes) ZEND_FE(stripcslashes, arginfo_stripcslashes) ZEND_FE(stripslashes, arginfo_stripslashes) - ZEND_FE(str_replace, arginfo_str_replace) - ZEND_FE(str_ireplace, arginfo_str_ireplace) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_replace, arginfo_str_replace) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_ireplace, arginfo_str_ireplace) ZEND_FE(hebrev, arginfo_hebrev) ZEND_FE(nl2br, arginfo_nl2br) ZEND_FE(strip_tags, arginfo_strip_tags) @@ -3158,7 +3158,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(str_rot13, arginfo_str_rot13) ZEND_FE(str_shuffle, arginfo_str_shuffle) ZEND_FE(str_word_count, arginfo_str_word_count) - ZEND_FE(str_split, arginfo_str_split) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(str_split, arginfo_str_split) ZEND_FE(strpbrk, arginfo_strpbrk) ZEND_FE(substr_compare, arginfo_substr_compare) ZEND_FE(utf8_encode, arginfo_utf8_encode) @@ -3278,10 +3278,10 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(getimagesize, arginfo_getimagesize) ZEND_FE(getimagesizefromstring, arginfo_getimagesizefromstring) ZEND_FE(phpinfo, arginfo_phpinfo) - ZEND_FE(phpversion, arginfo_phpversion) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(phpversion, arginfo_phpversion) ZEND_FE(phpcredits, arginfo_phpcredits) - ZEND_FE(php_sapi_name, arginfo_php_sapi_name) - ZEND_FE(php_uname, arginfo_php_uname) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(php_sapi_name, arginfo_php_sapi_name) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(php_uname, arginfo_php_uname) ZEND_FE(php_ini_scanned_files, arginfo_php_ini_scanned_files) ZEND_FE(php_ini_loaded_file, arginfo_php_ini_loaded_file) ZEND_FE(iptcembed, arginfo_iptcembed) @@ -3319,12 +3319,12 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(acosh, arginfo_acosh) ZEND_FE(expm1, arginfo_expm1) ZEND_FE(log1p, arginfo_log1p) - ZEND_FE(pi, arginfo_pi) - ZEND_FE(is_finite, arginfo_is_finite) - ZEND_FE(is_nan, arginfo_is_nan) - ZEND_FE(intdiv, arginfo_intdiv) - ZEND_FE(is_infinite, arginfo_is_infinite) - ZEND_FE(pow, arginfo_pow) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(pi, arginfo_pi) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(is_finite, arginfo_is_finite) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(is_nan, arginfo_is_nan) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(intdiv, arginfo_intdiv) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(is_infinite, arginfo_is_infinite) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(pow, arginfo_pow) ZEND_FE(exp, arginfo_exp) ZEND_FE(log, arginfo_log) ZEND_FE(log10, arginfo_log10) @@ -3332,12 +3332,12 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(hypot, arginfo_hypot) ZEND_FE(deg2rad, arginfo_deg2rad) ZEND_FE(rad2deg, arginfo_rad2deg) - ZEND_FE(bindec, arginfo_bindec) - ZEND_FE(hexdec, arginfo_hexdec) - ZEND_FE(octdec, arginfo_octdec) - ZEND_FE(decbin, arginfo_decbin) - ZEND_FE(decoct, arginfo_decoct) - ZEND_FE(dechex, arginfo_dechex) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(bindec, arginfo_bindec) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(hexdec, arginfo_hexdec) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(octdec, arginfo_octdec) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(decbin, arginfo_decbin) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(decoct, arginfo_decoct) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(dechex, arginfo_dechex) ZEND_FE(base_convert, arginfo_base_convert) ZEND_FE(number_format, arginfo_number_format) ZEND_FE(fmod, arginfo_fmod) @@ -3459,10 +3459,10 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(uniqid, arginfo_uniqid) #endif ZEND_FE(parse_url, arginfo_parse_url) - ZEND_FE(urlencode, arginfo_urlencode) - ZEND_FE(urldecode, arginfo_urldecode) - ZEND_FE(rawurlencode, arginfo_rawurlencode) - ZEND_FE(rawurldecode, arginfo_rawurldecode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(urlencode, arginfo_urlencode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(urldecode, arginfo_urldecode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(rawurlencode, arginfo_rawurlencode) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(rawurldecode, arginfo_rawurldecode) ZEND_FE(get_headers, arginfo_get_headers) ZEND_FE(stream_bucket_make_writeable, arginfo_stream_bucket_make_writeable) ZEND_FE(stream_bucket_prepend, arginfo_stream_bucket_prepend) @@ -3475,11 +3475,11 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(var_dump, arginfo_var_dump) ZEND_FE(var_export, arginfo_var_export) ZEND_FE(debug_zval_dump, arginfo_debug_zval_dump) - ZEND_FE(serialize, arginfo_serialize) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(serialize, arginfo_serialize) ZEND_FE(unserialize, arginfo_unserialize) ZEND_FE(memory_get_usage, arginfo_memory_get_usage) ZEND_FE(memory_get_peak_usage, arginfo_memory_get_peak_usage) - ZEND_FE(version_compare, arginfo_version_compare) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(version_compare, arginfo_version_compare) #if defined(PHP_WIN32) ZEND_FE(sapi_windows_cp_set, arginfo_sapi_windows_cp_set) #endif diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 5299006892797..2e78b2df01888 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -460,8 +460,10 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32. http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \ var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \ filters.c proc_open.c streamsfuncs.c http.c password.c \ - random.c net.c hrtime.c crc32_x86.c,,, + random.c net.c hrtime.c crc32_x86.c libavifinfo/avifinfo.c,,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) +PHP_ADD_BUILD_DIR($ext_builddir/libavifinfo) + PHP_ADD_MAKEFILE_FRAGMENT PHP_INSTALL_HEADERS([ext/standard/]) diff --git a/ext/standard/config.w32 b/ext/standard/config.w32 index 3de43d04c067e..1813210849344 100644 --- a/ext/standard/config.w32 +++ b/ext/standard/config.w32 @@ -37,6 +37,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \ user_filters.c uuencode.c filters.c proc_open.c password.c \ streamsfuncs.c http.c flock_compat.c random.c hrtime.c", false /* never shared */, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); +ADD_SOURCES("ext/standard/libavifinfo", "avifinfo.c", "standard"); PHP_STANDARD = "yes"; ADD_MAKEFILE_FRAGMENT(); PHP_INSTALL_HEADERS("", "ext/standard"); diff --git a/ext/standard/dns.c b/ext/standard/dns.c index e88e5a79db114..6d22e644a8eff 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -182,14 +182,14 @@ static zend_string *php_gethostbyaddr(char *ip) if (inet_pton(AF_INET6, ip, &sa6.sin6_addr)) { sa6.sin6_family = AF_INET6; - if (getnameinfo((struct sockaddr *)&sa6, sizeof(sa6), out, sizeof(out), NULL, 0, NI_NAMEREQD) < 0) { + if (getnameinfo((struct sockaddr *)&sa6, sizeof(sa6), out, sizeof(out), NULL, 0, NI_NAMEREQD) != 0) { return zend_string_init(ip, strlen(ip), 0); } return zend_string_init(out, strlen(out), 0); } else if (inet_pton(AF_INET, ip, &sa4.sin_addr)) { sa4.sin_family = AF_INET; - if (getnameinfo((struct sockaddr *)&sa4, sizeof(sa4), out, sizeof(out), NULL, 0, NI_NAMEREQD) < 0) { + if (getnameinfo((struct sockaddr *)&sa4, sizeof(sa4), out, sizeof(out), NULL, 0, NI_NAMEREQD) != 0) { return zend_string_init(ip, strlen(ip), 0); } return zend_string_init(out, strlen(out), 0); diff --git a/ext/standard/image.c b/ext/standard/image.c index 85ecda2f3d70b..a614521ae1091 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -22,6 +22,7 @@ #endif #include "fopen_wrappers.h" #include "ext/standard/fsock.h" +#include "libavifinfo/avifinfo.h" #if HAVE_UNISTD_H #include #endif @@ -1155,95 +1156,76 @@ static struct gfxinfo *php_handle_webp(php_stream * stream) } /* }}} */ -/* {{{ php_handle_avif - * There's no simple way to get this information - so, for now, this is unsupported. - * Simply return 0 for everything. - */ -static struct gfxinfo *php_handle_avif(php_stream * stream) { - return ecalloc(1, sizeof(struct gfxinfo)); -} -/* }}} */ - -/* {{{ php_ntohl - * Convert a big-endian network uint32 to host order - - * which may be either little-endian or big-endian. - * Thanks to Rob Pike via Joe Drago: - * https://commandcenter.blogspot.nl/2012/04/byte-order-fallacy.html - */ -static uint32_t php_ntohl(uint32_t val) { - uint8_t data[4]; - - memcpy(&data, &val, sizeof(data)); - return ((uint32_t)data[3] << 0) | - ((uint32_t)data[2] << 8) | - ((uint32_t)data[1] << 16) | - ((uint32_t)data[0] << 24); -} -/* }}} */ - -/* {{{ php_is_image_avif - * detect whether an image is of type AVIF - * - * An AVIF image will start off a header "box". - * This starts with with a four-byte integer containing the number of bytes in the filetype box. - * This must be followed by the string "ftyp". - * Next comes a four-byte string indicating the "major brand". - * If that's "avif" or "avis", this is an AVIF image. - * Next, there's a four-byte "minor version" field, which we can ignore. - * Next comes an array of four-byte strings containing "compatible brands". - * These extend to the end of the box. - * If any of the compatible brands is "avif" or "avis", then this is an AVIF image. - * Otherwise, well, it's not. - * For more, see https://mpeg.chiariglione.org/standards/mpeg-4/iso-base-media-file-format/text-isoiec-14496-12-5th-edition - */ -bool php_is_image_avif(php_stream * stream) { - uint32_t header_size_reversed, header_size, i; - char box_type[4], brand[4]; +/* {{{ User struct and stream read/skip implementations for libavifinfo API */ +struct php_avif_stream { + php_stream* stream; + uint8_t buffer[AVIFINFO_MAX_NUM_READ_BYTES]; +}; - ZEND_ASSERT(stream != NULL); +static const uint8_t* php_avif_stream_read(void* stream, size_t num_bytes) { + struct php_avif_stream* avif_stream = (struct php_avif_stream*)stream; - if (php_stream_read(stream, (char *) &header_size_reversed, 4) != 4) { - return 0; + if (avif_stream == NULL || avif_stream->stream == NULL) { + return NULL; } - - header_size = php_ntohl(header_size_reversed); - - /* If the box type isn't "ftyp", it can't be an AVIF image. */ - if (php_stream_read(stream, box_type, 4) != 4) { - return 0; + if (php_stream_read(avif_stream->stream, (char*)avif_stream->buffer, num_bytes) != num_bytes) { + avif_stream->stream = NULL; /* fail further calls */ + return NULL; } + return avif_stream->buffer; +} - if (memcmp(box_type, "ftyp", 4)) { - return 0; - } - - /* If the major brand is "avif" or "avis", it's an AVIF image. */ - if (php_stream_read(stream, brand, 4) != 4) { - return 0; - } +static void php_avif_stream_skip(void* stream, size_t num_bytes) { + struct php_avif_stream* avif_stream = (struct php_avif_stream*)stream; - if (!memcmp(brand, "avif", 4) || !memcmp(brand, "avis", 4)) { - return 1; + if (avif_stream == NULL || avif_stream->stream == NULL) { + return; } - - /* Skip the next four bytes, which are the "minor version". */ - if (php_stream_read(stream, brand, 4) != 4) { - return 0; + if (php_stream_seek(avif_stream->stream, num_bytes, SEEK_CUR)) { + avif_stream->stream = NULL; /* fail further calls */ } +} +/* }}} */ - /* Look for "avif" or "avis" in any member of compatible_brands[], to the end of the header. - Note we've already read four groups of four bytes. */ +/* {{{ php_handle_avif + * Parse AVIF features + * + * The stream must be positioned at the beginning of a box, so it does not + * matter whether the "ftyp" box was already read by php_is_image_avif() or not. + * It will read bytes from the stream until features are found or the file is + * declared as invalid. Around 450 bytes are usually enough. + * Transforms such as mirror and rotation are not applied on width and height. + */ +static struct gfxinfo *php_handle_avif(php_stream * stream) { + struct gfxinfo* result = NULL; + AvifInfoFeatures features; + struct php_avif_stream avif_stream; + avif_stream.stream = stream; + + if (AvifInfoGetFeaturesStream(&avif_stream, php_avif_stream_read, php_avif_stream_skip, &features) == kAvifInfoOk) { + result = (struct gfxinfo*)ecalloc(1, sizeof(struct gfxinfo)); + result->width = features.width; + result->height = features.height; + result->bits = features.bit_depth; + result->channels = features.num_channels; + } + return result; +} +/* }}} */ - for (i = 16; i < header_size; i += 4) { - if (php_stream_read(stream, brand, 4) != 4) { - return 0; - } +/* {{{ php_is_image_avif + * Detect whether an image is of type AVIF + * + * Only the first "ftyp" box is read. + * For a valid file, 12 bytes are usually read, but more might be necessary. + */ +bool php_is_image_avif(php_stream* stream) { + struct php_avif_stream avif_stream; + avif_stream.stream = stream; - if (!memcmp(brand, "avif", 4) || !memcmp(brand, "avis", 4)) { - return 1; - } + if (AvifInfoIdentifyStream(&avif_stream, php_avif_stream_read, php_avif_stream_skip) == kAvifInfoOk) { + return 1; } - return 0; } /* }}} */ diff --git a/ext/standard/libavifinfo/LICENSE b/ext/standard/libavifinfo/LICENSE new file mode 100644 index 0000000000000..330b6b87bb6e8 --- /dev/null +++ b/ext/standard/libavifinfo/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2021, Alliance for Open Media. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/ext/standard/libavifinfo/PATENTS b/ext/standard/libavifinfo/PATENTS new file mode 100644 index 0000000000000..826644b635355 --- /dev/null +++ b/ext/standard/libavifinfo/PATENTS @@ -0,0 +1,107 @@ +Alliance for Open Media Patent License 1.0 + +1. License Terms. + +1.1. Patent License. Subject to the terms and conditions of this License, each + Licensor, on behalf of itself and successors in interest and assigns, + grants Licensee a non-sublicensable, perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as expressly stated in this + License) patent license to its Necessary Claims to make, use, sell, offer + for sale, import or distribute any Implementation. + +1.2. Conditions. + +1.2.1. Availability. As a condition to the grant of rights to Licensee to make, + sell, offer for sale, import or distribute an Implementation under + Section 1.1, Licensee must make its Necessary Claims available under + this License, and must reproduce this License with any Implementation + as follows: + + a. For distribution in source code, by including this License in the + root directory of the source code with its Implementation. + + b. For distribution in any other form (including binary, object form, + and/or hardware description code (e.g., HDL, RTL, Gate Level Netlist, + GDSII, etc.)), by including this License in the documentation, legal + notices, and/or other written materials provided with the + Implementation. + +1.2.2. Additional Conditions. This license is directly from Licensor to + Licensee. Licensee acknowledges as a condition of benefiting from it + that no rights from Licensor are received from suppliers, distributors, + or otherwise in connection with this License. + +1.3. Defensive Termination. If any Licensee, its Affiliates, or its agents + initiates patent litigation or files, maintains, or voluntarily + participates in a lawsuit against another entity or any person asserting + that any Implementation infringes Necessary Claims, any patent licenses + granted under this License directly to the Licensee are immediately + terminated as of the date of the initiation of action unless 1) that suit + was in response to a corresponding suit regarding an Implementation first + brought against an initiating entity, or 2) that suit was brought to + enforce the terms of this License (including intervention in a third-party + action by a Licensee). + +1.4. Disclaimers. The Reference Implementation and Specification are provided + "AS IS" and without warranty. The entire risk as to implementing or + otherwise using the Reference Implementation or Specification is assumed + by the implementer and user. Licensor expressly disclaims any warranties + (express, implied, or otherwise), including implied warranties of + merchantability, non-infringement, fitness for a particular purpose, or + title, related to the material. IN NO EVENT WILL LICENSOR BE LIABLE TO + ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, + INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF + ACTION OF ANY KIND WITH RESPECT TO THIS LICENSE, WHETHER BASED ON BREACH + OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR + NOT THE OTHER PARTRY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +2. Definitions. + +2.1. Affiliate. "Affiliate" means an entity that directly or indirectly + Controls, is Controlled by, or is under common Control of that party. + +2.2. Control. "Control" means direct or indirect control of more than 50% of + the voting power to elect directors of that corporation, or for any other + entity, the power to direct management of such entity. + +2.3. Decoder. "Decoder" means any decoder that conforms fully with all + non-optional portions of the Specification. + +2.4. Encoder. "Encoder" means any encoder that produces a bitstream that can + be decoded by a Decoder only to the extent it produces such a bitstream. + +2.5. Final Deliverable. "Final Deliverable" means the final version of a + deliverable approved by the Alliance for Open Media as a Final + Deliverable. + +2.6. Implementation. "Implementation" means any implementation, including the + Reference Implementation, that is an Encoder and/or a Decoder. An + Implementation also includes components of an Implementation only to the + extent they are used as part of an Implementation. + +2.7. License. "License" means this license. + +2.8. Licensee. "Licensee" means any person or entity who exercises patent + rights granted under this License. + +2.9. Licensor. "Licensor" means (i) any Licensee that makes, sells, offers + for sale, imports or distributes any Implementation, or (ii) a person + or entity that has a licensing obligation to the Implementation as a + result of its membership and/or participation in the Alliance for Open + Media working group that developed the Specification. + +2.10. Necessary Claims. "Necessary Claims" means all claims of patents or + patent applications, (a) that currently or at any time in the future, + are owned or controlled by the Licensor, and (b) (i) would be an + Essential Claim as defined by the W3C Policy as of February 5, 2004 + (https://www.w3.org/Consortium/Patent-Policy-20040205/#def-essential) + as if the Specification was a W3C Recommendation; or (ii) are infringed + by the Reference Implementation. + +2.11. Reference Implementation. "Reference Implementation" means an Encoder + and/or Decoder released by the Alliance for Open Media as a Final + Deliverable. + +2.12. Specification. "Specification" means the specification designated by + the Alliance for Open Media as a Final Deliverable for which this + License was issued. diff --git a/ext/standard/libavifinfo/README.md b/ext/standard/libavifinfo/README.md new file mode 100644 index 0000000000000..a08c29067f4b9 --- /dev/null +++ b/ext/standard/libavifinfo/README.md @@ -0,0 +1,11 @@ +# AVIF-info + +There is no compact, reliable way to determine the size of an AVIF image. A +standalone C snippet called +[libavifinfo](https://aomedia.googlesource.com/libavifinfo) was created to +partially parse an AVIF payload and to extract the width, height, bit depth and +channel count without depending on the full libavif library. + +`avifinfo.h`, `avifinfo.c`, `LICENSE` and `PATENTS` were copied verbatim from: \ +https://aomedia.googlesource.com/libavifinfo/+/96f34d945ac7dac229feddfa94dbae66e202b838 \ +They can easily be kept up-to-date the same way. diff --git a/ext/standard/libavifinfo/avifinfo.c b/ext/standard/libavifinfo/avifinfo.c new file mode 100644 index 0000000000000..5b93d1997be33 --- /dev/null +++ b/ext/standard/libavifinfo/avifinfo.c @@ -0,0 +1,745 @@ +// Copyright (c) 2021, Alliance for Open Media. All rights reserved +// +// This source code is subject to the terms of the BSD 2 Clause License and +// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License +// was not distributed with this source code in the LICENSE file, you can +// obtain it at www.aomedia.org/license/software. If the Alliance for Open +// Media Patent License 1.0 was not distributed with this source code in the +// PATENTS file, you can obtain it at www.aomedia.org/license/patent. + +#include "avifinfo.h" + +#include +#include +#include + +//------------------------------------------------------------------------------ + +// Status returned when reading the content of a box (or file). +typedef enum { + kFound, // Input correctly parsed and information retrieved. + kNotFound, // Input correctly parsed but information is missing or elsewhere. + kTruncated, // Input correctly parsed until missing bytes to continue. + kAborted, // Input correctly parsed until stopped to avoid timeout or crash. + kInvalid, // Input incorrectly parsed. +} AvifInfoInternalStatus; + +static AvifInfoStatus AvifInfoInternalConvertStatus(AvifInfoInternalStatus s) { + return (s == kFound) ? kAvifInfoOk + : (s == kNotFound || s == kTruncated) ? kAvifInfoNotEnoughData + : (s == kAborted) ? kAvifInfoTooComplex + : kAvifInfoInvalidFile; +} + +// uint32_t is used everywhere in this file. It is unlikely to be insufficient +// to parse AVIF headers. +#define AVIFINFO_MAX_SIZE UINT32_MAX +// AvifInfoInternalFeatures uses uint8_t to store values and the number of +// values is clamped to 32 to limit the stack size. +#define AVIFINFO_MAX_VALUE UINT8_MAX +#define AVIFINFO_UNDEFINED 0 +// Maximum number of stored associations. Past that, they are skipped. +#define AVIFINFO_MAX_TILES 16 +#define AVIFINFO_MAX_PROPS 32 +#define AVIFINFO_MAX_FEATURES 8 + +// Reads an unsigned integer from 'input' with most significant bits first. +// 'input' must be at least 'num_bytes'-long. +static uint32_t AvifInfoInternalReadBigEndian(const uint8_t* input, + uint32_t num_bytes) { + uint32_t value = 0; + for (uint32_t i = 0; i < num_bytes; ++i) { + value = (value << 8) | input[i]; + } + return value; +} + +//------------------------------------------------------------------------------ +// Convenience macros. + +#if defined(AVIFINFO_LOG_ERROR) // Toggle to log encountered issues. +static void AvifInfoInternalLogError(const char* file, int line, + AvifInfoInternalStatus status) { + const char* kStr[] = {"Found", "NotFound", "Truncated", "Invalid", "Aborted"}; + fprintf(stderr, " %s:%d: %s\n", file, line, kStr[status]); + // Set a breakpoint here to catch the first detected issue. +} +#define AVIFINFO_RETURN(check_status) \ + do { \ + const AvifInfoInternalStatus status_checked = (check_status); \ + if (status_checked != kFound && status_checked != kNotFound) { \ + AvifInfoInternalLogError(__FILE__, __LINE__, status_checked); \ + } \ + return status_checked; \ + } while (0) +#else +#define AVIFINFO_RETURN(check_status) \ + do { \ + return (check_status); \ + } while (0) +#endif + +#define AVIFINFO_CHECK(check_condition, check_status) \ + do { \ + if (!(check_condition)) AVIFINFO_RETURN(check_status); \ + } while (0) +#define AVIFINFO_CHECK_STATUS_IS(check_status, expected_status) \ + do { \ + const AvifInfoInternalStatus status_returned = (check_status); \ + AVIFINFO_CHECK(status_returned == (expected_status), status_returned); \ + } while (0) +#define AVIFINFO_CHECK_FOUND(check_status) \ + AVIFINFO_CHECK_STATUS_IS((check_status), kFound) +#define AVIFINFO_CHECK_NOT_FOUND(check_status) \ + AVIFINFO_CHECK_STATUS_IS((check_status), kNotFound) + +//------------------------------------------------------------------------------ +// Streamed input struct and helper functions. + +typedef struct { + void* stream; // User-defined data. + read_stream_t read; // Used to fetch more bytes from the 'stream'. + skip_stream_t skip; // Used to advance the position in the 'stream'. + // Fallback to 'read' if 'skip' is null. +} AvifInfoInternalStream; + +// Reads 'num_bytes' from the 'stream'. They are available at '*data'. +// 'num_bytes' must be greater than zero. +static AvifInfoInternalStatus AvifInfoInternalRead( + AvifInfoInternalStream* stream, uint32_t num_bytes, const uint8_t** data) { + *data = stream->read(stream->stream, num_bytes); + AVIFINFO_CHECK(*data != NULL, kTruncated); + return kFound; +} + +// Skips 'num_bytes' from the 'stream'. 'num_bytes' can be zero. +static AvifInfoInternalStatus AvifInfoInternalSkip( + AvifInfoInternalStream* stream, uint32_t num_bytes) { + // Avoid a call to the user-defined function for nothing. + if (num_bytes > 0) { + if (stream->skip == NULL) { + const uint8_t* unused; + while (num_bytes > AVIFINFO_MAX_NUM_READ_BYTES) { + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, AVIFINFO_MAX_NUM_READ_BYTES, &unused)); + num_bytes -= AVIFINFO_MAX_NUM_READ_BYTES; + } + return AvifInfoInternalRead(stream, num_bytes, &unused); + } + stream->skip(stream->stream, num_bytes); + } + return kFound; +} + +//------------------------------------------------------------------------------ +// Features are parsed into temporary property associations. + +typedef struct { + uint8_t tile_item_id; + uint8_t parent_item_id; +} AvifInfoInternalTile; // Tile item id <-> parent item id associations. + +typedef struct { + uint8_t property_index; + uint8_t item_id; +} AvifInfoInternalProp; // Property index <-> item id associations. + +typedef struct { + uint8_t property_index; + uint32_t width, height; +} AvifInfoInternalDimProp; // Property <-> features associations. + +typedef struct { + uint8_t property_index; + uint8_t bit_depth, num_channels; +} AvifInfoInternalChanProp; // Property <-> features associations. + +typedef struct { + uint8_t has_primary_item; // True if "pitm" was parsed. + uint8_t has_alpha; // True if an alpha "auxC" was parsed. + uint8_t primary_item_id; + AvifInfoFeatures primary_item_features; // Deduced from the data below. + uint8_t data_was_skipped; // True if some loops/indices were skipped. + + uint8_t num_tiles; + AvifInfoInternalTile tiles[AVIFINFO_MAX_TILES]; + uint8_t num_props; + AvifInfoInternalProp props[AVIFINFO_MAX_PROPS]; + uint8_t num_dim_props; + AvifInfoInternalDimProp dim_props[AVIFINFO_MAX_FEATURES]; + uint8_t num_chan_props; + AvifInfoInternalChanProp chan_props[AVIFINFO_MAX_FEATURES]; +} AvifInfoInternalFeatures; + +// Generates the features of a given 'target_item_id' from internal features. +static AvifInfoInternalStatus AvifInfoInternalGetItemFeatures( + AvifInfoInternalFeatures* f, uint32_t target_item_id, uint32_t tile_depth) { + for (uint32_t prop_item = 0; prop_item < f->num_props; ++prop_item) { + if (f->props[prop_item].item_id != target_item_id) continue; + const uint32_t property_index = f->props[prop_item].property_index; + + // Retrieve the width and height of the primary item if not already done. + if (target_item_id == f->primary_item_id && + (f->primary_item_features.width == AVIFINFO_UNDEFINED || + f->primary_item_features.height == AVIFINFO_UNDEFINED)) { + for (uint32_t i = 0; i < f->num_dim_props; ++i) { + if (f->dim_props[i].property_index != property_index) continue; + f->primary_item_features.width = f->dim_props[i].width; + f->primary_item_features.height = f->dim_props[i].height; + if (f->primary_item_features.bit_depth != AVIFINFO_UNDEFINED && + f->primary_item_features.num_channels != AVIFINFO_UNDEFINED) { + return kFound; + } + break; + } + } + // Retrieve the bit depth and number of channels of the target item if not + // already done. + if (f->primary_item_features.bit_depth == AVIFINFO_UNDEFINED || + f->primary_item_features.num_channels == AVIFINFO_UNDEFINED) { + for (uint32_t i = 0; i < f->num_chan_props; ++i) { + if (f->chan_props[i].property_index != property_index) continue; + f->primary_item_features.bit_depth = f->chan_props[i].bit_depth; + f->primary_item_features.num_channels = f->chan_props[i].num_channels; + if (f->primary_item_features.width != AVIFINFO_UNDEFINED && + f->primary_item_features.height != AVIFINFO_UNDEFINED) { + return kFound; + } + break; + } + } + } + + // Check for the bit_depth and num_channels in a tile if not yet found. + for (uint32_t tile = 0; tile < f->num_tiles && tile_depth < 3; ++tile) { + if (f->tiles[tile].parent_item_id != target_item_id) continue; + AVIFINFO_CHECK_NOT_FOUND(AvifInfoInternalGetItemFeatures( + f, f->tiles[tile].tile_item_id, tile_depth + 1)); + } + AVIFINFO_RETURN(kNotFound); +} + +// Generates the 'f->primary_item_features' from the AvifInfoInternalFeatures. +// Returns kNotFound if there is not enough information. +static AvifInfoInternalStatus AvifInfoInternalGetPrimaryItemFeatures( + AvifInfoInternalFeatures* f) { + // Nothing to do without the primary item ID. + AVIFINFO_CHECK(f->has_primary_item, kNotFound); + // Early exit. + AVIFINFO_CHECK(f->num_dim_props > 0 && f->num_chan_props, kNotFound); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalGetItemFeatures(f, f->primary_item_id, /*tile_depth=*/0)); + + // "auxC" is parsed before the "ipma" properties so it is known now, if any. + if (f->has_alpha) ++f->primary_item_features.num_channels; + return kFound; +} + +//------------------------------------------------------------------------------ +// Box header parsing and various size checks. + +typedef struct { + uint32_t size; // In bytes. + uint8_t type[4]; // Four characters. + uint32_t version; // 0 or actual version if this is a full box. + uint32_t flags; // 0 or actual value if this is a full box. + uint32_t content_size; // 'size' minus the header size. +} AvifInfoInternalBox; + +// Reads the header of a 'box' starting at the beginning of a 'stream'. +// 'num_remaining_bytes' is the remaining size of the container of the 'box' +// (either the file size itself or the content size of the parent of the 'box'). +static AvifInfoInternalStatus AvifInfoInternalParseBox( + AvifInfoInternalStream* stream, uint32_t num_remaining_bytes, + uint32_t* num_parsed_boxes, AvifInfoInternalBox* box) { + const uint8_t* data; + // See ISO/IEC 14496-12:2012(E) 4.2 + uint32_t box_header_size = 8; // box 32b size + 32b type (at least) + AVIFINFO_CHECK(box_header_size <= num_remaining_bytes, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 8, &data)); + box->size = AvifInfoInternalReadBigEndian(data, sizeof(uint32_t)); + memcpy(box->type, data + 4, 4); + // 'box->size==1' means 64-bit size should be read after the box type. + // 'box->size==0' means this box extends to all remaining bytes. + if (box->size == 1) { + box_header_size += 8; + AVIFINFO_CHECK(box_header_size <= num_remaining_bytes, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 8, &data)); + // Stop the parsing if any box has a size greater than 4GB. + AVIFINFO_CHECK(AvifInfoInternalReadBigEndian(data, sizeof(uint32_t)) == 0, + kAborted); + // Read the 32 least-significant bits. + box->size = AvifInfoInternalReadBigEndian(data + 4, sizeof(uint32_t)); + } else if (box->size == 0) { + box->size = num_remaining_bytes; + } + AVIFINFO_CHECK(box->size >= box_header_size, kInvalid); + AVIFINFO_CHECK(box->size <= num_remaining_bytes, kInvalid); + + const int has_fullbox_header = + !memcmp(box->type, "meta", 4) || !memcmp(box->type, "pitm", 4) || + !memcmp(box->type, "ipma", 4) || !memcmp(box->type, "ispe", 4) || + !memcmp(box->type, "pixi", 4) || !memcmp(box->type, "iref", 4) || + !memcmp(box->type, "auxC", 4); + if (has_fullbox_header) box_header_size += 4; + AVIFINFO_CHECK(box->size >= box_header_size, kInvalid); + box->content_size = box->size - box_header_size; + // Avoid timeouts. The maximum number of parsed boxes is arbitrary. + ++*num_parsed_boxes; + AVIFINFO_CHECK(*num_parsed_boxes < 4096, kAborted); + + box->version = 0; + box->flags = 0; + if (has_fullbox_header) { + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 4, &data)); + box->version = AvifInfoInternalReadBigEndian(data, 1); + box->flags = AvifInfoInternalReadBigEndian(data + 1, 3); + // See AV1 Image File Format (AVIF) 8.1 + // at https://aomediacodec.github.io/av1-avif/#avif-boxes (available when + // https://github.com/AOMediaCodec/av1-avif/pull/170 is merged). + uint32_t is_parsable = 1; + if (!memcmp(box->type, "meta", 4)) is_parsable = (box->version <= 0); + if (!memcmp(box->type, "pitm", 4)) is_parsable = (box->version <= 1); + if (!memcmp(box->type, "ipma", 4)) is_parsable = (box->version <= 1); + if (!memcmp(box->type, "ispe", 4)) is_parsable = (box->version <= 0); + if (!memcmp(box->type, "pixi", 4)) is_parsable = (box->version <= 0); + if (!memcmp(box->type, "iref", 4)) is_parsable = (box->version <= 1); + if (!memcmp(box->type, "auxC", 4)) is_parsable = (box->version <= 0); + // Instead of considering this file as invalid, skip unparsable boxes. + if (!is_parsable) memcpy(box->type, "\0skp", 4); // \0 so not a valid type + } + return kFound; +} + +//------------------------------------------------------------------------------ + +// Parses a 'stream' of an "ipco" box into 'features'. +// "ispe" is used for width and height, "pixi" and "av1C" are used for bit depth +// and number of channels, and "auxC" is used for alpha. +static AvifInfoInternalStatus ParseIpco(AvifInfoInternalStream* stream, + uint32_t num_remaining_bytes, + uint32_t* num_parsed_boxes, + AvifInfoInternalFeatures* features) { + uint32_t box_index = 1; // 1-based index. Used for iterating over properties. + do { + AvifInfoInternalBox box; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes, + num_parsed_boxes, &box)); + + if (!memcmp(box.type, "ispe", 4)) { + // See ISO/IEC 23008-12:2017(E) 6.5.3.2 + const uint8_t* data; + AVIFINFO_CHECK(box.content_size >= 8, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 8, &data)); + const uint32_t width = AvifInfoInternalReadBigEndian(data + 0, 4); + const uint32_t height = AvifInfoInternalReadBigEndian(data + 4, 4); + AVIFINFO_CHECK(width != 0 && height != 0, kInvalid); + if (features->num_dim_props < AVIFINFO_MAX_FEATURES && + box_index <= AVIFINFO_MAX_VALUE) { + features->dim_props[features->num_dim_props].property_index = box_index; + features->dim_props[features->num_dim_props].width = width; + features->dim_props[features->num_dim_props].height = height; + ++features->num_dim_props; + } else { + features->data_was_skipped = 1; + } + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size - 8)); + } else if (!memcmp(box.type, "pixi", 4)) { + // See ISO/IEC 23008-12:2017(E) 6.5.6.2 + const uint8_t* data; + AVIFINFO_CHECK(box.content_size >= 1, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 1, &data)); + const uint32_t num_channels = AvifInfoInternalReadBigEndian(data + 0, 1); + AVIFINFO_CHECK(num_channels >= 1, kInvalid); + AVIFINFO_CHECK(box.content_size >= 1 + num_channels, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 1, &data)); + const uint32_t bit_depth = AvifInfoInternalReadBigEndian(data, 1); + AVIFINFO_CHECK(bit_depth >= 1, kInvalid); + for (uint32_t i = 1; i < num_channels; ++i) { + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 1, &data)); + // Bit depth should be the same for all channels. + AVIFINFO_CHECK(AvifInfoInternalReadBigEndian(data, 1) == bit_depth, + kInvalid); + AVIFINFO_CHECK(i <= 32, kAborted); // Be reasonable. + } + if (features->num_chan_props < AVIFINFO_MAX_FEATURES && + box_index <= AVIFINFO_MAX_VALUE && bit_depth <= AVIFINFO_MAX_VALUE && + num_channels <= AVIFINFO_MAX_VALUE) { + features->chan_props[features->num_chan_props].property_index = + box_index; + features->chan_props[features->num_chan_props].bit_depth = bit_depth; + features->chan_props[features->num_chan_props].num_channels = + num_channels; + ++features->num_chan_props; + } else { + features->data_was_skipped = 1; + } + AVIFINFO_CHECK_FOUND( + AvifInfoInternalSkip(stream, box.content_size - (1 + num_channels))); + } else if (!memcmp(box.type, "av1C", 4)) { + // See AV1 Codec ISO Media File Format Binding 2.3.1 + // at https://aomediacodec.github.io/av1-isobmff/#av1c + // Only parse the necessary third byte. Assume that the others are valid. + const uint8_t* data; + AVIFINFO_CHECK(box.content_size >= 3, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 3, &data)); + const int high_bitdepth = (data[2] & 0x40) != 0; + const int twelve_bit = (data[2] & 0x20) != 0; + const int monochrome = (data[2] & 0x10) != 0; + if (twelve_bit) { + AVIFINFO_CHECK(high_bitdepth, kInvalid); + } + if (features->num_chan_props < AVIFINFO_MAX_FEATURES && + box_index <= AVIFINFO_MAX_VALUE) { + features->chan_props[features->num_chan_props].property_index = + box_index; + features->chan_props[features->num_chan_props].bit_depth = + high_bitdepth ? twelve_bit ? 12 : 10 : 8; + features->chan_props[features->num_chan_props].num_channels = + monochrome ? 1 : 3; + ++features->num_chan_props; + } else { + features->data_was_skipped = 1; + } + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size - 3)); + } else if (!memcmp(box.type, "auxC", 4)) { + // See AV1 Image File Format (AVIF) 4 + // at https://aomediacodec.github.io/av1-avif/#auxiliary-images + const char* kAlphaStr = "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; + const uint32_t kAlphaStrLength = 44; // Includes terminating character. + if (box.content_size >= kAlphaStrLength) { + const uint8_t* data; + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, kAlphaStrLength, &data)); + const char* const aux_type = (const char*)data; + if (strcmp(aux_type, kAlphaStr) == 0) { + // Note: It is unlikely but it is possible that this alpha plane does + // not belong to the primary item or a tile. Ignore this issue. + features->has_alpha = 1; + } + AVIFINFO_CHECK_FOUND( + AvifInfoInternalSkip(stream, box.content_size - kAlphaStrLength)); + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + ++box_index; + num_remaining_bytes -= box.size; + } while (num_remaining_bytes > 0); + AVIFINFO_RETURN(kNotFound); +} + +// Parses a 'stream' of an "iprp" box into 'features'. The "ipco" box contain +// the properties which are linked to items by the "ipma" box. +static AvifInfoInternalStatus ParseIprp(AvifInfoInternalStream* stream, + uint32_t num_remaining_bytes, + uint32_t* num_parsed_boxes, + AvifInfoInternalFeatures* features) { + do { + AvifInfoInternalBox box; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes, + num_parsed_boxes, &box)); + + if (!memcmp(box.type, "ipco", 4)) { + AVIFINFO_CHECK_NOT_FOUND( + ParseIpco(stream, box.content_size, num_parsed_boxes, features)); + } else if (!memcmp(box.type, "ipma", 4)) { + // See ISO/IEC 23008-12:2017(E) 9.3.2 + uint32_t num_read_bytes = 4; + const uint8_t* data; + AVIFINFO_CHECK(box.content_size >= num_read_bytes, kInvalid); + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 4, &data)); + const uint32_t entry_count = AvifInfoInternalReadBigEndian(data, 4); + const uint32_t id_num_bytes = (box.version < 1) ? 2 : 4; + const uint32_t index_num_bytes = (box.flags & 1) ? 2 : 1; + const uint32_t essential_bit_mask = (box.flags & 1) ? 0x8000 : 0x80; + + for (uint32_t entry = 0; entry < entry_count; ++entry) { + if (entry >= AVIFINFO_MAX_PROPS || + features->num_props >= AVIFINFO_MAX_PROPS) { + features->data_was_skipped = 1; + break; + } + num_read_bytes += id_num_bytes + 1; + AVIFINFO_CHECK(box.content_size >= num_read_bytes, kInvalid); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, id_num_bytes + 1, &data)); + const uint32_t item_id = + AvifInfoInternalReadBigEndian(data, id_num_bytes); + const uint32_t association_count = + AvifInfoInternalReadBigEndian(data + id_num_bytes, 1); + + uint32_t property; + for (property = 0; property < association_count; ++property) { + if (property >= AVIFINFO_MAX_PROPS || + features->num_props >= AVIFINFO_MAX_PROPS) { + features->data_was_skipped = 1; + break; + } + num_read_bytes += index_num_bytes; + AVIFINFO_CHECK(box.content_size >= num_read_bytes, kInvalid); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, index_num_bytes, &data)); + const uint32_t value = + AvifInfoInternalReadBigEndian(data, index_num_bytes); + // const int essential = (value & essential_bit_mask); // Unused. + const uint32_t property_index = (value & ~essential_bit_mask); + if (property_index <= AVIFINFO_MAX_VALUE && + item_id <= AVIFINFO_MAX_VALUE) { + features->props[features->num_props].property_index = + property_index; + features->props[features->num_props].item_id = item_id; + ++features->num_props; + } else { + features->data_was_skipped = 1; + } + } + if (property < association_count) break; // Do not read garbage. + } + + // If all features are available now, do not look further. + AVIFINFO_CHECK_NOT_FOUND( + AvifInfoInternalGetPrimaryItemFeatures(features)); + + AVIFINFO_CHECK_FOUND( + AvifInfoInternalSkip(stream, box.content_size - num_read_bytes)); + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + num_remaining_bytes -= box.size; + } while (num_remaining_bytes != 0); + AVIFINFO_RETURN(kNotFound); +} + +//------------------------------------------------------------------------------ + +// Parses a 'stream' of an "iref" box into 'features'. +// The "dimg" boxes contain links between tiles and their parent items, which +// can be used to infer bit depth and number of channels for the primary item +// when the latter does not have these properties. +static AvifInfoInternalStatus ParseIref(AvifInfoInternalStream* stream, + uint32_t num_remaining_bytes, + uint32_t* num_parsed_boxes, + AvifInfoInternalFeatures* features) { + do { + AvifInfoInternalBox box; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes, + num_parsed_boxes, &box)); + + if (!memcmp(box.type, "dimg", 4)) { + // See ISO/IEC 14496-12:2015(E) 8.11.12.2 + const uint32_t num_bytes_per_id = (box.version == 0) ? 2 : 4; + uint32_t num_read_bytes = num_bytes_per_id + 2; + const uint8_t* data; + AVIFINFO_CHECK(box.content_size >= num_read_bytes, kInvalid); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, num_bytes_per_id + 2, &data)); + const uint32_t from_item_id = + AvifInfoInternalReadBigEndian(data, num_bytes_per_id); + const uint32_t reference_count = + AvifInfoInternalReadBigEndian(data + num_bytes_per_id, 2); + + for (uint32_t i = 0; i < reference_count; ++i) { + if (i >= AVIFINFO_MAX_TILES) { + features->data_was_skipped = 1; + break; + } + num_read_bytes += num_bytes_per_id; + AVIFINFO_CHECK(box.content_size >= num_read_bytes, kInvalid); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, num_bytes_per_id, &data)); + const uint32_t to_item_id = + AvifInfoInternalReadBigEndian(data, num_bytes_per_id); + if (from_item_id <= AVIFINFO_MAX_VALUE && + to_item_id <= AVIFINFO_MAX_VALUE && + features->num_tiles < AVIFINFO_MAX_TILES) { + features->tiles[features->num_tiles].tile_item_id = to_item_id; + features->tiles[features->num_tiles].parent_item_id = from_item_id; + ++features->num_tiles; + } else { + features->data_was_skipped = 1; + } + } + + // If all features are available now, do not look further. + AVIFINFO_CHECK_NOT_FOUND( + AvifInfoInternalGetPrimaryItemFeatures(features)); + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + num_remaining_bytes -= box.size; + } while (num_remaining_bytes > 0); + AVIFINFO_RETURN(kNotFound); +} + +//------------------------------------------------------------------------------ + +// Parses a 'stream' of a "meta" box. It looks for the primary item ID in the +// "pitm" box and recurses into other boxes to find its 'features'. +static AvifInfoInternalStatus ParseMeta(AvifInfoInternalStream* stream, + uint32_t num_remaining_bytes, + uint32_t* num_parsed_boxes, + AvifInfoInternalFeatures* features) { + do { + AvifInfoInternalBox box; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, num_remaining_bytes, + num_parsed_boxes, &box)); + + if (!memcmp(box.type, "pitm", 4)) { + // See ISO/IEC 14496-12:2015(E) 8.11.4.2 + const uint32_t num_bytes_per_id = (box.version == 0) ? 2 : 4; + const uint8_t* data; + AVIFINFO_CHECK(num_bytes_per_id <= num_remaining_bytes, kInvalid); + AVIFINFO_CHECK_FOUND( + AvifInfoInternalRead(stream, num_bytes_per_id, &data)); + const uint32_t primary_item_id = + AvifInfoInternalReadBigEndian(data, num_bytes_per_id); + AVIFINFO_CHECK(primary_item_id <= AVIFINFO_MAX_VALUE, kAborted); + features->has_primary_item = 1; + features->primary_item_id = primary_item_id; + AVIFINFO_CHECK_FOUND( + AvifInfoInternalSkip(stream, box.content_size - num_bytes_per_id)); + } else if (!memcmp(box.type, "iprp", 4)) { + AVIFINFO_CHECK_NOT_FOUND( + ParseIprp(stream, box.content_size, num_parsed_boxes, features)); + } else if (!memcmp(box.type, "iref", 4)) { + AVIFINFO_CHECK_NOT_FOUND( + ParseIref(stream, box.content_size, num_parsed_boxes, features)); + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + num_remaining_bytes -= box.size; + } while (num_remaining_bytes != 0); + // According to ISO/IEC 14496-12:2012(E) 8.11.1.1 there is at most one "meta". + AVIFINFO_RETURN(features->data_was_skipped ? kAborted : kInvalid); +} + +//------------------------------------------------------------------------------ + +// Parses a file 'stream'. The file type is checked through the "ftyp" box. +static AvifInfoInternalStatus ParseFtyp(AvifInfoInternalStream* stream) { + AvifInfoInternalBox box; + uint32_t num_parsed_boxes = 0; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, AVIFINFO_MAX_SIZE, + &num_parsed_boxes, &box)); + AVIFINFO_CHECK(!memcmp(box.type, "ftyp", 4), kInvalid); + // Iterate over brands. See ISO/IEC 14496-12:2012(E) 4.3.1 + AVIFINFO_CHECK(box.content_size >= 8, kInvalid); // major_brand,minor_version + for (uint32_t i = 0; i + 4 <= box.content_size; i += 4) { + const uint8_t* data; + AVIFINFO_CHECK_FOUND(AvifInfoInternalRead(stream, 4, &data)); + if (i == 4) continue; // Skip minor_version. + if (!memcmp(data, "avif", 4) || !memcmp(data, "avis", 4)) { + AVIFINFO_CHECK_FOUND( + AvifInfoInternalSkip(stream, box.content_size - (i + 4))); + return kFound; + } + AVIFINFO_CHECK(i <= 32 * 4, kAborted); // Be reasonable. + } + AVIFINFO_RETURN(kInvalid); // No AVIF brand no good. +} + +// Parses a file 'stream'. 'features' are extracted from the "meta" box. +static AvifInfoInternalStatus ParseFile(AvifInfoInternalStream* stream, + uint32_t* num_parsed_boxes, + AvifInfoInternalFeatures* features) { + while (1) { + AvifInfoInternalBox box; + AVIFINFO_CHECK_FOUND(AvifInfoInternalParseBox(stream, AVIFINFO_MAX_SIZE, + num_parsed_boxes, &box)); + if (!memcmp(box.type, "meta", 4)) { + return ParseMeta(stream, box.content_size, num_parsed_boxes, features); + } else { + AVIFINFO_CHECK_FOUND(AvifInfoInternalSkip(stream, box.content_size)); + } + } + AVIFINFO_RETURN(kInvalid); // No "meta" no good. +} + +//------------------------------------------------------------------------------ +// Helpers for converting the fixed-size input public API to the streamed one. + +typedef struct { + const uint8_t* data; + size_t data_size; +} AvifInfoInternalForward; + +static const uint8_t* AvifInfoInternalForwardRead(void* stream, + size_t num_bytes) { + AvifInfoInternalForward* forward = (AvifInfoInternalForward*)stream; + if (num_bytes > forward->data_size) return NULL; + const uint8_t* data = forward->data; + forward->data += num_bytes; + forward->data_size -= num_bytes; + return data; +} + +static void AvifInfoInternalForwardSkip(void* stream, size_t num_bytes) { + AvifInfoInternalForward* forward = (AvifInfoInternalForward*)stream; + if (num_bytes > forward->data_size) num_bytes = forward->data_size; + forward->data += num_bytes; + forward->data_size -= num_bytes; +} + +//------------------------------------------------------------------------------ +// Fixed-size input public API + +AvifInfoStatus AvifInfoIdentify(const uint8_t* data, size_t data_size) { + AvifInfoInternalForward stream; + stream.data = data; + stream.data_size = data_size; + // Forward null 'data' as a null 'stream' to handle it the same way. + return AvifInfoIdentifyStream( + (void*)&stream, (data == NULL) ? NULL : AvifInfoInternalForwardRead, + AvifInfoInternalForwardSkip); +} + +AvifInfoStatus AvifInfoGetFeatures(const uint8_t* data, size_t data_size, + AvifInfoFeatures* features) { + AvifInfoInternalForward stream; + stream.data = data; + stream.data_size = data_size; + return AvifInfoGetFeaturesStream( + (void*)&stream, (data == NULL) ? NULL : AvifInfoInternalForwardRead, + AvifInfoInternalForwardSkip, features); +} + +//------------------------------------------------------------------------------ +// Streamed input API + +AvifInfoStatus AvifInfoIdentifyStream(void* stream, read_stream_t read, + skip_stream_t skip) { + if (read == NULL) return kAvifInfoNotEnoughData; + + AvifInfoInternalStream internal_stream; + internal_stream.stream = stream; + internal_stream.read = read; + internal_stream.skip = skip; // Fallbacks to 'read' if null. + return AvifInfoInternalConvertStatus(ParseFtyp(&internal_stream)); +} + +AvifInfoStatus AvifInfoGetFeaturesStream(void* stream, read_stream_t read, + skip_stream_t skip, + AvifInfoFeatures* features) { + if (features != NULL) memset(features, 0, sizeof(*features)); + if (read == NULL) return kAvifInfoNotEnoughData; + + AvifInfoInternalStream internal_stream; + internal_stream.stream = stream; + internal_stream.read = read; + internal_stream.skip = skip; // Fallbacks to 'read' if null. + uint32_t num_parsed_boxes = 0; + AvifInfoInternalFeatures internal_features; + memset(&internal_features, AVIFINFO_UNDEFINED, sizeof(internal_features)); + + // Go through all relevant boxes sequentially. + const AvifInfoInternalStatus status = + ParseFile(&internal_stream, &num_parsed_boxes, &internal_features); + if (status == kFound && features != NULL) { + memcpy(features, &internal_features.primary_item_features, + sizeof(*features)); + } + return AvifInfoInternalConvertStatus(status); +} diff --git a/ext/standard/libavifinfo/avifinfo.h b/ext/standard/libavifinfo/avifinfo.h new file mode 100644 index 0000000000000..4c898c2efcc5c --- /dev/null +++ b/ext/standard/libavifinfo/avifinfo.h @@ -0,0 +1,92 @@ +// Copyright (c) 2021, Alliance for Open Media. All rights reserved +// +// This source code is subject to the terms of the BSD 2 Clause License and +// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License +// was not distributed with this source code in the LICENSE file, you can +// obtain it at www.aomedia.org/license/software. If the Alliance for Open +// Media Patent License 1.0 was not distributed with this source code in the +// PATENTS file, you can obtain it at www.aomedia.org/license/patent. + +#ifndef AVIFINFO_H_ +#define AVIFINFO_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------------------------------------------ + +typedef enum { + kAvifInfoOk, // The file was correctly parsed and the requested + // information was extracted. It is not guaranteed + // that the input bitstream is a valid complete + // AVIF file. + kAvifInfoNotEnoughData, // The input bitstream was correctly parsed until + // now but bytes are missing. The request should be + // repeated with more input bytes. + kAvifInfoTooComplex, // The input bitstream was correctly parsed until + // now but it is too complex. The parsing was + // stopped to avoid any timeout or crash. + kAvifInfoInvalidFile, // The input bitstream is not a valid AVIF file, + // truncated or not. +} AvifInfoStatus; + +typedef struct { + uint32_t width, height; // In number of pixels. Ignores mirror and rotation. + uint32_t bit_depth; // Likely 8, 10 or 12 bits per channel per pixel. + uint32_t num_channels; // Likely 1, 2, 3 or 4 channels: + // (1 monochrome or 3 colors) + (0 or 1 alpha) +} AvifInfoFeatures; + +//------------------------------------------------------------------------------ +// Fixed-size input API +// Use this API if a raw byte array of fixed size is available as input. + +// Parses the 'data' and returns kAvifInfoOk if it is identified as an AVIF. +// The file type can be identified in the first 12 bytes of most AVIF files. +AvifInfoStatus AvifInfoIdentify(const uint8_t* data, size_t data_size); + +// Parses the identified AVIF 'data' and extracts its 'features'. +// 'data' can be partial but must point to the beginning of the AVIF file. +// The 'features' can be parsed in the first 450 bytes of most AVIF files. +// 'features' are set to 0 unless kAvifInfoOk is returned. +AvifInfoStatus AvifInfoGetFeatures(const uint8_t* data, size_t data_size, + AvifInfoFeatures* features); + +//------------------------------------------------------------------------------ +// Streamed input API +// Use this API if the input bytes must be fetched and/or if the AVIF payload +// size is unknown. Implement the two function signatures below and pass them to +// AvifInfoRead*() with a 'stream', which can be anything (file, struct etc.). + +// Reads 'num_bytes' from the 'stream'. +// The position in the 'stream' must be advanced by 'num_bytes'. +// Returns a pointer to the 'num_bytes' or null if it cannot be fulfilled. +// The returned data must remain valid until the next read. +typedef const uint8_t* (*read_stream_t)(void* stream, size_t num_bytes); +// Advances the position in the 'stream' by 'num_bytes'. +typedef void (*skip_stream_t)(void* stream, size_t num_bytes); + +// Maximum number of bytes requested per read. There is no limit per skip. +#define AVIFINFO_MAX_NUM_READ_BYTES 64 + +// Same as AvifInfo*() but takes a 'stream' as input. AvifInfo*Stream() does +// not access the 'stream' directly but passes it as is to 'read' and 'skip'. +// 'read' cannot be null. If 'skip' is null, 'read' is called instead. +AvifInfoStatus AvifInfoIdentifyStream(void* stream, read_stream_t read, + skip_stream_t skip); +// Can be called right after AvifInfoIdentifyStream() with the same 'stream'. +AvifInfoStatus AvifInfoGetFeaturesStream(void* stream, read_stream_t read, + skip_stream_t skip, + AvifInfoFeatures* features); + +//------------------------------------------------------------------------------ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // AVIFINFO_H_ diff --git a/ext/standard/string.c b/ext/standard/string.c index c7c4e193ec378..7c2dce4595b0f 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1346,16 +1346,7 @@ PHP_FUNCTION(strtok) /* {{{ php_strtoupper */ PHPAPI char *php_strtoupper(char *s, size_t len) { - unsigned char *c; - const unsigned char *e; - - c = (unsigned char *)s; - e = (unsigned char *)c+len; - - while (c < e) { - *c = toupper(*c); - c++; - } + zend_str_toupper(s, len); return s; } /* }}} */ @@ -1363,35 +1354,7 @@ PHPAPI char *php_strtoupper(char *s, size_t len) /* {{{ php_string_toupper */ PHPAPI zend_string *php_string_toupper(zend_string *s) { - unsigned char *c; - const unsigned char *e; - - if (EXPECTED(!BG(ctype_string))) { - return zend_string_toupper(s); - } - c = (unsigned char *)ZSTR_VAL(s); - e = c + ZSTR_LEN(s); - - while (c < e) { - if (islower(*c)) { - unsigned char *r; - zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0); - - if (c != (unsigned char*)ZSTR_VAL(s)) { - memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s)); - } - r = c + (ZSTR_VAL(res) - ZSTR_VAL(s)); - while (c < e) { - *r = toupper(*c); - r++; - c++; - } - *r = '\0'; - return res; - } - c++; - } - return zend_string_copy(s); + return zend_string_toupper(s); } /* }}} */ @@ -1404,23 +1367,14 @@ PHP_FUNCTION(strtoupper) Z_PARAM_STR(arg) ZEND_PARSE_PARAMETERS_END(); - RETURN_STR(php_string_toupper(arg)); + RETURN_STR(zend_string_toupper(arg)); } /* }}} */ /* {{{ php_strtolower */ PHPAPI char *php_strtolower(char *s, size_t len) { - unsigned char *c; - const unsigned char *e; - - c = (unsigned char *)s; - e = c+len; - - while (c < e) { - *c = tolower(*c); - c++; - } + zend_str_tolower(s, len); return s; } /* }}} */ @@ -1428,32 +1382,7 @@ PHPAPI char *php_strtolower(char *s, size_t len) /* {{{ php_string_tolower */ PHPAPI zend_string *php_string_tolower(zend_string *s) { - if (EXPECTED(!BG(ctype_string))) { - return zend_string_tolower(s); - } - - unsigned char *c = (unsigned char *)ZSTR_VAL(s); - const unsigned char *e = c + ZSTR_LEN(s); - while (c < e) { - if (isupper(*c)) { - unsigned char *r; - zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0); - - if (c != (unsigned char*)ZSTR_VAL(s)) { - memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s)); - } - r = c + (ZSTR_VAL(res) - ZSTR_VAL(s)); - while (c < e) { - *r = tolower(*c); - r++; - c++; - } - *r = '\0'; - return res; - } - c++; - } - return zend_string_copy(s); + return zend_string_tolower(s); } /* }}} */ @@ -1466,7 +1395,7 @@ PHP_FUNCTION(strtolower) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - RETURN_STR(php_string_tolower(str)); + RETURN_STR(zend_string_tolower(str)); } /* }}} */ @@ -1758,8 +1687,8 @@ PHP_FUNCTION(pathinfo) case insensitive strstr */ PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len) { - php_strtolower(s, s_len); - php_strtolower(t, t_len); + zend_str_tolower(s, s_len); + zend_str_tolower(t, t_len); return (char*)php_memnstr(s, t, t_len, s + s_len); } /* }}} */ @@ -1982,8 +1911,8 @@ PHP_FUNCTION(stripos) RETURN_FALSE; } - haystack_dup = php_string_tolower(haystack); - needle_dup = php_string_tolower(needle); + haystack_dup = zend_string_tolower(haystack); + needle_dup = zend_string_tolower(needle); found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack)); @@ -2077,10 +2006,9 @@ PHP_FUNCTION(strripos) } e = ZSTR_VAL(haystack) + (ZSTR_LEN(haystack) + (size_t)offset); } - /* Borrow that ord_needle buffer to avoid repeatedly tolower()ing needle */ - lowered = tolower(*ZSTR_VAL(needle)); + lowered = zend_tolower_ascii(*ZSTR_VAL(needle)); while (e >= p) { - if (tolower(*e) == lowered) { + if (zend_tolower_ascii(*e) == lowered) { RETURN_LONG(e - p + (offset > 0 ? offset : 0)); } e--; @@ -2088,7 +2016,7 @@ PHP_FUNCTION(strripos) RETURN_FALSE; } - haystack_dup = php_string_tolower(haystack); + haystack_dup = zend_string_tolower(haystack); if (offset >= 0) { if ((size_t)offset > ZSTR_LEN(haystack)) { zend_string_release_ex(haystack_dup, 0); @@ -2112,7 +2040,7 @@ PHP_FUNCTION(strripos) } } - needle_dup = php_string_tolower(needle); + needle_dup = zend_string_tolower(needle); if ((found = (char *)zend_memnrstr(p, ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), e))) { RETVAL_LONG(found - ZSTR_VAL(haystack_dup)); zend_string_release_ex(needle_dup, 0); @@ -2647,7 +2575,7 @@ PHP_FUNCTION(chr) static zend_string* php_ucfirst(zend_string *str) { const unsigned char ch = ZSTR_VAL(str)[0]; - unsigned char r = toupper(ch); + unsigned char r = zend_toupper_ascii(ch); if (r == ch) { return zend_string_copy(str); } else { @@ -2679,7 +2607,7 @@ PHP_FUNCTION(ucfirst) Lowercase the first character of the word in a native string */ static zend_string* php_lcfirst(zend_string *str) { - unsigned char r = tolower(ZSTR_VAL(str)[0]); + unsigned char r = zend_tolower_ascii(ZSTR_VAL(str)[0]); if (r == ZSTR_VAL(str)[0]) { return zend_string_copy(str); } else { @@ -2732,10 +2660,10 @@ PHP_FUNCTION(ucwords) ZVAL_STRINGL(return_value, ZSTR_VAL(str), ZSTR_LEN(str)); r = Z_STRVAL_P(return_value); - *r = toupper((unsigned char) *r); + *r = zend_toupper_ascii((unsigned char) *r); for (r_end = r + Z_STRLEN_P(return_value) - 1; r < r_end; ) { if (mask[(unsigned char)*r++]) { - *r = toupper((unsigned char) *r); + *r = zend_toupper_ascii((unsigned char) *r); } } } @@ -3067,11 +2995,11 @@ static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, si if (case_sensitivity) { char_count = count_chars(ZSTR_VAL(str), ZSTR_LEN(str), from); } else { - lc_from = tolower(from); char_count = 0; + lc_from = zend_tolower_ascii(from); source_end = ZSTR_VAL(str) + ZSTR_LEN(str); for (source = ZSTR_VAL(str); source < source_end; source++) { - if (tolower(*source) == lc_from) { + if (zend_tolower_ascii(*source) == lc_from) { char_count++; } } @@ -3111,7 +3039,7 @@ static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, si } else { source_end = ZSTR_VAL(str) + ZSTR_LEN(str); for (source = ZSTR_VAL(str); source < source_end; source++) { - if (tolower(*source) == lc_from) { + if (zend_tolower_ascii(*source) == lc_from) { memcpy(target, to, to_len); target += to_len; } else { @@ -4345,7 +4273,7 @@ static zend_long php_str_replace_in_subject( zend_long old_replace_count = replace_count; if (!lc_subject_str) { - lc_subject_str = php_string_tolower(subject_str); + lc_subject_str = zend_string_tolower(subject_str); } tmp_result = php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str), search_str, replace_value, replace_len, &replace_count); @@ -4398,7 +4326,7 @@ static zend_long php_str_replace_in_subject( ZSTR_VAL(search_str), ZSTR_LEN(search_str), ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count)); } else { - lc_subject_str = php_string_tolower(subject_str); + lc_subject_str = zend_string_tolower(subject_str); ZVAL_STR(result, php_str_to_str_i_ex(subject_str, ZSTR_VAL(lc_subject_str), search_str, ZSTR_VAL(replace_str), ZSTR_LEN(replace_str), &replace_count)); zend_string_release_ex(lc_subject_str, 0); @@ -4941,7 +4869,7 @@ int php_tag_find(char *tag, size_t len, const char *set) { n = norm; t = tag; - c = tolower(*t); + c = zend_tolower_ascii(*t); /* normalize the tag removing leading and trailing whitespace and turn any into just and any @@ -4969,7 +4897,7 @@ int php_tag_find(char *tag, size_t len, const char *set) { } break; } - c = tolower(*(++t)); + c = zend_tolower_ascii(*(++t)); } *(n++) = '>'; *n = '\0'; diff --git a/ext/standard/tests/image/getimagesize.phpt b/ext/standard/tests/image/getimagesize.phpt index 0a1002d2934a1..472be1d25e366 100644 --- a/ext/standard/tests/image/getimagesize.phpt +++ b/ext/standard/tests/image/getimagesize.phpt @@ -70,15 +70,19 @@ array(17) { string(9) "image/bmp" } ["test1pix.avif"]=> - array(5) { + array(7) { [0]=> - int(0) + int(102) [1]=> - int(0) + int(121) [2]=> int(19) [3]=> - string(20) "width="0" height="0"" + string(24) "width="102" height="121"" + ["bits"]=> + int(8) + ["channels"]=> + int(4) ["mime"]=> string(10) "image/avif" } diff --git a/ext/standard/tests/strings/bug79986.phpt b/ext/standard/tests/strings/bug79986.phpt deleted file mode 100644 index fcbc72148c242..0000000000000 --- a/ext/standard/tests/strings/bug79986.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -Bug #79986 (str_ireplace bug with diacritics characters) ---SKIPIF-- - ---FILE-- - ---EXPECT-- -11 22 33 diff --git a/ext/standard/tests/strings/strtolower-win32.phpt b/ext/standard/tests/strings/strtolower-win32.phpt deleted file mode 100644 index 516248346cf01..0000000000000 --- a/ext/standard/tests/strings/strtolower-win32.phpt +++ /dev/null @@ -1,340 +0,0 @@ ---TEST-- -Test strtolower() function ---SKIPIF-- - ---FILE-- - ".(bin2hex(strtolower("$char")))."\n"; -} - -echo "*** Testing strlower() with basic strings ***\n"; -$str = "Mary Had A liTTle LAmb and ShE loveD IT So\n"; -var_dump(strtolower($str)); - -echo "\n*** Testing strtolower() with various strings ***"; -/* strings to pass strtolower() */ -$strings = array ( - "", - "string", - "stRINg0234", - "1.233.344StrinG12333", - "$$$$$$!!!!@@@@@@@ ABCDEF !!!***", - "ABCD\0abcdABCD", - TRUE, - FALSE, -); - -$count = 0; -/* loop through to check possible variations */ -foreach ($strings as $string) { - echo "\n-- Iteration $count --\n"; - var_dump( strtolower($string) ); - $count++; -} - -echo "\n*** Testing strtolower() with two different case strings ***\n"; -if (strtolower("HeLLo woRLd") === strtolower("hEllo WORLD")) - echo "strings are same, with Case Insensitive\n"; -else - echo "strings are not same\n"; - -echo "*** Done ***"; -?> ---EXPECTF-- -*** Testing strtolower() with all 256 chars *** -00 => 00 -01 => 01 -02 => 02 -03 => 03 -04 => 04 -05 => 05 -06 => 06 -07 => 07 -08 => 08 -09 => 09 -0a => 0a -0b => 0b -0c => 0c -0d => 0d -0e => 0e -0f => 0f -10 => 10 -11 => 11 -12 => 12 -13 => 13 -14 => 14 -15 => 15 -16 => 16 -17 => 17 -18 => 18 -19 => 19 -1a => 1a -1b => 1b -1c => 1c -1d => 1d -1e => 1e -1f => 1f -20 => 20 -21 => 21 -22 => 22 -23 => 23 -24 => 24 -25 => 25 -26 => 26 -27 => 27 -28 => 28 -29 => 29 -2a => 2a -2b => 2b -2c => 2c -2d => 2d -2e => 2e -2f => 2f -30 => 30 -31 => 31 -32 => 32 -33 => 33 -34 => 34 -35 => 35 -36 => 36 -37 => 37 -38 => 38 -39 => 39 -3a => 3a -3b => 3b -3c => 3c -3d => 3d -3e => 3e -3f => 3f -40 => 40 -41 => 61 -42 => 62 -43 => 63 -44 => 64 -45 => 65 -46 => 66 -47 => 67 -48 => 68 -49 => 69 -4a => 6a -4b => 6b -4c => 6c -4d => 6d -4e => 6e -4f => 6f -50 => 70 -51 => 71 -52 => 72 -53 => 73 -54 => 74 -55 => 75 -56 => 76 -57 => 77 -58 => 78 -59 => 79 -5a => 7a -5b => 5b -5c => 5c -5d => 5d -5e => 5e -5f => 5f -60 => 60 -61 => 61 -62 => 62 -63 => 63 -64 => 64 -65 => 65 -66 => 66 -67 => 67 -68 => 68 -69 => 69 -6a => 6a -6b => 6b -6c => 6c -6d => 6d -6e => 6e -6f => 6f -70 => 70 -71 => 71 -72 => 72 -73 => 73 -74 => 74 -75 => 75 -76 => 76 -77 => 77 -78 => 78 -79 => 79 -7a => 7a -7b => 7b -7c => 7c -7d => 7d -7e => 7e -7f => 7f -80 => 80 -81 => 81 -82 => 82 -83 => 83 -84 => 84 -85 => 85 -86 => 86 -87 => 87 -88 => 88 -89 => 89 -8a => 9a -8b => 8b -8c => 9c -8d => 8d -8e => 9e -8f => 8f -90 => 90 -91 => 91 -92 => 92 -93 => 93 -94 => 94 -95 => 95 -96 => 96 -97 => 97 -98 => 98 -99 => 99 -9a => 9a -9b => 9b -9c => 9c -9d => 9d -9e => 9e -9f => ff -a0 => a0 -a1 => a1 -a2 => a2 -a3 => a3 -a4 => a4 -a5 => a5 -a6 => a6 -a7 => a7 -a8 => a8 -a9 => a9 -aa => aa -ab => ab -ac => ac -ad => ad -ae => ae -af => af -b0 => b0 -b1 => b1 -b2 => b2 -b3 => b3 -b4 => b4 -b5 => b5 -b6 => b6 -b7 => b7 -b8 => b8 -b9 => b9 -ba => ba -bb => bb -bc => bc -bd => bd -be => be -bf => bf -c0 => e0 -c1 => e1 -c2 => e2 -c3 => e3 -c4 => e4 -c5 => e5 -c6 => e6 -c7 => e7 -c8 => e8 -c9 => e9 -ca => ea -cb => eb -cc => ec -cd => ed -ce => ee -cf => ef -d0 => f0 -d1 => f1 -d2 => f2 -d3 => f3 -d4 => f4 -d5 => f5 -d6 => f6 -d7 => d7 -d8 => f8 -d9 => f9 -da => fa -db => fb -dc => fc -dd => fd -de => fe -df => df -e0 => e0 -e1 => e1 -e2 => e2 -e3 => e3 -e4 => e4 -e5 => e5 -e6 => e6 -e7 => e7 -e8 => e8 -e9 => e9 -ea => ea -eb => eb -ec => ec -ed => ed -ee => ee -ef => ef -f0 => f0 -f1 => f1 -f2 => f2 -f3 => f3 -f4 => f4 -f5 => f5 -f6 => f6 -f7 => f7 -f8 => f8 -f9 => f9 -fa => fa -fb => fb -fc => fc -fd => fd -fe => fe -ff => ff -*** Testing strlower() with basic strings *** -string(43) "mary had a little lamb and she loved it so -" - -*** Testing strtolower() with various strings *** --- Iteration 0 -- -string(0) "" - --- Iteration 1 -- -string(6) "string" - --- Iteration 2 -- -string(10) "string0234" - --- Iteration 3 -- -string(20) "1.233.344string12333" - --- Iteration 4 -- -string(31) "$$$$$$!!!!@@@@@@@ abcdef !!!***" - --- Iteration 5 -- -string(13) "abcd%0abcdabcd" - --- Iteration 6 -- -string(1) "1" - --- Iteration 7 -- -string(0) "" - -*** Testing strtolower() with two different case strings *** -strings are same, with Case Insensitive -*** Done *** diff --git a/ext/standard/tests/strings/strtolower.phpt b/ext/standard/tests/strings/strtolower.phpt index 37a38493ce114..6052ccd840d50 100644 --- a/ext/standard/tests/strings/strtolower.phpt +++ b/ext/standard/tests/strings/strtolower.phpt @@ -1,27 +1,10 @@ --TEST-- Test strtolower() function ---SKIPIF-- - --FILE-- ".(bin2hex(strtolower("$char")))."\n"; } @@ -66,7 +49,7 @@ else echo "*** Done ***"; ?> --EXPECTF-- -*** Testing strtolower() with 128 chars *** +*** Testing strtolower() with 256 chars *** 00 => 00 01 => 01 02 => 02 @@ -195,6 +178,134 @@ echo "*** Done ***"; 7d => 7d 7e => 7e 7f => 7f +80 => 80 +81 => 81 +82 => 82 +83 => 83 +84 => 84 +85 => 85 +86 => 86 +87 => 87 +88 => 88 +89 => 89 +8a => 8a +8b => 8b +8c => 8c +8d => 8d +8e => 8e +8f => 8f +90 => 90 +91 => 91 +92 => 92 +93 => 93 +94 => 94 +95 => 95 +96 => 96 +97 => 97 +98 => 98 +99 => 99 +9a => 9a +9b => 9b +9c => 9c +9d => 9d +9e => 9e +9f => 9f +a0 => a0 +a1 => a1 +a2 => a2 +a3 => a3 +a4 => a4 +a5 => a5 +a6 => a6 +a7 => a7 +a8 => a8 +a9 => a9 +aa => aa +ab => ab +ac => ac +ad => ad +ae => ae +af => af +b0 => b0 +b1 => b1 +b2 => b2 +b3 => b3 +b4 => b4 +b5 => b5 +b6 => b6 +b7 => b7 +b8 => b8 +b9 => b9 +ba => ba +bb => bb +bc => bc +bd => bd +be => be +bf => bf +c0 => c0 +c1 => c1 +c2 => c2 +c3 => c3 +c4 => c4 +c5 => c5 +c6 => c6 +c7 => c7 +c8 => c8 +c9 => c9 +ca => ca +cb => cb +cc => cc +cd => cd +ce => ce +cf => cf +d0 => d0 +d1 => d1 +d2 => d2 +d3 => d3 +d4 => d4 +d5 => d5 +d6 => d6 +d7 => d7 +d8 => d8 +d9 => d9 +da => da +db => db +dc => dc +dd => dd +de => de +df => df +e0 => e0 +e1 => e1 +e2 => e2 +e3 => e3 +e4 => e4 +e5 => e5 +e6 => e6 +e7 => e7 +e8 => e8 +e9 => e9 +ea => ea +eb => eb +ec => ec +ed => ed +ee => ee +ef => ef +f0 => f0 +f1 => f1 +f2 => f2 +f3 => f3 +f4 => f4 +f5 => f5 +f6 => f6 +f7 => f7 +f8 => f8 +f9 => f9 +fa => fa +fb => fb +fc => fc +fd => fd +fe => fe +ff => ff *** Testing strlower() with basic strings *** string(43) "mary had a little lamb and she loved it so " diff --git a/ext/standard/tests/strings/strtoupper.phpt b/ext/standard/tests/strings/strtoupper.phpt deleted file mode 100644 index 41bc5e6080643..0000000000000 --- a/ext/standard/tests/strings/strtoupper.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -Test strtoupper on non-ASCII characters ---SKIPIF-- - ---FILE-- - ---EXPECT-- -ÄÖÜ diff --git a/ext/standard/tests/strings/strtoupper1-win32.phpt b/ext/standard/tests/strings/strtoupper1-win32.phpt deleted file mode 100644 index c9328462cf7a9..0000000000000 --- a/ext/standard/tests/strings/strtoupper1-win32.phpt +++ /dev/null @@ -1,342 +0,0 @@ ---TEST-- -Test strtoupper() function ---SKIPIF-- - ---FILE-- - ".(bin2hex(strtoupper("$char")))."\n"; -} - -echo "\n*** Testing strtoupper() with basic strings ***\n"; -$str = "Mary Had A liTTle LAmb and ShE loveD IT So\n"; -var_dump(strtoupper($str)); - -echo "\n*** Testing strtoupper() with various strings ***"; -/* strings to pass strtoupper() */ -$strings = array ( - "", - "string", - "stRINg0234", - "1.233.344StrinG12333", - "$$$$$$!!!!@@@@@@@ ABCDEF !!!***", - "ABCD\0abcdABCD", - TRUE, - FALSE, -); - -$count = 0; -/* loop through to check possible variations */ -foreach ($strings as $string) { - echo "\n-- Iteration $count --\n"; - var_dump( strtoupper($string) ); - $count++; -} - -echo "\n*** Testing strtoupper() with two different case strings ***\n"; -if (strtoupper("HeLLo woRLd") === strtoupper("hEllo WORLD")) - echo "strings are same, with Case Insensitive\n"; -else - echo "strings are not same\n"; - -echo "*** Done ***"; -?> ---EXPECTF-- -*** Testing strtoupper() with all 256 chars *** -00 => 00 -01 => 01 -02 => 02 -03 => 03 -04 => 04 -05 => 05 -06 => 06 -07 => 07 -08 => 08 -09 => 09 -0a => 0a -0b => 0b -0c => 0c -0d => 0d -0e => 0e -0f => 0f -10 => 10 -11 => 11 -12 => 12 -13 => 13 -14 => 14 -15 => 15 -16 => 16 -17 => 17 -18 => 18 -19 => 19 -1a => 1a -1b => 1b -1c => 1c -1d => 1d -1e => 1e -1f => 1f -20 => 20 -21 => 21 -22 => 22 -23 => 23 -24 => 24 -25 => 25 -26 => 26 -27 => 27 -28 => 28 -29 => 29 -2a => 2a -2b => 2b -2c => 2c -2d => 2d -2e => 2e -2f => 2f -30 => 30 -31 => 31 -32 => 32 -33 => 33 -34 => 34 -35 => 35 -36 => 36 -37 => 37 -38 => 38 -39 => 39 -3a => 3a -3b => 3b -3c => 3c -3d => 3d -3e => 3e -3f => 3f -40 => 40 -41 => 41 -42 => 42 -43 => 43 -44 => 44 -45 => 45 -46 => 46 -47 => 47 -48 => 48 -49 => 49 -4a => 4a -4b => 4b -4c => 4c -4d => 4d -4e => 4e -4f => 4f -50 => 50 -51 => 51 -52 => 52 -53 => 53 -54 => 54 -55 => 55 -56 => 56 -57 => 57 -58 => 58 -59 => 59 -5a => 5a -5b => 5b -5c => 5c -5d => 5d -5e => 5e -5f => 5f -60 => 60 -61 => 41 -62 => 42 -63 => 43 -64 => 44 -65 => 45 -66 => 46 -67 => 47 -68 => 48 -69 => 49 -6a => 4a -6b => 4b -6c => 4c -6d => 4d -6e => 4e -6f => 4f -70 => 50 -71 => 51 -72 => 52 -73 => 53 -74 => 54 -75 => 55 -76 => 56 -77 => 57 -78 => 58 -79 => 59 -7a => 5a -7b => 7b -7c => 7c -7d => 7d -7e => 7e -7f => 7f -80 => 80 -81 => 81 -82 => 82 -83 => 83 -84 => 84 -85 => 85 -86 => 86 -87 => 87 -88 => 88 -89 => 89 -8a => 8a -8b => 8b -8c => 8c -8d => 8d -8e => 8e -8f => 8f -90 => 90 -91 => 91 -92 => 92 -93 => 93 -94 => 94 -95 => 95 -96 => 96 -97 => 97 -98 => 98 -99 => 99 -9a => 8a -9b => 9b -9c => 8c -9d => 9d -9e => 8e -9f => 9f -a0 => a0 -a1 => a1 -a2 => a2 -a3 => a3 -a4 => a4 -a5 => a5 -a6 => a6 -a7 => a7 -a8 => a8 -a9 => a9 -aa => aa -ab => ab -ac => ac -ad => ad -ae => ae -af => af -b0 => b0 -b1 => b1 -b2 => b2 -b3 => b3 -b4 => b4 -b5 => b5 -b6 => b6 -b7 => b7 -b8 => b8 -b9 => b9 -ba => ba -bb => bb -bc => bc -bd => bd -be => be -bf => bf -c0 => c0 -c1 => c1 -c2 => c2 -c3 => c3 -c4 => c4 -c5 => c5 -c6 => c6 -c7 => c7 -c8 => c8 -c9 => c9 -ca => ca -cb => cb -cc => cc -cd => cd -ce => ce -cf => cf -d0 => d0 -d1 => d1 -d2 => d2 -d3 => d3 -d4 => d4 -d5 => d5 -d6 => d6 -d7 => d7 -d8 => d8 -d9 => d9 -da => da -db => db -dc => dc -dd => dd -de => de -df => df -e0 => c0 -e1 => c1 -e2 => c2 -e3 => c3 -e4 => c4 -e5 => c5 -e6 => c6 -e7 => c7 -e8 => c8 -e9 => c9 -ea => ca -eb => cb -ec => cc -ed => cd -ee => ce -ef => cf -f0 => d0 -f1 => d1 -f2 => d2 -f3 => d3 -f4 => d4 -f5 => d5 -f6 => d6 -f7 => f7 -f8 => d8 -f9 => d9 -fa => da -fb => db -fc => dc -fd => dd -fe => de -ff => 9f - -*** Testing strtoupper() with basic strings *** -string(43) "MARY HAD A LITTLE LAMB AND SHE LOVED IT SO -" - -*** Testing strtoupper() with various strings *** --- Iteration 0 -- -string(0) "" - --- Iteration 1 -- -string(6) "STRING" - --- Iteration 2 -- -string(10) "STRING0234" - --- Iteration 3 -- -string(20) "1.233.344STRING12333" - --- Iteration 4 -- -string(31) "$$$$$$!!!!@@@@@@@ ABCDEF !!!***" - --- Iteration 5 -- -string(13) "ABCD%0ABCDABCD" - --- Iteration 6 -- -string(1) "1" - --- Iteration 7 -- -string(0) "" - -*** Testing strtoupper() with two different case strings *** -strings are same, with Case Insensitive -*** Done *** diff --git a/ext/standard/tests/strings/strtoupper1.phpt b/ext/standard/tests/strings/strtoupper1.phpt index 7b79f6f457be9..abda0b9d38b28 100644 --- a/ext/standard/tests/strings/strtoupper1.phpt +++ b/ext/standard/tests/strings/strtoupper1.phpt @@ -1,27 +1,9 @@ --TEST-- Test strtoupper() function ---SKIPIF-- - --FILE-- ".(bin2hex(strtoupper("$char")))."\n"; } @@ -65,7 +47,7 @@ else echo "*** Done ***"; ?> --EXPECTF-- -*** Testing strtoupper() with 128 chars *** +*** Testing strtoupper() with 256 chars *** 00 => 00 01 => 01 02 => 02 @@ -194,6 +176,134 @@ echo "*** Done ***"; 7d => 7d 7e => 7e 7f => 7f +80 => 80 +81 => 81 +82 => 82 +83 => 83 +84 => 84 +85 => 85 +86 => 86 +87 => 87 +88 => 88 +89 => 89 +8a => 8a +8b => 8b +8c => 8c +8d => 8d +8e => 8e +8f => 8f +90 => 90 +91 => 91 +92 => 92 +93 => 93 +94 => 94 +95 => 95 +96 => 96 +97 => 97 +98 => 98 +99 => 99 +9a => 9a +9b => 9b +9c => 9c +9d => 9d +9e => 9e +9f => 9f +a0 => a0 +a1 => a1 +a2 => a2 +a3 => a3 +a4 => a4 +a5 => a5 +a6 => a6 +a7 => a7 +a8 => a8 +a9 => a9 +aa => aa +ab => ab +ac => ac +ad => ad +ae => ae +af => af +b0 => b0 +b1 => b1 +b2 => b2 +b3 => b3 +b4 => b4 +b5 => b5 +b6 => b6 +b7 => b7 +b8 => b8 +b9 => b9 +ba => ba +bb => bb +bc => bc +bd => bd +be => be +bf => bf +c0 => c0 +c1 => c1 +c2 => c2 +c3 => c3 +c4 => c4 +c5 => c5 +c6 => c6 +c7 => c7 +c8 => c8 +c9 => c9 +ca => ca +cb => cb +cc => cc +cd => cd +ce => ce +cf => cf +d0 => d0 +d1 => d1 +d2 => d2 +d3 => d3 +d4 => d4 +d5 => d5 +d6 => d6 +d7 => d7 +d8 => d8 +d9 => d9 +da => da +db => db +dc => dc +dd => dd +de => de +df => df +e0 => e0 +e1 => e1 +e2 => e2 +e3 => e3 +e4 => e4 +e5 => e5 +e6 => e6 +e7 => e7 +e8 => e8 +e9 => e9 +ea => ea +eb => eb +ec => ec +ed => ed +ee => ee +ef => ef +f0 => f0 +f1 => f1 +f2 => f2 +f3 => f3 +f4 => f4 +f5 => f5 +f6 => f6 +f7 => f7 +f8 => f8 +f9 => f9 +fa => fa +fb => fb +fc => fc +fd => fd +fe => fe +ff => ff *** Testing strtoupper() with basic strings *** string(43) "MARY HAD A LITTLE LAMB AND SHE LOVED IT SO diff --git a/ext/xml/xml.c b/ext/xml/xml.c index 5c73837c385a1..be69940545cda 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -23,7 +23,6 @@ #include "php.h" #include "zend_variables.h" -#include "ext/standard/php_string.h" #include "ext/standard/info.h" #include "ext/standard/html.h" diff --git a/php.ini-production b/php.ini-production index e25092e688f59..89a198db395a6 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1273,7 +1273,7 @@ mysqlnd.collect_memory_statistics = Off ; https://php.net/oci8.statement-cache-size ;oci8.statement_cache_size = 20 -; Tuning: Enables statement prefetching and sets the default number of +; Tuning: Enables row prefetching and sets the default number of ; rows that will be fetched automatically after statement execution. ; https://php.net/oci8.default-prefetch ;oci8.default_prefetch = 100 diff --git a/run-tests.php b/run-tests.php index baf3ddb91cbe6..36ed068d0eac9 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1091,7 +1091,9 @@ function find_files(string $dir, bool $is_ext_dir = false, bool $ignore = false) } // Otherwise we're only interested in *.phpt files. - if (substr($name, -5) == '.phpt') { + // (but not those starting with a dot, which are hidden on + // many platforms) + if (substr($name, -5) == '.phpt' && substr($name, 0, 1) !== '.') { if ($ignore) { $ignored_by_ext++; } else { diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md index 2099a34bfbf40..63de20aa1ca76 100644 --- a/sapi/fuzzer/README.md +++ b/sapi/fuzzer/README.md @@ -1,7 +1,7 @@ Fuzzing SAPI for PHP -------------------- -The following `./configure` options can be used to enable the fuzzing SAPI, as well as all availablefuzzers. If you don't build the exif/json/mbstring extensions, fuzzers for these extensions will not be built. +The following `./configure` options can be used to enable the fuzzing SAPI, as well as all available fuzzers. If you don't build the exif/json/mbstring extensions, fuzzers for these extensions will not be built. ```sh CC=clang CXX=clang++ \