diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb070a5d..19ae78a99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Added PHP compatible string offsets, so reading, writing, `isset()`, `empty()` and `unset()` on a string behave as they do in PHP, including negative offsets, out of range warnings, dynamically typed sources and `for c in s` [#760](https://github.com/zephir-lang/zephir/issues/760) ### Fixed +- Fixed the `zephir_array_*_long()` helpers truncating a negative or large index to 32 bits on Windows, where `unsigned long` is narrower than `zend_long`, so `s[-1]` read index 4294967295 and a write tried to grow the string to 4 GB [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed Zephir `long`/`ulong` mapping to a C `long`, 32-bit on Windows, so a 64-bit value truncated and `Z_PARAM_LONG()` stored 8 bytes into a 4-byte local [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed the kernel signatures that carry a PHP integer (`zephir_get_intval_ex()`, the comparison, division and modulo helpers, `zephir_substr()`, `zephir_fast_explode()`, `zephir_preg_match()` and the multi-dimensional array key) using a C `long` [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed a multi-dimensional array index reaching `zephir_array_update_multi()` as a C `int` where the callee reads a `zend_long`, so `a[0][1]` produced keys such as 140733193388033 on Windows [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed a `uint`/`ulong` multi-dimensional array index being passed by address, so the pointer value became the array key [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed six `zephir_safe_mod_*()` helpers being declared and emitted but never defined, so any `%` with a float operand died at load with `undefined symbol` [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed `%` yielding a float instead of an `int`, which lost every result above 2^53 [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed `/ 0` and `% 0` raising an `E_WARNING` and returning 0 instead of throwing `DivisionByZeroError` as PHP 8 does [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed `PHP_INT_MIN % -1` crashing the process with SIGFPE [#2666](https://github.com/zephir-lang/zephir/issues/2666) +- Fixed a float divisor being silently truncated to an integer by `/` and `%` [#2666](https://github.com/zephir-lang/zephir/issues/2666) - Fixed a `-Wformat` warning in `zephir_fclose()` by casting the resource handle to `zend_long` and formatting it with the portable `ZEND_LONG_FMT` macro instead of `%d`, preserving compatibility with PHP 8.0's `int` handle. - A variable whose only consumer is a closure's `use (...)` clause now counts as used: it is no longer reported as `unused-variable`, and it is declared in the generated C. A declared-but-unassigned capture was skipped by both, so the generated code referenced an undeclared identifier and the extension failed to build. Capturing a variable that was never declared now fails with `Cannot capture variable 'x' because it wasn't declared` instead of a PHP fatal error [#2029](https://github.com/zephir-lang/zephir/issues/2029) - Fixed array defaults on a class (typed `array` property defaults, trait array property defaults and array class constants), which were one shared table that every instance mutated in place instead of being copy-on-write [#2651](https://github.com/zephir-lang/zephir/issues/2651) diff --git a/ext/config.m4 b/ext/config.m4 index a1bd759fd..968eabc99 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -150,6 +150,7 @@ if test "$PHP_STUB" = "yes"; then stub/issue2635/caller.zep.c stub/issue2635/impl.zep.c stub/issue2651.zep.c + stub/issue2666.zep.c stub/issue663.zep.c stub/issue760.zep.c stub/issue808.zep.c diff --git a/ext/config.w32 b/ext/config.w32 index cebab426e..08fa31c81 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -11,7 +11,7 @@ if (PHP_STUB != "no") { } ADD_SOURCES(configure_module_dirname + "/stub/invokes", "abstractprotected.zep.c abstractinvoker.zep.c abstractinvokercomplex.zep.c invokeprotected.zep.c invokeprotectedcomplex.zep.c", "stub"); - ADD_SOURCES(configure_module_dirname + "/stub", "testinterface.zep.c scallparent.zep.c constantsparent.zep.c issue1628abstract.zep.c issue2324.zep.c issue2505.zep.c issue2537.zep.c methodinterface.zep.c propertycache.zep.c arithmetic.zep.c arrayaccessarr.zep.c arrayaccessobj.zep.c arrayaccessproperty.zep.c arrayaccesstest.zep.c arrayiterator.zep.c arrayiteratortest.zep.c arraymanipulation.zep.c arrayobject.zep.c arraysearch.zep.c assign.zep.c bench.zep.c bitwise.zep.c branchprediction.zep.c cast.zep.c cblock.zep.c chars.zep.c closures.zep.c compare.zep.c concat.zep.c constantparameterdefault.zep.c constants.zep.c constantsinterface.zep.c constantsinterfacea.zep.c constantsinterfaceb.zep.c declaretest.zep.c destructure.zep.c diinterface.zep.c echoes.zep.c emptytest.zep.c evaltest.zep.c exception.zep.c exceptions.zep.c exists.zep.c exitdie.zep.c extendedinterface.zep.c factorial.zep.c fannkuch.zep.c fasta.zep.c fcall.zep.c fetchtest.zep.c fibonnaci.zep.c flow.zep.c fortytwo.zep.c functional.zep.c functionexists.zep.c functions.zep.c generators.zep.c geometry.zep.c globals.zep.c instance.zep.c instanceoff.zep.c internalclasses.zep.c internalinterfaces.zep.c invoke.zep.c issettest.zep.c issue1097.zep.c issue1134.zep.c issue1404.zep.c issue1521.zep.c issue1628.zep.c issue1629.zep.c issue1704.zep.c issue1706.zep.c issue1790.zep.c issue2030.zep.c issue2061.zep.c issue2063.zep.c issue2167.zep.c issue2324extended.zep.c issue2385.zep.c issue2394.zep.c issue2409.zep.c issue2428.zep.c issue2469.zep.c issue2505crosschain.zep.c issue2505extended.zep.c issue2522.zep.c issue2533.zep.c issue2537child.zep.c issue2542.zep.c issue2543.zep.c issue2546.zep.c issue2564.zep.c issue2564padfactory.zep.c issue2565.zep.c issue2651.zep.c issue663.zep.c issue760.zep.c issue808.zep.c issue820.zep.c issue882.zep.c issue887.zep.c issue893.zep.c issue914.zep.c issues.zep.c json.zep.c logical.zep.c mcall.zep.c mcallchained.zep.c mcalldynamic.zep.c mcallinternal.zep.c methodabstract.zep.c methodargs.zep.c methodswithdefaultvalues.zep.c multidimarray.zep.c nativearray.zep.c oo.zep.c openssl.zep.c operator.zep.c pdostatement.zep.c pregmatch.zep.c propertycachechild.zep.c propertycachemagic.zep.c quantum.zep.c range.zep.c references.zep.c reflection.zep.c regexdna.zep.c requires.zep.c resourcetest.zep.c returns.zep.c router.zep.c scall.zep.c scalldynamic.zep.c scallexternal.zep.c scalllateconstruct.zep.c scope.zep.c sort.zep.c spectralnorm.zep.c spl.zep.c spropertyaccess.zep.c statements.zep.c strings.zep.c stubs.zep.c ternary.zep.c trytest.zep.c typeinstances.zep.c typeoff.zep.c unknownclass.zep.c unreachableloop.zep.c unsettest.zep.c unspacedminus.zep.c usetest.zep.c variadic.zep.c vars.zep.c 0__closure.zep.c 1__closure.zep.c 2__closure.zep.c 3__closure.zep.c 4__closure.zep.c 5__closure.zep.c 6__closure.zep.c 7__closure.zep.c 8__closure.zep.c 9__closure.zep.c 10__closure.zep.c 11__closure.zep.c 12__closure.zep.c 13__closure.zep.c 14__closure.zep.c 15__closure.zep.c 16__closure.zep.c 17__closure.zep.c 18__closure.zep.c 19__closure.zep.c 20__closure.zep.c 21__closure.zep.c 22__closure.zep.c 23__closure.zep.c 24__closure.zep.c 25__closure.zep.c 26__closure.zep.c 27__closure.zep.c 28__closure.zep.c 29__closure.zep.c 30__closure.zep.c 31__closure.zep.c 32__closure.zep.c 33__closure.zep.c 34__closure.zep.c 35__closure.zep.c 36__closure.zep.c 37__closure.zep.c 38__closure.zep.c 39__closure.zep.c 40__closure.zep.c 41__closure.zep.c 42__closure.zep.c 43__closure.zep.c 44__closure.zep.c 45__closure.zep.c 46__closure.zep.c", "stub"); + ADD_SOURCES(configure_module_dirname + "/stub", "testinterface.zep.c scallparent.zep.c constantsparent.zep.c issue1628abstract.zep.c issue2324.zep.c issue2505.zep.c issue2537.zep.c methodinterface.zep.c propertycache.zep.c arithmetic.zep.c arrayaccessarr.zep.c arrayaccessobj.zep.c arrayaccessproperty.zep.c arrayaccesstest.zep.c arrayiterator.zep.c arrayiteratortest.zep.c arraymanipulation.zep.c arrayobject.zep.c arraysearch.zep.c assign.zep.c bench.zep.c bitwise.zep.c branchprediction.zep.c cast.zep.c cblock.zep.c chars.zep.c closures.zep.c compare.zep.c concat.zep.c constantparameterdefault.zep.c constants.zep.c constantsinterface.zep.c constantsinterfacea.zep.c constantsinterfaceb.zep.c declaretest.zep.c destructure.zep.c diinterface.zep.c echoes.zep.c emptytest.zep.c evaltest.zep.c exception.zep.c exceptions.zep.c exists.zep.c exitdie.zep.c extendedinterface.zep.c factorial.zep.c fannkuch.zep.c fasta.zep.c fcall.zep.c fetchtest.zep.c fibonnaci.zep.c flow.zep.c fortytwo.zep.c functional.zep.c functionexists.zep.c functions.zep.c generators.zep.c geometry.zep.c globals.zep.c instance.zep.c instanceoff.zep.c internalclasses.zep.c internalinterfaces.zep.c invoke.zep.c issettest.zep.c issue1097.zep.c issue1134.zep.c issue1404.zep.c issue1521.zep.c issue1628.zep.c issue1629.zep.c issue1704.zep.c issue1706.zep.c issue1790.zep.c issue2030.zep.c issue2061.zep.c issue2063.zep.c issue2167.zep.c issue2324extended.zep.c issue2385.zep.c issue2394.zep.c issue2409.zep.c issue2428.zep.c issue2469.zep.c issue2505crosschain.zep.c issue2505extended.zep.c issue2522.zep.c issue2533.zep.c issue2537child.zep.c issue2542.zep.c issue2543.zep.c issue2546.zep.c issue2564.zep.c issue2564padfactory.zep.c issue2565.zep.c issue2651.zep.c issue2666.zep.c issue663.zep.c issue760.zep.c issue808.zep.c issue820.zep.c issue882.zep.c issue887.zep.c issue893.zep.c issue914.zep.c issues.zep.c json.zep.c logical.zep.c mcall.zep.c mcallchained.zep.c mcalldynamic.zep.c mcallinternal.zep.c methodabstract.zep.c methodargs.zep.c methodswithdefaultvalues.zep.c multidimarray.zep.c nativearray.zep.c oo.zep.c openssl.zep.c operator.zep.c pdostatement.zep.c pregmatch.zep.c propertycachechild.zep.c propertycachemagic.zep.c quantum.zep.c range.zep.c references.zep.c reflection.zep.c regexdna.zep.c requires.zep.c resourcetest.zep.c returns.zep.c router.zep.c scall.zep.c scalldynamic.zep.c scallexternal.zep.c scalllateconstruct.zep.c scope.zep.c sort.zep.c spectralnorm.zep.c spl.zep.c spropertyaccess.zep.c statements.zep.c strings.zep.c stubs.zep.c ternary.zep.c trytest.zep.c typeinstances.zep.c typeoff.zep.c unknownclass.zep.c unreachableloop.zep.c unsettest.zep.c unspacedminus.zep.c usetest.zep.c variadic.zep.c vars.zep.c 0__closure.zep.c 1__closure.zep.c 2__closure.zep.c 3__closure.zep.c 4__closure.zep.c 5__closure.zep.c 6__closure.zep.c 7__closure.zep.c 8__closure.zep.c 9__closure.zep.c 10__closure.zep.c 11__closure.zep.c 12__closure.zep.c 13__closure.zep.c 14__closure.zep.c 15__closure.zep.c 16__closure.zep.c 17__closure.zep.c 18__closure.zep.c 19__closure.zep.c 20__closure.zep.c 21__closure.zep.c 22__closure.zep.c 23__closure.zep.c 24__closure.zep.c 25__closure.zep.c 26__closure.zep.c 27__closure.zep.c 28__closure.zep.c 29__closure.zep.c 30__closure.zep.c 31__closure.zep.c 32__closure.zep.c 33__closure.zep.c 34__closure.zep.c 35__closure.zep.c 36__closure.zep.c 37__closure.zep.c 38__closure.zep.c 39__closure.zep.c 40__closure.zep.c 41__closure.zep.c 42__closure.zep.c 43__closure.zep.c 44__closure.zep.c 45__closure.zep.c 46__closure.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/issue2635", "inner.zep.c outer.zep.c base.zep.c caller.zep.c impl.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/oo/extend", "exception.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/issue2165", "issueextendinterface.zep.c issueinterface.zep.c issue.zep.c", "stub"); diff --git a/ext/kernel/array.c b/ext/kernel/array.c index 0117b3464..c227eb77e 100644 --- a/ext/kernel/array.c +++ b/ext/kernel/array.c @@ -222,7 +222,7 @@ int zephir_array_isset_string_fetch(zval *fetched, const zval *arr, char *index, return 0; } -int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long index, int readonly) +int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, zend_long index, int readonly) { zval *zv; @@ -245,7 +245,7 @@ int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long return 0; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), index)) != NULL) { + if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index)) != NULL) { zephir_ensure_array(zv); if (!readonly) { @@ -256,8 +256,8 @@ int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long return 1; } } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - if (zephir_string_offset_isset(arr, (zend_long) index)) { - zephir_string_offset_read(fetched, (zval *) arr, (zend_long) index, 0); + if (zephir_string_offset_isset(arr, index)) { + zephir_string_offset_read(fetched, (zval *) arr, index, 0); return 1; } @@ -347,7 +347,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index return 0; } -int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index) +int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, zend_long index) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev((zval *)arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -361,9 +361,9 @@ int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index return 0; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - return zend_hash_index_exists(Z_ARRVAL_P(arr), index); + return zend_hash_index_exists(Z_ARRVAL_P(arr), (zend_ulong) index); } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - return zephir_string_offset_isset(arr, (zend_long) index); + return zephir_string_offset_isset(arr, index); } return 0; @@ -458,7 +458,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_value_string(const zval *arr, const char return Z_TYPE_P(entry) != IS_NULL; } -int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long index) +int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, zend_long index) { zval *entry; @@ -475,7 +475,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long return 0; } - entry = zend_hash_index_find(Z_ARRVAL_P(arr), index); + entry = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index); if (entry == NULL) { return 0; } @@ -516,7 +516,7 @@ int zephir_isempty_dim(zval *container, zval *offset) return zephir_isempty_dim_fetched(zephir_array_isset_fetch(&fetched, container, offset, 0), &fetched); } -int zephir_isempty_dim_long(zval *container, unsigned long offset) +int zephir_isempty_dim_long(zval *container, zend_long offset) { zval fetched; @@ -617,7 +617,7 @@ int ZEPHIR_FASTCALL zephir_array_unset_string(zval *arr, const char *index, uint return zend_hash_str_del(Z_ARRVAL_P(arr), index, index_length); } -int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int flags) +int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, zend_long index, int flags) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev(arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -642,7 +642,7 @@ int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int SEPARATE_ARRAY(arr); } - return zend_hash_index_del(Z_ARRVAL_P(arr), index); + return zend_hash_index_del(Z_ARRVAL_P(arr), (zend_ulong) index); } int zephir_array_append(zval *arr, zval *value, int flags ZEPHIR_DEBUG_PARAMS) @@ -738,7 +738,7 @@ int zephir_array_fetch(zval *return_value, zval *arr, zval *index, int flags ZEP if ((flags & PH_NOISY) == PH_NOISY) { if (sidx == NULL) { - zend_error(E_NOTICE, "Undefined index: %ld in %s on line %d", uidx, file, line); + zend_error(E_NOTICE, "Undefined index: " ZEND_LONG_FMT " in %s on line %d", (zend_long) uidx, file, line); } else { zend_error(E_NOTICE, "Undefined index: %s in %s on line %d", sidx, file, line); } @@ -809,7 +809,7 @@ int zephir_array_fetch_string(zval *return_value, zval *arr, const char *index, return FAILURE; } -int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, int flags ZEPHIR_DEBUG_PARAMS) +int zephir_array_fetch_long(zval *return_value, zval *arr, zend_long index, int flags ZEPHIR_DEBUG_PARAMS) { zval *zv; @@ -827,7 +827,7 @@ int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, return FAILURE; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), index)) != NULL) { + if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index)) != NULL) { if ((flags & PH_READONLY) == PH_READONLY) { ZVAL_COPY_VALUE(return_value, zv); @@ -837,13 +837,12 @@ int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, return SUCCESS; } if ((flags & PH_NOISY) == PH_NOISY) { - zend_error(E_NOTICE, "Undefined index: %lu", index); + zend_error(E_NOTICE, "Undefined index: " ZEND_LONG_FMT, index); } } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { /* The compiler cannot prove a `var` holds a string, so the string - * offset is dispatched here. `index` is `unsigned long` for historical - * reasons: a negative offset arrives wrapped and the cast restores it. */ - zephir_string_offset_read(return_value, arr, (zend_long) index, flags); + * offset is dispatched here. */ + zephir_string_offset_read(return_value, arr, index, flags); return SUCCESS; } else { @@ -998,7 +997,7 @@ int zephir_array_update_string(zval *arr, const char *index, uint32_t index_leng return zend_hash_str_update(Z_ARRVAL_P(arr), index, index_length, value) ? SUCCESS : FAILURE; } -int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS) +int zephir_array_update_long(zval *arr, zend_long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev(arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -1011,7 +1010,7 @@ int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int fl return FAILURE; } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - zephir_string_offset_write(arr, (zend_long) index, value); + zephir_string_offset_write(arr, index, value); return EG(exception) ? FAILURE : SUCCESS; } else if (Z_TYPE_P(arr) != IS_ARRAY) { @@ -1032,7 +1031,7 @@ int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int fl SEPARATE_ARRAY(arr); } - return zend_hash_index_update(Z_ARRVAL_P(arr), index, value) ? SUCCESS : FAILURE; + return zend_hash_index_update(Z_ARRVAL_P(arr), (zend_ulong) index, value) ? SUCCESS : FAILURE; } void zephir_array_keys(zval *return_value, zval *input) @@ -1104,7 +1103,11 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int zval *item; zval pzv; zend_array *p; - int i, l, ll, re_update, must_continue, wrap_tmp; + int i, re_update, must_continue, wrap_tmp; + zend_long ll; + /* SL() yields sizeof(...) - 1, a size_t, so that is what the variadic + * slot holds. Reading it back as `int` was reading half of it. */ + size_t l; ZVAL_UNDEF(&pzv); @@ -1128,7 +1131,7 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int case 's': s = va_arg(ap, char*); - l = va_arg(ap, int); + l = va_arg(ap, size_t); /* * Issue #1884: the final offset overwrites its slot regardless of @@ -1173,7 +1176,7 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int break; case 'l': - ll = va_arg(ap, long); + ll = va_arg(ap, zend_long); /* Issue #1884: final offset always overwrites -> store directly. */ if (i == (types_length - 1)) { diff --git a/ext/kernel/array.h b/ext/kernel/array.h index ada7ba3c1..0a78747bc 100644 --- a/ext/kernel/array.h +++ b/ext/kernel/array.h @@ -24,14 +24,24 @@ void ZEPHIR_FASTCALL zephir_create_array(zval *return_value, uint32_t size, int */ void ZEPHIR_FASTCALL zephir_ensure_array(zval *probable_array); +/* + * The `*_long` helpers take a signed `zend_long`, the same width PHP uses for + * an array key or a string offset. They used to take `unsigned long`, which is + * 32-bit on Windows (LLP64): a negative or large offset was truncated there + * before the callee ever saw it, so `s[-1]` read index 4294967295 and a write + * tried to grow the string to 4 GB. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + /** Combined isset/fetch */ int zephir_array_isset_fetch(zval *fetched, const zval *arr, zval *index, int readonly); int zephir_array_isset_string_fetch(zval *fetched, const zval *arr, char *index, uint32_t index_length, int readonly); -int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long index, int readonly); +int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, zend_long index, int readonly); /** Check for index existence */ int ZEPHIR_FASTCALL zephir_array_isset(const zval *arr, zval *index); -int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index); +int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, zend_long index); int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index, uint32_t index_length); /** @@ -41,23 +51,23 @@ int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index * semantics (e.g. array_key_exists). */ int ZEPHIR_FASTCALL zephir_array_isset_value(const zval *arr, zval *index); -int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long index); +int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, zend_long index); int ZEPHIR_FASTCALL zephir_array_isset_value_string(const zval *arr, const char *index, uint32_t index_length); -/** Unset existing indexes */ /** PHP's silent `empty($container[$offset])` handler */ int zephir_isempty_dim(zval *container, zval *offset); -int zephir_isempty_dim_long(zval *container, unsigned long offset); +int zephir_isempty_dim_long(zval *container, zend_long offset); int zephir_isempty_dim_string(zval *container, char *offset, uint32_t offset_length); +/** Unset existing indexes */ int ZEPHIR_FASTCALL zephir_array_unset(zval *arr, zval *index, int flags); -int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int flags); +int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, zend_long index, int flags); int ZEPHIR_FASTCALL zephir_array_unset_string(zval *arr, const char *index, uint32_t index_length, int flags); /** Fetch items from arrays */ int zephir_array_fetch(zval *return_value, zval *arr, zval *index, int flags ZEPHIR_DEBUG_PARAMS); int zephir_array_fetch_string(zval *return_value, zval *arr, const char *index, uint32_t index_length, int flags ZEPHIR_DEBUG_PARAMS); -int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, int flags ZEPHIR_DEBUG_PARAMS); +int zephir_array_fetch_long(zval *return_value, zval *arr, zend_long index, int flags ZEPHIR_DEBUG_PARAMS); /** Append elements to arrays */ int zephir_array_append(zval *arr, zval *value, int separate ZEPHIR_DEBUG_PARAMS); @@ -66,7 +76,7 @@ void zephir_merge_append(zval *left, zval *values); /** Modify array */ int zephir_array_update_zval(zval *arr, zval *index, zval *value, int flags); int zephir_array_update_string(zval *arr, const char *index, uint32_t index_length, zval *value, int flags); -int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS); +int zephir_array_update_long(zval *arr, zend_long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS); void zephir_array_keys(zval *return_value, zval *arr); int zephir_array_key_exists(zval *arr, zval *key); diff --git a/ext/kernel/file.c b/ext/kernel/file.c index 16d3ef516..5f79989ee 100644 --- a/ext/kernel/file.c +++ b/ext/kernel/file.c @@ -191,7 +191,7 @@ void zephir_file_get_contents(zval *return_value, zval *filename) { zend_string *contents; php_stream *stream; - long maxlen = PHP_STREAM_COPY_ALL; + size_t maxlen = PHP_STREAM_COPY_ALL; zval *zcontext = NULL; php_stream_context *context = NULL; @@ -353,7 +353,7 @@ void zephir_prepare_virtual_path(zval *return_value, zval *path, zval *virtual_s */ void zephir_unique_path_key(zval *return_value, zval *path) { - unsigned long h; + zend_ulong h; char *strKey; if (Z_TYPE_P(path) != IS_STRING) { @@ -363,7 +363,7 @@ void zephir_unique_path_key(zval *return_value, zval *path) h = zend_hash_func(Z_STRVAL_P(path), Z_STRLEN_P(path) + 1); strKey = emalloc(24); - sprintf(strKey, "v%lu", h); + snprintf(strKey, 24, "v" ZEND_ULONG_FMT, h); RETVAL_STRING(strKey); efree(strKey); diff --git a/ext/kernel/main.h b/ext/kernel/main.h index ca4c870c6..d53197bbf 100644 --- a/ext/kernel/main.h +++ b/ext/kernel/main.h @@ -48,6 +48,20 @@ extern zend_string* i_self; #define ZEND_ACC_READONLY 0 #endif +/* The float-to-int coercion PHP applies to a `%` operand. PHP 8.1 started + * deprecating a conversion that loses precision ("Deprecate implicit + * non-integer-compatible float to int conversions"), and carries that + * diagnostic in zend_dval_to_lval_safe(), which does not exist on 8.0. Routing + * through this shim keeps the kernel's `%` byte-identical to the engine's on + * every supported version: silent on 8.0, deprecating from 8.1. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 */ +#if PHP_VERSION_ID >= 80100 + #define ZEPHIR_DVAL_TO_LVAL(d) zend_dval_to_lval_safe(d) +#else + #define ZEPHIR_DVAL_TO_LVAL(d) zend_dval_to_lval(d) +#endif + #define SL(str) ZEND_STRL(str) #define SS(str) ZEND_STRS(str) #define ISL(str) (zephir_interned_##str), (sizeof(#str)-1) diff --git a/ext/kernel/math.c b/ext/kernel/math.c index d41d4c447..aed6a6325 100644 --- a/ext/kernel/math.c +++ b/ext/kernel/math.c @@ -151,7 +151,7 @@ extern double _php_math_round(double value, int places, int mode); void zephir_round(zval *return_value, zval *op1, zval *op2, zval *op3) { int places = 0; - long mode = PHP_ROUND_HALF_UP; + int mode = PHP_ROUND_HALF_UP; double return_val; convert_scalar_to_number_ex(op1); diff --git a/ext/kernel/object.c b/ext/kernel/object.c index 0026bc948..5a0fa0611 100644 --- a/ext/kernel/object.c +++ b/ext/kernel/object.c @@ -1045,13 +1045,14 @@ int zephir_update_property_array_multi(zval *object, const char *property, uint3 va_start(ap, types_count); switch (types[0]) { case 's': { - char *str = va_arg(ap, char*); - int len = va_arg(ap, int); + char *str = va_arg(ap, char*); + /* SL() pushes a size_t; see kernel/array.c. */ + size_t len = va_arg(ap, size_t); ZVAL_STRINGL(&offset, str, len); break; } case 'l': - ZVAL_LONG(&offset, va_arg(ap, long)); + ZVAL_LONG(&offset, va_arg(ap, zend_long)); break; case 'z': ZVAL_COPY(&offset, va_arg(ap, zval*)); diff --git a/ext/kernel/operators.c b/ext/kernel/operators.c index 5bee608fd..bb36f338f 100644 --- a/ext/kernel/operators.c +++ b/ext/kernel/operators.c @@ -23,6 +23,7 @@ #include "kernel/string.h" #include "kernel/operators.h" +#include "Zend/zend_exceptions.h" #include "Zend/zend_operators.h" /** @@ -210,7 +211,7 @@ void zephir_concat_self_long(zval *left, const zend_long right) /** * Natural compare with long operandus on right */ -int zephir_compare_strict_long(zval *op1, long op2) +int zephir_compare_strict_long(zval *op1, zend_long op2) { switch (Z_TYPE_P(op1)) { case IS_LONG: @@ -331,7 +332,7 @@ void zephir_convert_to_object(zval *op) /** * Returns the long value of a zval */ -long zephir_get_intval_ex(const zval *op) +zend_long zephir_get_intval_ex(const zval *op) { int type; double double_value = 0; @@ -358,7 +359,7 @@ long zephir_get_intval_ex(const zval *op) return 0; case IS_DOUBLE: - return (long) Z_DVAL_P(op); + return zend_dval_to_lval(Z_DVAL_P(op)); case IS_STRING: { ASSUME(Z_STRVAL_P(op) != NULL); @@ -369,7 +370,7 @@ long zephir_get_intval_ex(const zval *op) return long_value; case IS_DOUBLE: - return (long) double_value; + return zend_dval_to_lval(double_value); } } } @@ -377,7 +378,7 @@ long zephir_get_intval_ex(const zval *op) return 0; } -long zephir_get_charval_ex(const zval *op) +zend_long zephir_get_charval_ex(const zval *op) { switch (Z_TYPE_P(op)) { case IS_ARRAY: @@ -396,7 +397,7 @@ long zephir_get_charval_ex(const zval *op) return 0; case IS_DOUBLE: - return (long) Z_DVAL_P(op); + return zend_dval_to_lval(Z_DVAL_P(op)); case IS_STRING: { if (Z_STRLEN_P(op) > 0) { @@ -575,7 +576,7 @@ int zephir_less_equal(zval *op1, zval *op2) /** * Check if a zval is less than a long value */ -int zephir_less_long(zval *op1, long op2) +int zephir_less_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -593,7 +594,7 @@ int zephir_less_double(zval *op1, double op2) return Z_TYPE(result) == IS_TRUE; } -int zephir_less_equal_long(zval *op1, long op2) +int zephir_less_equal_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -605,7 +606,7 @@ int zephir_less_equal_long(zval *op1, long op2) /** * Check if a zval is greater than a long value */ -int zephir_greater_long(zval *op1, long op2) +int zephir_greater_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -636,7 +637,7 @@ int zephir_greater_equal(zval *op1, zval *op2) /** * Check for greater/equal */ -int zephir_greater_equal_long(zval *op1, long op2) +int zephir_greater_equal_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -644,27 +645,91 @@ int zephir_greater_equal_long(zval *op1, long op2) return Z_TYPE(result) == IS_FALSE; } +/** + * A zero divisor is a DivisionByZeroError in PHP 8, thrown by + * div_function_base()/mod_function() in Zend/zend_operators.c. + * + * These helpers return a value and have no way to abort their caller, so the + * rest of the generated method body runs with the exception pending and the + * engine discards the return value on the way out. That is the convention the + * kernel already uses for the concat overflow guards above and the + * string-offset guards in kernel/array.c. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ +static double zephir_throw_division_by_zero(void) +{ + zend_throw_exception(zend_ce_division_by_zero_error, "Division by zero", 0); + return 0; +} + +static zend_long zephir_throw_modulo_by_zero(void) +{ + zend_throw_exception(zend_ce_division_by_zero_error, "Modulo by zero", 0); + return 0; +} + +/** + * The operand coercion PHP's `%` performs, in the order it performs it: the + * float-to-int deprecation of an operand fires before the zero divisor is + * inspected (convert_op1_op2_long, then the op2_lval == 0 test). + */ +static zend_long zephir_mod_operand(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_DOUBLE: + return ZEPHIR_DVAL_TO_LVAL(Z_DVAL_P(op)); + + case IS_ARRAY: + case IS_OBJECT: + case IS_RESOURCE: + /* PHP 8 throws a TypeError here instead. See #2676. */ + zend_error(E_WARNING, "Unsupported operand types"); + break; + } + + return zephir_get_intval(op); +} + +/** + * The operand coercion `/` performs. Unlike `%` the result is a double, so a + * non-integral operand is kept as one. + */ +static double zephir_div_operand(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_ARRAY: + case IS_OBJECT: + case IS_RESOURCE: + /* PHP 8 throws a TypeError here instead. See #2676. */ + zend_error(E_WARNING, "Unsupported operand types"); + break; + } + + return (double) zephir_get_numberval(op); +} + /** * Do safe divisions between two longs */ -double zephir_safe_div_long_long(long op1, long op2) +double zephir_safe_div_long_long(zend_long op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return (double) op1 / (double) op2; } /** * Do safe divisions between two long/double */ -double zephir_safe_div_long_double(long op1, double op2) +double zephir_safe_div_long_double(zend_long op1, double op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return (double) op1 / op2; } @@ -673,29 +738,24 @@ double zephir_safe_div_long_double(long op1, double op2) */ double zephir_safe_div_double_zval(double op1, zval *op2) { - if (!zephir_get_numberval(op2)) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op2)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + double divisor = zephir_div_operand(op2); + + if (!divisor) { + return zephir_throw_division_by_zero(); } - return op1 / ((double) zephir_get_numberval(op2)); + + return op1 / divisor; } /** * Do safe divisions between two double/long */ -double zephir_safe_div_double_long(double op1, long op2) +double zephir_safe_div_double_long(double op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return op1 / (double) op2; } @@ -705,48 +765,38 @@ double zephir_safe_div_double_long(double op1, long op2) double zephir_safe_div_double_double(double op1, double op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return op1 / op2; } /** * Do safe divisions between two zval/long */ -double zephir_safe_div_zval_long(zval *op1, long op2) +double zephir_safe_div_zval_long(zval *op1, zend_long op2) { + double dividend = zephir_div_operand(op1); + if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; - } - return ((double) zephir_get_numberval(op1)) / (double) op2; + + return dividend / (double) op2; } /** * Do safe divisions between two long/zval */ -double zephir_safe_div_long_zval(long op1, zval *op2) +double zephir_safe_div_long_zval(zend_long op1, zval *op2) { - if (!zephir_get_numberval(op2)) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op2)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + double divisor = zephir_div_operand(op2); + + if (!divisor) { + return zephir_throw_division_by_zero(); } - return (double) op1 / ((double) zephir_get_numberval(op2)); + + return (double) op1 / divisor; } /** @@ -754,47 +804,105 @@ double zephir_safe_div_long_zval(long op1, zval *op2) */ double zephir_safe_div_zval_double(zval *op1, double op2) { + double dividend = zephir_div_operand(op1); + if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + return zephir_throw_division_by_zero(); } - return ((double) zephir_get_numberval(op1)) / op2; + + return dividend / op2; } /** - * Do safe divisions between two longs + * Do safe modulo between two longs. + * + * Every other modulo helper funnels through here, so the two guards PHP's + * mod_function() applies live in one place: a zero divisor throws, and a `-1` + * divisor short-circuits to 0 because PHP_INT_MIN % -1 overflows and raises + * SIGFPE on x86. */ -long zephir_safe_mod_long_long(long op1, long op2) +zend_long zephir_safe_mod_long_long(zend_long op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); + return zephir_throw_modulo_by_zero(); + } + + if (op2 == -1) { return 0; } + return op1 % op2; } /** - * Do safe divisions between two zval/long + * Do safe modulo between two long/double */ -long zephir_safe_mod_zval_long(zval *op1, long op2) +zend_long zephir_safe_mod_long_double(zend_long op1, double op2) { - if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; - } - return ((long) zephir_get_numberval(op1)) % (long) op2; + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(op1, divisor); +} + +/** + * Do safe modulo between two double/long + */ +zend_long zephir_safe_mod_double_long(double op1, zend_long op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + + return zephir_safe_mod_long_long(dividend, op2); +} + +/** + * Do safe modulo between two doubles + */ +zend_long zephir_safe_mod_double_double(double op1, double op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(dividend, divisor); +} + +/** + * Do safe modulo between two zval/long + */ +zend_long zephir_safe_mod_zval_long(zval *op1, zend_long op2) +{ + zend_long dividend = zephir_mod_operand(op1); + + return zephir_safe_mod_long_long(dividend, op2); +} + +/** + * Do safe modulo between two zval/double + */ +zend_long zephir_safe_mod_zval_double(zval *op1, double op2) +{ + zend_long dividend = zephir_mod_operand(op1); + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(dividend, divisor); +} + +/** + * Do safe modulo between two long/zval + */ +zend_long zephir_safe_mod_long_zval(zend_long op1, zval *op2) +{ + zend_long divisor = zephir_mod_operand(op2); + + return zephir_safe_mod_long_long(op1, divisor); +} + +/** + * Do safe modulo between two double/zval + */ +zend_long zephir_safe_mod_double_zval(double op1, zval *op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + zend_long divisor = zephir_mod_operand(op2); + + return zephir_safe_mod_long_long(dividend, divisor); } diff --git a/ext/kernel/operators.h b/ext/kernel/operators.h index cef954027..d2dcd1561 100644 --- a/ext/kernel/operators.h +++ b/ext/kernel/operators.h @@ -68,6 +68,18 @@ #define ZEPHIR_STRING_OFFSET(op1, index) ((index >= 0 && index < Z_STRLEN_P(op1)) ? Z_STRVAL_P(op1)[index] : '\0') +/* + * A PHP integer is a `zend_long`, i.e. `int64_t` on every 64-bit target. A C + * `long` is 64-bit under LP64 (Linux, macOS) but 32-bit under LLP64 (Windows + * x64), so a prototype spelling `long` truncates every value the compiler + * routes through it -- and does so only on Windows, where no CI leg of this + * repo can see it. + * + * Keep `zend_long`/`zend_ulong` in every signature that carries a PHP integer. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + /* concatenation */ void zephir_concat_self(zval *left, zval *right); void zephir_concat_self_str(zval *left, const char *right, int right_length); @@ -76,7 +88,7 @@ void zephir_concat_self_char(zval *left, unsigned char right); /** Strict comparing */ int zephir_compare_strict_string(zval *op1, const char *op2, int op2_length); -int zephir_compare_strict_long(zval *op1, long op2); +int zephir_compare_strict_long(zval *op1, zend_long op2); /** Operator functions */ int zephir_add_function_ex(zval *result, zval *op1, zval *op2); @@ -92,14 +104,14 @@ int zephir_shift_right_function(zval *result, zval *op1, zval *op2); /** Strict comparing */ int zephir_compare_strict_string(zval *op1, const char *op2, int op2_length); -int zephir_compare_strict_long(zval *op1, long op2); +int zephir_compare_strict_long(zval *op1, zend_long op2); int zephir_compare_strict_double(zval *op1, double op2); int zephir_compare_strict_bool(zval *op1, zend_bool op2); void zephir_cast(zval *result, zval *var, uint32_t type); void zephir_convert_to_object(zval *op); -long zephir_get_intval_ex(const zval *op); -long zephir_get_charval_ex(const zval *op); +zend_long zephir_get_intval_ex(const zval *op); +zend_long zephir_get_charval_ex(const zval *op); double zephir_get_doubleval_ex(const zval *op); zend_bool zephir_get_boolval_ex(zval *op); @@ -109,36 +121,47 @@ int zephir_is_equal(zval *op1, zval *op2); int zephir_is_identical(zval *op1, zval *op2); int zephir_less(zval *op1, zval *op2); -int zephir_less_long(zval *op1, long op2); +int zephir_less_long(zval *op1, zend_long op2); int zephir_less_double(zval *op1, double op2); int zephir_greater(zval *op1, zval *op2); -int zephir_greater_long(zval *op1, long op2); +int zephir_greater_long(zval *op1, zend_long op2); int zephir_greater_double(zval *op1, double op2); int zephir_less_equal(zval *op1, zval *op2); -int zephir_less_equal_long(zval *op1, long op2); +int zephir_less_equal_long(zval *op1, zend_long op2); int zephir_greater_equal(zval *op1, zval *op2); -int zephir_greater_equal_long(zval *op1, long op2); +int zephir_greater_equal_long(zval *op1, zend_long op2); -double zephir_safe_div_long_long(long op1, long op2); -double zephir_safe_div_long_double(long op1, double op2); -double zephir_safe_div_double_long(double op1, long op2); +/* + * A zero divisor throws DivisionByZeroError, as PHP 8 does; the helper still + * returns 0 because it has no way to abort its caller, so the rest of the + * generated method body runs with the exception pending and the engine + * discards the return value on the way out. + */ +double zephir_safe_div_long_long(zend_long op1, zend_long op2); +double zephir_safe_div_long_double(zend_long op1, double op2); +double zephir_safe_div_double_long(double op1, zend_long op2); double zephir_safe_div_double_double(double op1, double op2); -double zephir_safe_div_zval_long(zval *op1, long op2); +double zephir_safe_div_zval_long(zval *op1, zend_long op2); double zephir_safe_div_zval_double(zval *op1, double op2); -double zephir_safe_div_long_zval(long op1, zval *op2); +double zephir_safe_div_long_zval(zend_long op1, zval *op2); double zephir_safe_div_double_zval(double op1, zval *op2); -long zephir_safe_mod_long_long(long op1, long op2); -long zephir_safe_mod_long_double(long op1, double op2); -long zephir_safe_mod_double_long(double op1, long op2); -long zephir_safe_mod_double_double(double op1, double op2); -long zephir_safe_mod_zval_long(zval *op1, long op2); -long zephir_safe_mod_zval_double(zval *op1, double op2); -long zephir_safe_mod_long_zval(long op1, zval *op2); -long zephir_safe_mod_double_zval(double op1, zval *op2); +/* + * PHP's `%` converts both operands to `zend_long` and yields a `zend_long`. + * A `-1` divisor short-circuits to 0: `PHP_INT_MIN % -1` overflows and raises + * SIGFPE on x86, which is why php-src special-cases it in mod_function(). + */ +zend_long zephir_safe_mod_long_long(zend_long op1, zend_long op2); +zend_long zephir_safe_mod_long_double(zend_long op1, double op2); +zend_long zephir_safe_mod_double_long(double op1, zend_long op2); +zend_long zephir_safe_mod_double_double(double op1, double op2); +zend_long zephir_safe_mod_zval_long(zval *op1, zend_long op2); +zend_long zephir_safe_mod_zval_double(zval *op1, double op2); +zend_long zephir_safe_mod_long_zval(zend_long op1, zval *op2); +zend_long zephir_safe_mod_double_zval(double op1, zval *op2); #define zephir_get_numberval(z) (Z_TYPE_P(z) == IS_LONG ? Z_LVAL_P(z) : zephir_get_doubleval(z)) #define zephir_get_intval(z) (Z_TYPE_P(z) == IS_LONG ? Z_LVAL_P(z) : zephir_get_intval_ex(z)) diff --git a/ext/kernel/string.c b/ext/kernel/string.c index b949cfa2d..bd350daab 100644 --- a/ext/kernel/string.c +++ b/ext/kernel/string.c @@ -282,7 +282,7 @@ int zephir_end_with_str(const zval *str, char *compared, unsigned int compared_l /** * Makes a substr like the PHP function. This function SUPPORT negative from and length */ -void zephir_substr(zval *return_value, zval *str, long f, long l, int flags) +void zephir_substr(zval *return_value, zval *str, zend_long f, zend_long l, int flags) { zval copy; int use_copy = 0; @@ -712,7 +712,7 @@ int zephir_memnstr_str(const zval *haystack, char *needle, unsigned int needle_l /** * Fast call to explode php function */ -void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, long limit) +void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, zend_long limit) { if (UNEXPECTED(Z_TYPE_P(str) != IS_STRING || Z_TYPE_P(delimiter) != IS_STRING)) { zend_error(E_WARNING, "Invalid arguments supplied for explode()"); @@ -726,7 +726,7 @@ void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, long li /** * Fast call to explode php function */ -void zephir_fast_explode_str(zval *return_value, const char *delim, int delim_length, zval *str, long limit) +void zephir_fast_explode_str(zval *return_value, const char *delim, int delim_length, zval *str, zend_long limit) { zend_string *delimiter; @@ -1044,7 +1044,7 @@ void zephir_fast_str_replace(zval *return_value_ptr, zval *search, zval *replace /** * Execute preg-match without function lookup in the PHP userland */ -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset) +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset) { int use_copy = 0; zval copy, tmp_matches; @@ -1104,7 +1104,7 @@ void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *mat #else -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset) +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset) { zval tmp_flags; zval tmp_offset; diff --git a/ext/kernel/string.h b/ext/kernel/string.h index 93e911ac0..36bff2f9c 100644 --- a/ext/kernel/string.h +++ b/ext/kernel/string.h @@ -31,8 +31,8 @@ void zephir_fast_strtolower(zval *return_value, zval *str); void zephir_fast_strtoupper(zval *return_value, zval *str); void zephir_fast_join(zval *result, zval *glue, zval *pieces); void zephir_fast_join_str(zval *result, char *glue, unsigned int glue_length, zval *pieces); -void zephir_fast_explode(zval *result, zval *delimiter, zval *str, long limit); -void zephir_fast_explode_str(zval *result, const char *delimiter, int delimiter_length, zval *str, long limit); +void zephir_fast_explode(zval *result, zval *delimiter, zval *str, zend_long limit); +void zephir_fast_explode_str(zval *result, const char *delimiter, int delimiter_length, zval *str, zend_long limit); void zephir_fast_strpos(zval *return_value, const zval *haystack, const zval *needle, unsigned int offset); void zephir_fast_strpos_str(zval *return_value, const zval *haystack, char *needle, unsigned int needle_length); void zephir_fast_trim(zval *return_value, zval *str, zval *charlist, int where); @@ -57,10 +57,10 @@ int zephir_json_encode(zval *return_value, zval *v, int opts); int zephir_json_decode(zval *return_value, zval *v, zend_bool assoc); /* Substr */ -void zephir_substr(zval *return_value, zval *str, long from, long length, int flags); +void zephir_substr(zval *return_value, zval *str, zend_long from, zend_long length, int flags); /** Preg-Match */ -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset); +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset); /** Hash */ void zephir_md5(zval *return_value, zval *str); diff --git a/ext/stub.c b/ext/stub.c index 8545a2a7a..b82b4bf5f 100644 --- a/ext/stub.c +++ b/ext/stub.c @@ -214,6 +214,7 @@ zend_class_entry *stub_issue2565_ce; zend_class_entry *stub_issue2635_caller_ce; zend_class_entry *stub_issue2635_impl_ce; zend_class_entry *stub_issue2651_ce; +zend_class_entry *stub_issue2666_ce; zend_class_entry *stub_issue663_ce; zend_class_entry *stub_issue760_ce; zend_class_entry *stub_issue808_ce; @@ -511,6 +512,7 @@ static PHP_MINIT_FUNCTION(stub) ZEPHIR_INIT(Stub_Issue2635_Caller); ZEPHIR_INIT(Stub_Issue2635_Impl); ZEPHIR_INIT(Stub_Issue2651); + ZEPHIR_INIT(Stub_Issue2666); ZEPHIR_INIT(Stub_Issue663); ZEPHIR_INIT(Stub_Issue760); ZEPHIR_INIT(Stub_Issue808); diff --git a/ext/stub.h b/ext/stub.h index bd6ad195e..f2107bc1c 100644 --- a/ext/stub.h +++ b/ext/stub.h @@ -145,6 +145,7 @@ #include "stub/issue2635/caller.zep.h" #include "stub/issue2635/impl.zep.h" #include "stub/issue2651.zep.h" +#include "stub/issue2666.zep.h" #include "stub/issue663.zep.h" #include "stub/issue760.zep.h" #include "stub/issue808.zep.h" diff --git a/ext/stub/arithmetic.zep.c b/ext/stub/arithmetic.zep.c index 357d58605..72eecf3a7 100644 --- a/ext/stub/arithmetic.zep.c +++ b/ext/stub/arithmetic.zep.c @@ -1683,7 +1683,7 @@ PHP_METHOD(Stub_Arithmetic, negativeInt) PHP_METHOD(Stub_Arithmetic, negativeLong) { zval *val_param = NULL; - long val; + zend_long val; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(val) diff --git a/ext/stub/assign.zep.c b/ext/stub/assign.zep.c index 51bcd20cd..bacfc24fd 100644 --- a/ext/stub/assign.zep.c +++ b/ext/stub/assign.zep.c @@ -377,8 +377,8 @@ PHP_METHOD(Stub_Assign, testAssign37) zephir_array_update_long(&arr, 1, &_1, PH_COPY | PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY); ZEPHIR_INIT_NVAR(&_3); ZVAL_LONG(&_3, 6); - zephir_array_update_multi(&arr, &_3, SL("lll"), 3, 1, 2, 5); - zephir_array_update_multi(&arr, &v, SL("llz"), 3, 1, 2, &v); + zephir_array_update_multi(&arr, &_3, SL("lll"), 3, (zend_long) 1, (zend_long) 2, (zend_long) 5); + zephir_array_update_multi(&arr, &v, SL("llz"), 3, (zend_long) 1, (zend_long) 2, &v); RETURN_CCTOR(&arr); } @@ -1271,22 +1271,22 @@ PHP_METHOD(Stub_Assign, testPropertyArray6) zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 19, &_0); ZEPHIR_INIT_VAR(&_1); ZVAL_LONG(&_1, 1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, 0, 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 1); ZEPHIR_INIT_NVAR(&_1); ZVAL_DOUBLE(&_1, 1.5); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, 0, 1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$null, SL("ll"), 2, 0, 1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$false, SL("ll"), 2, 0, 1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$true, SL("ll"), 2, 0, 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$null, SL("ll"), 2, (zend_long) 0, (zend_long) 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$false, SL("ll"), 2, (zend_long) 0, (zend_long) 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$true, SL("ll"), 2, (zend_long) 0, (zend_long) 1); ZEPHIR_INIT_NVAR(&_1); ZVAL_LONG(&_1, 'A'); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, 0, 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 1); ZEPHIR_INIT_NVAR(&_1); ZVAL_STRING(&_1, "hello"); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, 0, 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 1); ZEPHIR_INIT_NVAR(&_1); array_init(&_1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, 0, 1); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 1); RETURN_MM_MEMBER(getThis(), "myArray"); } @@ -1407,22 +1407,22 @@ PHP_METHOD(Stub_Assign, testPropertyArray9) zephir_update_property_zval_cached(this_ptr, _zephir_prop_0, 19, &_0); ZEPHIR_INIT_VAR(&_1); ZVAL_LONG(&_1, 1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, index, index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) index, (zend_long) index); ZEPHIR_INIT_NVAR(&_1); ZVAL_DOUBLE(&_1, 1.5); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, index, index); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$null, SL("ll"), 2, index, index); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$false, SL("ll"), 2, index, index); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$true, SL("ll"), 2, index, index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) index, (zend_long) index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$null, SL("ll"), 2, (zend_long) index, (zend_long) index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$false, SL("ll"), 2, (zend_long) index, (zend_long) index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &__$true, SL("ll"), 2, (zend_long) index, (zend_long) index); ZEPHIR_INIT_NVAR(&_1); ZVAL_LONG(&_1, 'A'); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, index, index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) index, (zend_long) index); ZEPHIR_INIT_NVAR(&_1); ZVAL_STRING(&_1, "hello"); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, index, index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) index, (zend_long) index); ZEPHIR_INIT_NVAR(&_1); array_init(&_1); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, index, index); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_1, SL("ll"), 2, (zend_long) index, (zend_long) index); RETURN_MM_MEMBER(getThis(), "myArray"); } @@ -1690,8 +1690,8 @@ PHP_METHOD(Stub_Assign, testPropertyArray14) zephir_update_property_array_multi(this_ptr, SL("myArray"), &_6, SL("sssss"), 10, SL("a"), SL("b"), SL("c"), SL("d"), SL("e")); ZEPHIR_INIT_NVAR(&_6); ZVAL_LONG(&_6, 6); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &_6, SL("lll"), 3, 1, 2, 5); - zephir_update_property_array_multi(this_ptr, SL("myArray"), &v, SL("llz"), 3, 1, 2, &v); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &_6, SL("lll"), 3, (zend_long) 1, (zend_long) 2, (zend_long) 5); + zephir_update_property_array_multi(this_ptr, SL("myArray"), &v, SL("llz"), 3, (zend_long) 1, (zend_long) 2, &v); RETURN_MM_MEMBER(getThis(), "myArray"); } @@ -1807,22 +1807,22 @@ PHP_METHOD(Stub_Assign, testStaticPropertyArray1) zephir_update_static_property_ce(stub_assign_ce, ZEND_STRL("testVarStatic"), &_0); ZEPHIR_INIT_VAR(&_1); ZVAL_LONG(&_1, 1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_DOUBLE(&_1, 1.5); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("l"), 1, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("l"), 1, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("l"), 1, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("l"), 1, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("l"), 1, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_LONG(&_1, 'A'); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_STRING(&_1, "hello"); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); array_init(&_1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 0); zephir_read_static_property_ce(&_2, stub_assign_ce, SL("testVarStatic"), PH_NOISY_CC | PH_READONLY); RETURN_CTOR(&_2); } @@ -1960,22 +1960,22 @@ PHP_METHOD(Stub_Assign, testStaticPropertyArrayMutli1) zephir_update_static_property_ce(stub_assign_ce, ZEND_STRL("testVarStatic"), &_0); ZEPHIR_INIT_VAR(&_1); ZVAL_LONG(&_1, 1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, 0, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_DOUBLE(&_1, 1.5); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, 0, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("ll"), 2, 0, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("ll"), 2, 0, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("ll"), 2, 0, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("ll"), 2, (zend_long) 0, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("ll"), 2, (zend_long) 0, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("ll"), 2, (zend_long) 0, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_LONG(&_1, 'A'); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, 0, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_STRING(&_1, "hello"); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, 0, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); array_init(&_1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, 0, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("ll"), 2, (zend_long) 0, (zend_long) 0); zephir_read_static_property_ce(&_2, stub_assign_ce, SL("testVarStatic"), PH_NOISY_CC | PH_READONLY); RETURN_CTOR(&_2); } @@ -2095,7 +2095,7 @@ PHP_METHOD(Stub_Assign, testStaticPropertyArrayMulti4) zephir_create_array(&_3, 1, 0); add_index_long(&_3, 3, 4); zephir_array_update_long(&_1, 2, &_3, PH_COPY ZEPHIR_DEBUG_PARAMS_DUMMY); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, 1); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("l"), 1, (zend_long) 1); ZEPHIR_INIT_NVAR(&_2); ZVAL_LONG(&_2, 1); zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_2, SL("s"), 2, SL("s")); @@ -2104,8 +2104,8 @@ PHP_METHOD(Stub_Assign, testStaticPropertyArrayMulti4) zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_2, SL("sssss"), 10, SL("a"), SL("b"), SL("c"), SL("d"), SL("e")); ZEPHIR_INIT_NVAR(&_2); ZVAL_LONG(&_2, 6); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_2, SL("lll"), 3, 1, 2, 5); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &v, SL("llz"), 3, 1, 2, &v); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_2, SL("lll"), 3, (zend_long) 1, (zend_long) 2, (zend_long) 5); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &v, SL("llz"), 3, (zend_long) 1, (zend_long) 2, &v); zephir_read_static_property_ce(&_4, stub_assign_ce, SL("testVarStatic"), PH_NOISY_CC | PH_READONLY); RETURN_CTOR(&_4); } @@ -2129,22 +2129,22 @@ PHP_METHOD(Stub_Assign, testStaticPropertyArrayAppend1) zephir_update_static_property_ce(stub_assign_ce, ZEND_STRL("testVarStatic"), &_0); ZEPHIR_INIT_VAR(&_1); ZVAL_LONG(&_1, 1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_DOUBLE(&_1, 1.5); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("la"), 2, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("la"), 2, 0); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("la"), 2, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$null, SL("la"), 2, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$false, SL("la"), 2, (zend_long) 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &__$true, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_LONG(&_1, 'A'); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); ZVAL_STRING(&_1, "hello"); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_NVAR(&_1); array_init(&_1); - zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, 0); + zephir_update_static_property_array_multi_ce(stub_assign_ce, SL("testVarStatic"), &_1, SL("la"), 2, (zend_long) 0); zephir_read_static_property_ce(&_2, stub_assign_ce, SL("testVarStatic"), PH_NOISY_CC | PH_READONLY); RETURN_CTOR(&_2); } diff --git a/ext/stub/bench.zep.c b/ext/stub/bench.zep.c index de18153a7..82c44153a 100644 --- a/ext/stub/bench.zep.c +++ b/ext/stub/bench.zep.c @@ -80,7 +80,7 @@ PHP_METHOD(Stub_Bench, __construct) PHP_METHOD(Stub_Bench, forOverArray) { zend_bool _2; - long sum; + zend_long sum; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *arr_param = NULL, v, *_0, _1; @@ -138,7 +138,7 @@ PHP_METHOD(Stub_Bench, forOverIterator) { zend_bool _5; zval _0; - long sum; + zend_long sum; zval v, iter, _1, *_2, *_3, _4; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; @@ -239,7 +239,7 @@ PHP_METHOD(Stub_Bench, forOverIteratorWithContinue) { zend_bool _5; zval _0; - long sum, iv; + zend_long sum, iv; zval v, iter, _1, *_2, *_3, _4; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; @@ -345,7 +345,7 @@ PHP_METHOD(Stub_Bench, propertyReadLoop) { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *n_param = NULL, _0$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -381,7 +381,7 @@ PHP_METHOD(Stub_Bench, propertyReadLoop) PHP_METHOD(Stub_Bench, arrayFetchStringLoop) { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; - long n, i, sum; + zend_long n, i, sum; zval *arr_param = NULL, *n_param = NULL, _0$$3, _1$$3; zval arr; @@ -421,7 +421,7 @@ PHP_METHOD(Stub_Bench, methodDispatchLoop) zephir_fcall_cache_entry *_1 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, _0$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -457,7 +457,7 @@ PHP_METHOD(Stub_Bench, returnConst) PHP_METHOD(Stub_Bench, stringConcatLoop) { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; - long n, i; + zend_long n, i; zval base_zv, *n_param = NULL, out, _0$$3; zend_string *base = NULL; @@ -497,7 +497,7 @@ PHP_METHOD(Stub_Bench, newInstanceLoop) zephir_fcall_cache_entry *_0 = NULL, *_2 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, obj, _1$$3; - long n, i, sum; + zend_long n, i, sum; ZVAL_UNDEF(&obj); ZVAL_UNDEF(&_1$$3); @@ -531,7 +531,7 @@ PHP_METHOD(Stub_Bench, newInstanceLoop) */ PHP_METHOD(Stub_Bench, variadicSum) { - long sum; + zend_long sum; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval numbers, v, *_0, _1, *_2; @@ -574,7 +574,7 @@ PHP_METHOD(Stub_Bench, variadicSumNarrowLoop) zephir_fcall_cache_entry *_6 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, _0$$3, _1$$3, _2$$3, _3$$3, _4$$3, _5$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -618,7 +618,7 @@ PHP_METHOD(Stub_Bench, variadicSumWideLoop) zephir_fcall_cache_entry *_16 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, _0$$3, _1$$3, _2$$3, _3$$3, _4$$3, _5$$3, _6$$3, _7$$3, _8$$3, _9$$3, _10$$3, _11$$3, _12$$3, _13$$3, _14$$3, _15$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -679,7 +679,7 @@ PHP_METHOD(Stub_Bench, variadicSumWideLoop) */ PHP_METHOD(Stub_Bench, funcGetArgsSum) { - long sum; + zend_long sum; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *a, a_sub, *b, b_sub, *c, c_sub, *d, d_sub, *e, e_sub, args, v, *_0, _1, *_2; @@ -729,7 +729,7 @@ PHP_METHOD(Stub_Bench, funcGetArgsSumLoop) zephir_fcall_cache_entry *_6 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, _0$$3, _1$$3, _2$$3, _3$$3, _4$$3, _5$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -787,7 +787,7 @@ PHP_METHOD(Stub_Bench, finalDispatchLoop) zephir_fcall_cache_entry *_1 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, _0$$3; - long n, i, sum; + zend_long n, i, sum; zval *this_ptr = getThis(); ZVAL_UNDEF(&_0$$3); @@ -817,7 +817,7 @@ PHP_METHOD(Stub_Bench, staticDispatchLoop) zend_long ZEPHIR_LAST_CALL_STATUS; zephir_fcall_cache_entry *_1 = NULL; zval *n_param = NULL, _0$$3; - long n, i, sum; + zend_long n, i, sum; ZVAL_UNDEF(&_0$$3); ZEND_PARSE_PARAMETERS_START(1, 1) @@ -848,7 +848,7 @@ PHP_METHOD(Stub_Bench, staticDispatchLoop) PHP_METHOD(Stub_Bench, generatorRange) { zval *n_param = NULL; - long n; + zend_long n; zval *this_ptr = getThis(); ZEND_PARSE_PARAMETERS_START(1, 1) @@ -865,7 +865,7 @@ PHP_METHOD(Stub_Bench, arrayRange) zval result; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *n_param = NULL, _0$$3; - long n, i; + zend_long n, i; ZVAL_UNDEF(&_0$$3); ZVAL_UNDEF(&result); @@ -925,7 +925,7 @@ PHP_METHOD(Stub_Bench, sumViaGenerator) zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, v, _0, _1, *_2, _3, *_4, _5; - long n, total; + zend_long n, total; zval *this_ptr = getThis(); ZVAL_UNDEF(&v); @@ -989,7 +989,7 @@ PHP_METHOD(Stub_Bench, sumViaArray) zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *n_param = NULL, v, _0, _1, *_2, _3, *_4, _5; - long n, total; + zend_long n, total; zval *this_ptr = getThis(); ZVAL_UNDEF(&v); @@ -1058,7 +1058,7 @@ PHP_METHOD(Stub_Bench, sumRangeOperator) zend_long _1; zend_bool _0; zval *n_param = NULL; - long n, i, total, _2; + zend_long n, i, total, _2; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) @@ -1091,7 +1091,7 @@ PHP_METHOD(Stub_Bench, sumRangeFn) zend_long _1; zend_bool _0; zval *n_param = NULL; - long n, i, total, _2; + zend_long n, i, total, _2; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) @@ -1130,7 +1130,7 @@ PHP_METHOD(Stub_Bench, buildMatrix) zend_bool _0, _4$$3; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *n_param = NULL, output, i, j, _3$$3, _7$$4; - long n, _2, _6$$3; + zend_long n, _2, _6$$3; ZVAL_UNDEF(&output); ZVAL_UNDEF(&i); @@ -1204,7 +1204,7 @@ PHP_METHOD(Stub_Bench, addAssignUnionLiteral) zend_bool _0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *count_param = NULL, a, _3$$3; - long count, i, _2; + zend_long count, i, _2; ZVAL_UNDEF(&a); ZVAL_UNDEF(&_3$$3); @@ -1277,7 +1277,7 @@ PHP_METHOD(Stub_Bench, addAssignUnionVar) zend_bool _1; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *count_param = NULL, a, b, _0, _4$$3; - long count, i, _3; + zend_long count, i, _3; ZVAL_UNDEF(&a); ZVAL_UNDEF(&b); @@ -1351,7 +1351,7 @@ PHP_METHOD(Stub_Bench, addAssignAccumulate) zend_bool _0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *count_param = NULL, a, b, _3$$3; - long count, i, _2; + zend_long count, i, _2; ZVAL_UNDEF(&a); ZVAL_UNDEF(&b); @@ -1397,7 +1397,7 @@ PHP_METHOD(Stub_Bench, addAssignAccumulate) PHP_METHOD(Stub_Bench, countOptimizedLoop) { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; - long n, i, sum; + zend_long n, i, sum; zval *arr_param = NULL, *n_param = NULL; zval arr; @@ -1433,7 +1433,7 @@ PHP_METHOD(Stub_Bench, countUnoptimizedLoop) zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zephir_fcall_cache_entry *_2 = NULL; - long n, i, sum; + zend_long n, i, sum; zval *arr_param = NULL, *n_param = NULL, _0$$3, _1$$3; zval arr; @@ -1470,7 +1470,7 @@ PHP_METHOD(Stub_Bench, countUnoptimizedLoop) PHP_METHOD(Stub_Bench, implodeOptimizedLoop) { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; - long n, i; + zend_long n, i; zval *arr_param = NULL, *n_param = NULL, out; zval arr; @@ -1508,7 +1508,7 @@ PHP_METHOD(Stub_Bench, implodeUnoptimizedLoop) zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zephir_fcall_cache_entry *_0 = NULL; - long n, i; + zend_long n, i; zval *arr_param = NULL, *n_param = NULL, out; zval arr; @@ -1545,7 +1545,7 @@ PHP_METHOD(Stub_Bench, acosLoop) { double x, sum; zval *n_param = NULL, _0$$3; - long n, i; + zend_long n, i; ZVAL_UNDEF(&_0$$3); ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1570,7 +1570,7 @@ void zep_Stub_Bench_zephir_gen_step_generatorRange(int ht, zend_execute_data *ex { zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval *zephir_gen, zephir_gen_sub, _0$$3; - long n, i; + zend_long n, i; ZVAL_UNDEF(&zephir_gen_sub); ZVAL_UNDEF(&_0$$3); ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); @@ -1578,9 +1578,9 @@ void zep_Stub_Bench_zephir_gen_step_generatorRange(int ht, zend_execute_data *ex zephir_gen = zephir_gen_ext; /* Generator resume dispatch (issue #1849) */ zephir_generator_slots_ensure(zephir_gen, 3); - if (Z_TYPE_P(zephir_generator_slot(zephir_gen, 0)) != IS_UNDEF) { n = (long) Z_LVAL_P(zephir_generator_slot(zephir_gen, 0)); } + if (Z_TYPE_P(zephir_generator_slot(zephir_gen, 0)) != IS_UNDEF) { n = (zend_long) Z_LVAL_P(zephir_generator_slot(zephir_gen, 0)); } ZEPHIR_GEN_RESTORE_ZVAL(zephir_gen, 1, &_0$$3); - if (Z_TYPE_P(zephir_generator_slot(zephir_gen, 2)) != IS_UNDEF) { i = (long) Z_LVAL_P(zephir_generator_slot(zephir_gen, 2)); } + if (Z_TYPE_P(zephir_generator_slot(zephir_gen, 2)) != IS_UNDEF) { i = (zend_long) Z_LVAL_P(zephir_generator_slot(zephir_gen, 2)); } switch (zephir_generator_get_state(zephir_gen)) { case 1: goto zephir_yield_resume_1; default: break; diff --git a/ext/stub/builtin/charmethods.zep.c b/ext/stub/builtin/charmethods.zep.c index 2b2e3426a..4de17f06a 100644 --- a/ext/stub/builtin/charmethods.zep.c +++ b/ext/stub/builtin/charmethods.zep.c @@ -44,7 +44,7 @@ PHP_METHOD(Stub_BuiltIn_CharMethods, getHex) PHP_METHOD(Stub_BuiltIn_CharMethods, getHexForString) { - long _0; + zend_long _0; char ch = 0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zval str_zv, o, _1$$3, _2$$3, _3$$3; diff --git a/ext/stub/cast.zep.c b/ext/stub/cast.zep.c index bd56dabfd..1ea53378b 100644 --- a/ext/stub/cast.zep.c +++ b/ext/stub/cast.zep.c @@ -1118,7 +1118,7 @@ PHP_METHOD(Stub_Cast, issue1841ToInt) zval s; zend_bool b; double d; - long l; + zend_long l; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, i; zval *dyn, dyn_sub, _0, _1, _3, _4; @@ -1335,7 +1335,7 @@ PHP_METHOD(Stub_Cast, issue1841ToLong) zval s; zend_bool b; double d; - long l; + zend_long l; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, i; zval *dyn, dyn_sub, _0, _1, _3; @@ -1446,7 +1446,7 @@ PHP_METHOD(Stub_Cast, issue1841ToFloat) zval s; zend_bool b; double d; - long l; + zend_long l; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, i; zval *dyn, dyn_sub, _0, _1, _3, _4; @@ -1569,7 +1569,7 @@ PHP_METHOD(Stub_Cast, issue1841ToBool) zval s, zero; zend_bool b; double d; - long l; + zend_long l; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, i; zval *dyn, dyn_sub, _0, _1, _2, _3, _5; @@ -1809,7 +1809,7 @@ PHP_METHOD(Stub_Cast, issue1841ToString) zval s, _1, _3, _5, _7, _9, _11, _14, _16, _18, _20, _21, _22, _24, _25, _27; zend_bool b; double d; - long l; + zend_long l; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, i; zval *dyn, dyn_sub, _0, _2, _4, _6, _8, _10, _12, _13, _15, _17, _19, _23, _26; @@ -2282,7 +2282,7 @@ PHP_METHOD(Stub_Cast, issue1841CastKeepsOperandIntact) */ PHP_METHOD(Stub_Cast, issue1841DeclarationInitializers) { - unsigned long w; + zend_ulong w; zend_ulong u; zval _1, _3; double y; diff --git a/ext/stub/cblock.zep.c b/ext/stub/cblock.zep.c index a02bb343b..dbbf943df 100644 --- a/ext/stub/cblock.zep.c +++ b/ext/stub/cblock.zep.c @@ -55,7 +55,7 @@ PHP_METHOD(Stub_Cblock, testCblock1) PHP_METHOD(Stub_Cblock, testCblock2) { - long a; + zend_long a; a = 0; a = fibonacci(MAX_FACTOR); diff --git a/ext/stub/compare.zep.c b/ext/stub/compare.zep.c index 4e77d0798..744431dd5 100644 --- a/ext/stub/compare.zep.c +++ b/ext/stub/compare.zep.c @@ -186,7 +186,7 @@ PHP_METHOD(Stub_Compare, testNotIdenticalZeroInt) PHP_METHOD(Stub_Compare, testNotIdenticalZeroLong) { - long a; + zend_long a; a = 5; RETURN_BOOL(0 != a); } diff --git a/ext/stub/concat.zep.c b/ext/stub/concat.zep.c index f80363c7c..e2c46d044 100644 --- a/ext/stub/concat.zep.c +++ b/ext/stub/concat.zep.c @@ -215,26 +215,30 @@ PHP_METHOD(Stub_Concat, testConcatStringWithIntVar) } /** - * `long` is a C `long`, which is 32-bit on Windows LLP64, so this stays a - * local holding a 32-bit-safe value. The 64-bit range is exercised through - * the `int` variant above, which is a `zend_long` on every platform. + * `long` is a `zend_long` on every platform since #2666, so this carries + * the full 64-bit range exactly like the `int` variant above. It used to + * be a C `long`, 32-bit under Windows LLP64, and this method held a + * 32-bit-safe local to keep CI green. * * @link https://github.com/zephir-lang/zephir/issues/2660 - * @see https://github.com/zephir-lang/zephir/issues/2666 + * @link https://github.com/zephir-lang/zephir/issues/2666 */ PHP_METHOD(Stub_Concat, testConcatStringWithLongVar) { - long number; zval retval; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *number_param = NULL; + zend_long number; ZVAL_UNDEF(&retval); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); - + zephir_fetch_params(1, 1, 0, &number_param); ZEPHIR_INIT_VAR(&retval); ZVAL_STRING(&retval, "n="); - number = 2147483647; zephir_concat_self_long(&retval, number); RETURN_CTOR(&retval); } @@ -264,7 +268,7 @@ PHP_METHOD(Stub_Concat, testConcatStringWithUintVar) */ PHP_METHOD(Stub_Concat, testConcatStringWithUlongVar) { - unsigned long number; + zend_ulong number; zval retval; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; diff --git a/ext/stub/concat.zep.h b/ext/stub/concat.zep.h index 993204dfe..8412e2333 100644 --- a/ext/stub/concat.zep.h +++ b/ext/stub/concat.zep.h @@ -48,7 +48,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_concat_testconcatstringwith ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_concat_testconcatstringwithlongvar, 0, 0, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_concat_testconcatstringwithlongvar, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_concat_testconcatstringwithuintvar, 0, 0, IS_STRING, 0) diff --git a/ext/stub/declaretest.zep.c b/ext/stub/declaretest.zep.c index d490ebe41..c2ad6530b 100644 --- a/ext/stub/declaretest.zep.c +++ b/ext/stub/declaretest.zep.c @@ -99,7 +99,7 @@ PHP_METHOD(Stub_DeclareTest, testDeclare6) PHP_METHOD(Stub_DeclareTest, testDeclare7) { - long a; + zend_long a; a = 1; RETURN_LONG(a); } diff --git a/ext/stub/factorial.zep.c b/ext/stub/factorial.zep.c index 9d9a43d79..008026fc2 100644 --- a/ext/stub/factorial.zep.c +++ b/ext/stub/factorial.zep.c @@ -28,7 +28,7 @@ ZEPHIR_INIT_CLASS(Stub_Factorial) PHP_METHOD(Stub_Factorial, intIterativeFactorial) { zend_bool _0; - long result, i = 0; + zend_long result, i = 0; zval *n_param = NULL; zend_long n, _1, _2; diff --git a/ext/stub/fasta.zep.c b/ext/stub/fasta.zep.c index 1d55dfeee..573ff8fac 100644 --- a/ext/stub/fasta.zep.c +++ b/ext/stub/fasta.zep.c @@ -122,7 +122,7 @@ PHP_METHOD(Stub_Fasta, fastaRepeat) ZEPHIR_INIT_NVAR(&j); ZVAL_LONG(&j, 0); ZEPHIR_INIT_NVAR(&k); - ZVAL_DOUBLE(&k, zephir_safe_mod_zval_long(&l, i)); + ZVAL_LONG(&k, zephir_safe_mod_zval_long(&l, i)); while (1) { if (!(ZEPHIR_LT(&j, &k))) { break; @@ -135,7 +135,7 @@ PHP_METHOD(Stub_Fasta, fastaRepeat) if (zephir_safe_mod_zval_long(n, 60) > 0) { zephir_array_fetch(&_12$$7, &lines, &k, PH_NOISY | PH_READONLY, "stub/fasta.zep", 43); ZVAL_LONG(&_13$$7, 0); - ZVAL_DOUBLE(&_14$$7, zephir_safe_mod_zval_long(n, 60)); + ZVAL_LONG(&_14$$7, zephir_safe_mod_zval_long(n, 60)); ZEPHIR_INIT_VAR(&_15$$7); zephir_substr(&_15$$7, &_12$$7, 0 , zephir_get_intval(&_14$$7), 0); zend_print_zval(&_15$$7, 0); diff --git a/ext/stub/flow.zep.c b/ext/stub/flow.zep.c index 9d0eec350..d52c645c9 100644 --- a/ext/stub/flow.zep.c +++ b/ext/stub/flow.zep.c @@ -1168,7 +1168,7 @@ PHP_METHOD(Stub_Flow, testFor16) { zend_long _2, _3; zend_bool _1; - long _0; + zend_long _0; zval a, b, c; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -1209,7 +1209,7 @@ PHP_METHOD(Stub_Flow, testFor17) { zend_long _2, _3; zend_bool _1; - long _0; + zend_long _0; zval a, b, c; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -1249,7 +1249,7 @@ PHP_METHOD(Stub_Flow, testFor17) PHP_METHOD(Stub_Flow, testFor18) { zend_bool _1; - long _0; + zend_long _0; zval c, _4$$3; zend_long a = 0, b = 0, _2, _3; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -1507,7 +1507,7 @@ PHP_METHOD(Stub_Flow, testFor24) PHP_METHOD(Stub_Flow, testFor30) { - long _0; + zend_long _0; zend_long v = 0; zval b; zval a, _1$$3; @@ -1534,7 +1534,7 @@ PHP_METHOD(Stub_Flow, testFor30) PHP_METHOD(Stub_Flow, testFor31) { - long _0; + zend_long _0; zend_long k = 0, v = 0; zval b; zval a, _1$$3; @@ -1563,7 +1563,7 @@ PHP_METHOD(Stub_Flow, testFor31) PHP_METHOD(Stub_Flow, testFor32) { zend_bool _4; - long sum; + zend_long sum; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *e, e_sub, v, *_0, _1, *_2, _3; @@ -1821,7 +1821,7 @@ PHP_METHOD(Stub_Flow, testFor37) PHP_METHOD(Stub_Flow, testFor38) { zend_bool _1; - long _0; + zend_long _0; zval v; zend_long i, _2, _3; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -1858,7 +1858,7 @@ PHP_METHOD(Stub_Flow, testFor38) PHP_METHOD(Stub_Flow, testFor39) { zval _2; - long _1; + zend_long _1; char _0; zend_long i; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; diff --git a/ext/stub/flow/switchflow.zep.c b/ext/stub/flow/switchflow.zep.c index 6639b2054..6d6e86da6 100644 --- a/ext/stub/flow/switchflow.zep.c +++ b/ext/stub/flow/switchflow.zep.c @@ -224,7 +224,7 @@ PHP_METHOD(Stub_Flow_SwitchFlow, testSwitch13) zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS; zval *a_param = NULL, _0, _1, _2; - long a; + zend_long a; ZVAL_UNDEF(&_0); ZVAL_UNDEF(&_1); diff --git a/ext/stub/issue1629.zep.c b/ext/stub/issue1629.zep.c index f21049fdf..bf5b11875 100644 --- a/ext/stub/issue1629.zep.c +++ b/ext/stub/issue1629.zep.c @@ -332,7 +332,7 @@ PHP_METHOD(Stub_Issue1629, staticIndexTarget) zephir_update_static_property_ce(stub_issue1629_ce, ZEND_STRL("staticParts"), &_0); ZEPHIR_INIT_VAR(&_2); zephir_string_offset_read(&_2, &keyspace_zv, nb, PH_NOISY); - zephir_update_static_property_array_multi_ce(stub_issue1629_ce, SL("staticParts"), &_2, SL("l"), 1, 0); + zephir_update_static_property_array_multi_ce(stub_issue1629_ce, SL("staticParts"), &_2, SL("l"), 1, (zend_long) 0); zephir_read_static_property_ce(&_3, stub_issue1629_ce, SL("staticParts"), PH_NOISY_CC | PH_READONLY); RETURN_CTOR(&_3); } diff --git a/ext/stub/issue2394.zep.c b/ext/stub/issue2394.zep.c index 8fcc0a757..9c84f82d1 100644 --- a/ext/stub/issue2394.zep.c +++ b/ext/stub/issue2394.zep.c @@ -115,7 +115,7 @@ PHP_METHOD(Stub_Issue2394, modulo) Z_PARAM_LONG(a) ZEND_PARSE_PARAMETERS_END(); zephir_fetch_params_without_memory_grow(1, 0, &a_param); - x = (long) (zephir_safe_mod_long_long(a, 3)); + x = zephir_safe_mod_long_long(a, 3); RETURN_LONG(x); } diff --git a/ext/stub/issue2666.zep.c b/ext/stub/issue2666.zep.c new file mode 100644 index 000000000..bf6f7e991 --- /dev/null +++ b/ext/stub/issue2666.zep.c @@ -0,0 +1,747 @@ + +#ifdef HAVE_CONFIG_H +#include "../ext_config.h" +#endif + +#include +#include "../php_ext.h" +#include "../ext.h" + +#include +#include +#include + +#include "kernel/main.h" +#include "kernel/memory.h" +#include "kernel/operators.h" +#include "kernel/object.h" +#include "kernel/array.h" +#include "ext/spl/spl_exceptions.h" +#include "kernel/exception.h" +#include "kernel/string.h" + + +/** + * @issue https://github.com/zephir-lang/zephir/issues/2666 + * + * Zephir's `long`/`ulong` mapped to a C `long`/`unsigned long`, which is + * 32-bit under LLP64 (Windows x64) and 64-bit under LP64 (Linux, macOS), + * while a PHP integer is always a 64-bit `zend_long`. Every method here moves + * a value that only fits in 64 bits across that boundary, and the test asserts + * the result against the same expression evaluated in plain PHP. + * + * The division and modulo methods cover the second half of the fix: the + * `zephir_safe_mod_*` family was declared and emitted but mostly undefined, + * and both families answered a zero divisor with a warning instead of PHP 8's + * DivisionByZeroError. + */ +ZEPHIR_INIT_CLASS(Stub_Issue2666) +{ + ZEPHIR_REGISTER_CLASS(Stub, Issue2666, stub, issue2666, stub_issue2666_method_entry, 0); + + return SUCCESS; +} + +/** + * The reproduction from the issue: `Z_PARAM_LONG(number)` stores a + * `zend_long` through `&number`. + */ +PHP_METHOD(Stub_Issue2666, longParamToString) +{ + zval retval; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *number_param = NULL; + zend_long number; + + ZVAL_UNDEF(&retval); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &number_param); + ZEPHIR_INIT_VAR(&retval); + ZVAL_STRING(&retval, "n="); + zephir_concat_self_long(&retval, number); + RETURN_CTOR(&retval); +} + +PHP_METHOD(Stub_Issue2666, ulongParamToString) +{ + zval retval; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *number_param = NULL; + zend_ulong number; + + ZVAL_UNDEF(&retval); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &number_param); + ZEPHIR_INIT_VAR(&retval); + ZVAL_STRING(&retval, "n="); + zephir_concat_self_long(&retval, number); + RETURN_CTOR(&retval); +} + +PHP_METHOD(Stub_Issue2666, intParamToString) +{ + zval retval; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *number_param = NULL; + zend_long number; + + ZVAL_UNDEF(&retval); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &number_param); + ZEPHIR_INIT_VAR(&retval); + ZVAL_STRING(&retval, "n="); + zephir_concat_self_long(&retval, number); + RETURN_CTOR(&retval); +} + +PHP_METHOD(Stub_Issue2666, longRoundTrip) +{ + zval *number_param = NULL; + zend_long number; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &number_param); + RETURN_LONG(number); +} + +PHP_METHOD(Stub_Issue2666, ulongRoundTrip) +{ + zval *number_param = NULL; + zend_ulong number; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &number_param); + RETURN_LONG(number); +} + +/** + * Parameter -> local -> return, so the value passes through a declared + * local rather than staying in the parameter slot. + */ +PHP_METHOD(Stub_Issue2666, longThroughLocal) +{ + zval *number_param = NULL; + zend_long number, copy = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(number) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &number_param); + copy = number; + RETURN_LONG(copy); +} + +/** + * `zephir_get_intval()`, the funnel every cast shares. Its `_ex` helper + * returned a C `long`, so this truncated even for Zephir `int`. + */ +PHP_METHOD(Stub_Issue2666, castToInt) +{ + zend_long result = 0; + zval *value, value_sub; + + ZVAL_UNDEF(&value_sub); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &value); + result = zephir_get_intval(value); + RETURN_LONG(result); +} + +PHP_METHOD(Stub_Issue2666, castToLong) +{ + zend_long result = 0; + zval *value, value_sub; + + ZVAL_UNDEF(&value_sub); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &value); + result = zephir_get_intval(value); + RETURN_LONG(result); +} + +/** + * Arithmetic on two 64-bit locals: no kernel helper, but the operands and + * the result must all be 64-bit wide. + */ +PHP_METHOD(Stub_Issue2666, longAdd) +{ + zval *a_param = NULL, *b_param = NULL; + zend_long a, b; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(a) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b_param); + RETURN_LONG((a + b)); +} + +/** Array key built from a `long`: `zephir_array_update_long()`. */ +PHP_METHOD(Stub_Issue2666, longArrayKey) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, *value, value_sub; + zend_long key; + + ZVAL_UNDEF(&value_sub); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(key) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 2, 0, &key_param, &value); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + zephir_array_update_long(&result, key, value, PH_COPY | PH_SEPARATE ZEPHIR_DEBUG_PARAMS_DUMMY); + RETURN_CTOR(&result); +} + +/** `zephir_array_fetch_long()` with a 64-bit key. */ +PHP_METHOD(Stub_Issue2666, longArrayFetch) +{ + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zend_long key; + zval *source_param = NULL, *key_param = NULL, _0; + zval source; + + ZVAL_UNDEF(&source); + ZVAL_UNDEF(&_0); + ZEND_PARSE_PARAMETERS_START(2, 2) + ZEPHIR_Z_PARAM_ARRAY(source, source_param) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 2, 0, &source_param, &key_param); + ZEPHIR_OBS_COPY_OR_DUP(&source, source_param); + zephir_array_fetch_long(&_0, &source, key, PH_NOISY | PH_READONLY, "stub/issue2666.zep", 103); + RETURN_CTOR(&_0); +} + +/** `zephir_array_isset_long()`. */ +PHP_METHOD(Stub_Issue2666, longArrayIsset) +{ + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zend_long key; + zval *source_param = NULL, *key_param = NULL; + zval source; + + ZVAL_UNDEF(&source); + ZEND_PARSE_PARAMETERS_START(2, 2) + ZEPHIR_Z_PARAM_ARRAY(source, source_param) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 2, 0, &source_param, &key_param); + ZEPHIR_OBS_COPY_OR_DUP(&source, source_param); + RETURN_MM_BOOL(zephir_array_isset_value_long(&source, key)); +} + +/** `zephir_less_long()` / `zephir_greater_long()`: zval against a native. */ +PHP_METHOD(Stub_Issue2666, varLessThanLong) +{ + zend_long limit; + zval *value, value_sub, *limit_param = NULL; + + ZVAL_UNDEF(&value_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(value) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &value, &limit_param); + RETURN_BOOL(ZEPHIR_LT_LONG(value, limit)); +} + +PHP_METHOD(Stub_Issue2666, varGreaterThanLong) +{ + zend_long limit; + zval *value, value_sub, *limit_param = NULL; + + ZVAL_UNDEF(&value_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(value) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &value, &limit_param); + RETURN_BOOL(ZEPHIR_GT_LONG(value, limit)); +} + +/** `zephir_substr()` took `long from, long length`. */ +PHP_METHOD(Stub_Issue2666, substrWithLongOffset) +{ + zend_long from, length; + zval source_zv, *from_param = NULL, *length_param = NULL, _0, _1; + zend_string *source = NULL; + + ZVAL_UNDEF(&source_zv); + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&_1); + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(source) + Z_PARAM_LONG(from) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END(); + from_param = ZEND_CALL_ARG(execute_data, 2); + length_param = ZEND_CALL_ARG(execute_data, 3); + ZVAL_STR(&source_zv, source); + ZVAL_LONG(&_0, from); + ZVAL_LONG(&_1, length); + zephir_substr(return_value, &source_zv, zephir_get_intval(&_0), zephir_get_intval(&_1), 0); + return; +} + +/** `zephir_fast_explode()` took a `long limit`. */ +PHP_METHOD(Stub_Issue2666, explodeWithLongLimit) +{ + zend_long limit; + zval source_zv, *limit_param = NULL, _0; + zend_string *source = NULL; + + ZVAL_UNDEF(&source_zv); + ZVAL_UNDEF(&_0); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(source) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END(); + limit_param = ZEND_CALL_ARG(execute_data, 2); + ZVAL_STR(&source_zv, source); + ZVAL_LONG(&_0, limit); + zephir_fast_explode_str(return_value, SL(","), &source_zv, zephir_get_intval(&_0) ); + return; +} + +/** + * The eight `zephir_safe_div_*` / `zephir_safe_mod_*` shapes the compiler + * can actually reach. A double *literal* on the right is the only way to + * reach the `*_double` variants: a `double` variable there is rejected by + * ModOperator/DivOperator. + */ +PHP_METHOD(Stub_Issue2666, divLongLong) +{ + zval *a_param = NULL, *b_param = NULL; + zend_long a, b; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(a) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b_param); + RETURN_DOUBLE(zephir_safe_div_long_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, divLongDouble) +{ + zval *a_param = NULL; + zend_long a; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(a) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a_param); + RETURN_DOUBLE(zephir_safe_div_long_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, divDoubleLong) +{ + zend_long b; + zval *a_param = NULL, *b_param = NULL; + double a; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a_param) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b_param); + a = zephir_get_doubleval(a_param); + RETURN_DOUBLE(zephir_safe_div_double_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, divDoubleDouble) +{ + zval *a_param = NULL; + double a; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(a_param) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a_param); + a = zephir_get_doubleval(a_param); + RETURN_DOUBLE(zephir_safe_div_double_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, divVarLong) +{ + zend_long b; + zval *a, a_sub, *b_param = NULL; + + ZVAL_UNDEF(&a_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a, &b_param); + RETURN_DOUBLE(zephir_safe_div_zval_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, divVarDouble) +{ + zval *a, a_sub; + + ZVAL_UNDEF(&a_sub); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(a) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a); + RETURN_DOUBLE(zephir_safe_div_zval_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, divLongVar) +{ + zval *a_param = NULL, *b, b_sub; + zend_long a; + + ZVAL_UNDEF(&b_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(a) + Z_PARAM_ZVAL(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b); + RETURN_DOUBLE(zephir_safe_div_long_zval(a, b)); +} + +PHP_METHOD(Stub_Issue2666, divDoubleVar) +{ + zval *a_param = NULL, *b, b_sub; + double a; + + ZVAL_UNDEF(&b_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a_param) + Z_PARAM_ZVAL(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b); + a = zephir_get_doubleval(a_param); + RETURN_DOUBLE(zephir_safe_div_double_zval(a, b)); +} + +PHP_METHOD(Stub_Issue2666, modLongLong) +{ + zval *a_param = NULL, *b_param = NULL; + zend_long a, b; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(a) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b_param); + RETURN_LONG(zephir_safe_mod_long_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, modLongDouble) +{ + zval *a_param = NULL; + zend_long a; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(a) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a_param); + RETURN_LONG(zephir_safe_mod_long_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, modDoubleLong) +{ + zend_long b; + zval *a_param = NULL, *b_param = NULL; + double a; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a_param) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b_param); + a = zephir_get_doubleval(a_param); + RETURN_LONG(zephir_safe_mod_double_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, modDoubleDouble) +{ + zval *a_param = NULL; + double a; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(a_param) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a_param); + a = zephir_get_doubleval(a_param); + RETURN_LONG(zephir_safe_mod_double_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, modVarLong) +{ + zend_long b; + zval *a, a_sub, *b_param = NULL; + + ZVAL_UNDEF(&a_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a) + Z_PARAM_LONG(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a, &b_param); + RETURN_LONG(zephir_safe_mod_zval_long(a, b)); +} + +PHP_METHOD(Stub_Issue2666, modVarDouble) +{ + zval *a, a_sub; + + ZVAL_UNDEF(&a_sub); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(a) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &a); + RETURN_LONG(zephir_safe_mod_zval_double(a, 2.5)); +} + +PHP_METHOD(Stub_Issue2666, modLongVar) +{ + zval *a_param = NULL, *b, b_sub; + zend_long a; + + ZVAL_UNDEF(&b_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(a) + Z_PARAM_ZVAL(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b); + RETURN_LONG(zephir_safe_mod_long_zval(a, b)); +} + +PHP_METHOD(Stub_Issue2666, modDoubleVar) +{ + zval *a_param = NULL, *b, b_sub; + double a; + + ZVAL_UNDEF(&b_sub); + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(a_param) + Z_PARAM_ZVAL(b) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(2, 0, &a_param, &b); + a = zephir_get_doubleval(a_param); + RETURN_LONG(zephir_safe_mod_double_zval(a, b)); +} + +/** Literal double on the left: the only shape reaching `*_double_double`. */ +PHP_METHOD(Stub_Issue2666, divLiteralDoubleByDouble) +{ + zval *b_param = NULL; + double b; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(b_param) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &b_param); + b = zephir_get_doubleval(b_param); + RETURN_DOUBLE(zephir_safe_div_double_double(2.5, b)); +} + +PHP_METHOD(Stub_Issue2666, modLiteralDoubleByDouble) +{ + zval *b_param = NULL; + double b; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(b_param) + ZEND_PARSE_PARAMETERS_END(); + zephir_fetch_params_without_memory_grow(1, 0, &b_param); + b = zephir_get_doubleval(b_param); + RETURN_LONG(zephir_safe_mod_double_double(2.5, b)); +} + +/** + * Multi-dimensional keys reach `zephir_array_update_multi()` through a + * variadic slot read back as `zend_long`. An integer literal is a C `int`, + * so the upper half of the slot was whatever the ABI left there: on Windows + * `a[0][1]` produced the key 140733193388033. `uint`/`ulong` were worse, + * passing `&i` and using the pointer as the key. + */ +PHP_METHOD(Stub_Issue2666, multiLiteralKeys) +{ + zval _0; + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + + ZVAL_UNDEF(&result); + ZVAL_UNDEF(&_0); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) 0, (zend_long) 1); + RETURN_CTOR(&result); +} + +PHP_METHOD(Stub_Issue2666, multiIntKey) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, _0; + zend_long key; + + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &key_param); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) key, (zend_long) 2); + RETURN_CTOR(&result); +} + +PHP_METHOD(Stub_Issue2666, multiUintKey) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, _0; + zend_ulong key; + + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &key_param); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) key, (zend_long) 2); + RETURN_CTOR(&result); +} + +PHP_METHOD(Stub_Issue2666, multiLongKey) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, _0; + zend_long key; + + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &key_param); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) key, (zend_long) 2); + RETURN_CTOR(&result); +} + +PHP_METHOD(Stub_Issue2666, multiUlongKey) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, _0; + zend_ulong key; + + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &key_param); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) key, (zend_long) 2); + RETURN_CTOR(&result); +} + +/** Enough offsets to spill past the register arguments. */ +PHP_METHOD(Stub_Issue2666, multiDeepKeys) +{ + zval _0; + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + + ZVAL_UNDEF(&result); + ZVAL_UNDEF(&_0); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("lllllllll"), 9, (zend_long) 0, (zend_long) 1, (zend_long) 2, (zend_long) 3, (zend_long) 4, (zend_long) 5, (zend_long) 6, (zend_long) 7, (zend_long) 8); + RETURN_CTOR(&result); +} + +/** Mixed string and integer offsets in one call. */ +PHP_METHOD(Stub_Issue2666, multiMixedKeys) +{ + zval result; + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *key_param = NULL, _0; + zend_long key; + + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&result); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(key) + ZEND_PARSE_PARAMETERS_END(); + ZEPHIR_METHOD_GLOBALS_PTR = pecalloc(1, sizeof(zephir_method_globals), 0); + zephir_memory_grow_stack(ZEPHIR_METHOD_GLOBALS_PTR, __func__); + zephir_fetch_params(1, 1, 0, &key_param); + ZEPHIR_INIT_VAR(&result); + array_init(&result); + ZEPHIR_INIT_VAR(&_0); + ZVAL_STRING(&_0, "v"); + zephir_array_update_multi(&result, &_0, SL("sl"), 3, SL("k"), (zend_long) key); + RETURN_CTOR(&result); +} + diff --git a/ext/stub/issue2666.zep.h b/ext/stub/issue2666.zep.h new file mode 100644 index 000000000..da0c7dd23 --- /dev/null +++ b/ext/stub/issue2666.zep.h @@ -0,0 +1,272 @@ + +extern zend_class_entry *stub_issue2666_ce; + +ZEPHIR_INIT_CLASS(Stub_Issue2666); + +PHP_METHOD(Stub_Issue2666, longParamToString); +PHP_METHOD(Stub_Issue2666, ulongParamToString); +PHP_METHOD(Stub_Issue2666, intParamToString); +PHP_METHOD(Stub_Issue2666, longRoundTrip); +PHP_METHOD(Stub_Issue2666, ulongRoundTrip); +PHP_METHOD(Stub_Issue2666, longThroughLocal); +PHP_METHOD(Stub_Issue2666, castToInt); +PHP_METHOD(Stub_Issue2666, castToLong); +PHP_METHOD(Stub_Issue2666, longAdd); +PHP_METHOD(Stub_Issue2666, longArrayKey); +PHP_METHOD(Stub_Issue2666, longArrayFetch); +PHP_METHOD(Stub_Issue2666, longArrayIsset); +PHP_METHOD(Stub_Issue2666, varLessThanLong); +PHP_METHOD(Stub_Issue2666, varGreaterThanLong); +PHP_METHOD(Stub_Issue2666, substrWithLongOffset); +PHP_METHOD(Stub_Issue2666, explodeWithLongLimit); +PHP_METHOD(Stub_Issue2666, divLongLong); +PHP_METHOD(Stub_Issue2666, divLongDouble); +PHP_METHOD(Stub_Issue2666, divDoubleLong); +PHP_METHOD(Stub_Issue2666, divDoubleDouble); +PHP_METHOD(Stub_Issue2666, divVarLong); +PHP_METHOD(Stub_Issue2666, divVarDouble); +PHP_METHOD(Stub_Issue2666, divLongVar); +PHP_METHOD(Stub_Issue2666, divDoubleVar); +PHP_METHOD(Stub_Issue2666, modLongLong); +PHP_METHOD(Stub_Issue2666, modLongDouble); +PHP_METHOD(Stub_Issue2666, modDoubleLong); +PHP_METHOD(Stub_Issue2666, modDoubleDouble); +PHP_METHOD(Stub_Issue2666, modVarLong); +PHP_METHOD(Stub_Issue2666, modVarDouble); +PHP_METHOD(Stub_Issue2666, modLongVar); +PHP_METHOD(Stub_Issue2666, modDoubleVar); +PHP_METHOD(Stub_Issue2666, divLiteralDoubleByDouble); +PHP_METHOD(Stub_Issue2666, modLiteralDoubleByDouble); +PHP_METHOD(Stub_Issue2666, multiLiteralKeys); +PHP_METHOD(Stub_Issue2666, multiIntKey); +PHP_METHOD(Stub_Issue2666, multiUintKey); +PHP_METHOD(Stub_Issue2666, multiLongKey); +PHP_METHOD(Stub_Issue2666, multiUlongKey); +PHP_METHOD(Stub_Issue2666, multiDeepKeys); +PHP_METHOD(Stub_Issue2666, multiMixedKeys); + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longparamtostring, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_ulongparamtostring, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_intparamtostring, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longroundtrip, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_ulongroundtrip, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longthroughlocal, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, number, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_casttoint, 0, 1, IS_LONG, 0) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_casttolong, 0, 1, IS_LONG, 0) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longadd, 0, 2, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longarraykey, 0, 2, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_longarrayfetch, 0, 0, 2) + ZEND_ARG_ARRAY_INFO(0, source, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_longarrayisset, 0, 2, _IS_BOOL, 0) + ZEND_ARG_ARRAY_INFO(0, source, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_varlessthanlong, 0, 2, _IS_BOOL, 0) + ZEND_ARG_INFO(0, value) + ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_vargreaterthanlong, 0, 2, _IS_BOOL, 0) + ZEND_ARG_INFO(0, value) + ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_substrwithlongoffset, 0, 3, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, from, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_explodewithlonglimit, 0, 2, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, source, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, limit, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divlonglong, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divlongdouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divdoublelong, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divdoubledouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divvarlong, 0, 0, 2) + ZEND_ARG_INFO(0, a) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divvardouble, 0, 0, 1) + ZEND_ARG_INFO(0, a) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divlongvar, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) + ZEND_ARG_INFO(0, b) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divdoublevar, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) + ZEND_ARG_INFO(0, b) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modlonglong, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modlongdouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_moddoublelong, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_moddoubledouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modvarlong, 0, 0, 2) + ZEND_ARG_INFO(0, a) + ZEND_ARG_TYPE_INFO(0, b, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modvardouble, 0, 0, 1) + ZEND_ARG_INFO(0, a) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modlongvar, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_LONG, 0) + ZEND_ARG_INFO(0, b) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_moddoublevar, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, a, IS_DOUBLE, 0) + ZEND_ARG_INFO(0, b) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_divliteraldoublebydouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, b, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_issue2666_modliteraldoublebydouble, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, b, IS_DOUBLE, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multiliteralkeys, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multiintkey, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multiuintkey, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multilongkey, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multiulongkey, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multideepkeys, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_issue2666_multimixedkeys, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEPHIR_INIT_FUNCS(stub_issue2666_method_entry) { + PHP_ME(Stub_Issue2666, longParamToString, arginfo_stub_issue2666_longparamtostring, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, ulongParamToString, arginfo_stub_issue2666_ulongparamtostring, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, intParamToString, arginfo_stub_issue2666_intparamtostring, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longRoundTrip, arginfo_stub_issue2666_longroundtrip, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, ulongRoundTrip, arginfo_stub_issue2666_ulongroundtrip, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longThroughLocal, arginfo_stub_issue2666_longthroughlocal, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, castToInt, arginfo_stub_issue2666_casttoint, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, castToLong, arginfo_stub_issue2666_casttolong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longAdd, arginfo_stub_issue2666_longadd, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longArrayKey, arginfo_stub_issue2666_longarraykey, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longArrayFetch, arginfo_stub_issue2666_longarrayfetch, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, longArrayIsset, arginfo_stub_issue2666_longarrayisset, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, varLessThanLong, arginfo_stub_issue2666_varlessthanlong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, varGreaterThanLong, arginfo_stub_issue2666_vargreaterthanlong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, substrWithLongOffset, arginfo_stub_issue2666_substrwithlongoffset, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, explodeWithLongLimit, arginfo_stub_issue2666_explodewithlonglimit, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divLongLong, arginfo_stub_issue2666_divlonglong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divLongDouble, arginfo_stub_issue2666_divlongdouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divDoubleLong, arginfo_stub_issue2666_divdoublelong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divDoubleDouble, arginfo_stub_issue2666_divdoubledouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divVarLong, arginfo_stub_issue2666_divvarlong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divVarDouble, arginfo_stub_issue2666_divvardouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divLongVar, arginfo_stub_issue2666_divlongvar, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divDoubleVar, arginfo_stub_issue2666_divdoublevar, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modLongLong, arginfo_stub_issue2666_modlonglong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modLongDouble, arginfo_stub_issue2666_modlongdouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modDoubleLong, arginfo_stub_issue2666_moddoublelong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modDoubleDouble, arginfo_stub_issue2666_moddoubledouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modVarLong, arginfo_stub_issue2666_modvarlong, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modVarDouble, arginfo_stub_issue2666_modvardouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modLongVar, arginfo_stub_issue2666_modlongvar, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modDoubleVar, arginfo_stub_issue2666_moddoublevar, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, divLiteralDoubleByDouble, arginfo_stub_issue2666_divliteraldoublebydouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, modLiteralDoubleByDouble, arginfo_stub_issue2666_modliteraldoublebydouble, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiLiteralKeys, arginfo_stub_issue2666_multiliteralkeys, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiIntKey, arginfo_stub_issue2666_multiintkey, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiUintKey, arginfo_stub_issue2666_multiuintkey, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiLongKey, arginfo_stub_issue2666_multilongkey, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiUlongKey, arginfo_stub_issue2666_multiulongkey, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiDeepKeys, arginfo_stub_issue2666_multideepkeys, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Issue2666, multiMixedKeys, arginfo_stub_issue2666_multimixedkeys, ZEND_ACC_PUBLIC) + PHP_FE_END +}; diff --git a/ext/stub/issue760.zep.c b/ext/stub/issue760.zep.c index 8ac4fea88..1776e93c6 100644 --- a/ext/stub/issue760.zep.c +++ b/ext/stub/issue760.zep.c @@ -738,7 +738,7 @@ PHP_METHOD(Stub_Issue760, dynTraverseReverse) PHP_METHOD(Stub_Issue760, strTraverse) { - long _0; + zend_long _0; zval out; char c = 0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; diff --git a/ext/stub/mcall.zep.c b/ext/stub/mcall.zep.c index 43b9cea41..1d99b8376 100644 --- a/ext/stub/mcall.zep.c +++ b/ext/stub/mcall.zep.c @@ -443,7 +443,7 @@ PHP_METHOD(Stub_Mcall, testCall18) PHP_METHOD(Stub_Mcall, testMethod19) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(a) @@ -460,7 +460,7 @@ PHP_METHOD(Stub_Mcall, testCall20) zephir_fcall_cache_entry *_4 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, _1; zval *k_param = NULL, *p, p_sub, _3$$3; - long k, i = 0, j, _2; + zend_long k, i = 0, j, _2; zval *this_ptr = getThis(); ZVAL_UNDEF(&p_sub); @@ -498,7 +498,7 @@ PHP_METHOD(Stub_Mcall, testCall20) PHP_METHOD(Stub_Mcall, testMethod21) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(a) @@ -515,7 +515,7 @@ PHP_METHOD(Stub_Mcall, testCall22) zephir_fcall_cache_entry *_4 = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, _1; zval *k_param = NULL, *p, p_sub, _3$$3; - long k, i = 0, j, _2; + zend_long k, i = 0, j, _2; zval *this_ptr = getThis(); ZVAL_UNDEF(&p_sub); diff --git a/ext/stub/mcallinternal.zep.c b/ext/stub/mcallinternal.zep.c index de14a8b43..d6d1604ed 100644 --- a/ext/stub/mcallinternal.zep.c +++ b/ext/stub/mcallinternal.zep.c @@ -55,7 +55,7 @@ void zep_Stub_McallInternal_b(int ht, zend_execute_data *execute_data, zval *ret void zep_Stub_McallInternal_c(int ht, zend_execute_data *execute_data, zval *return_value, zval *this_ptr, int return_value_used, zval *a_param_ext, zval *b_param_ext) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; a_param = a_param_ext; b_param = b_param_ext; @@ -154,7 +154,7 @@ PHP_METHOD(Stub_McallInternal, g) { zval _3$$3, _4$$3, _5$$3; zend_bool _0; - long i = 0; + zend_long i = 0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, a, _1, _2; zval *this_ptr = getThis(); @@ -193,7 +193,7 @@ PHP_METHOD(Stub_McallInternal, g) void zep_Stub_McallInternal_other(int ht, zend_execute_data *execute_data, zval *return_value, zval *this_ptr, int return_value_used, zval *a_param_ext, zval *b_param_ext) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; a_param = a_param_ext; b_param = b_param_ext; @@ -207,7 +207,7 @@ PHP_METHOD(Stub_McallInternal, callFibonacci) { zval _3$$3, _4$$3, _5$$3; zend_bool _0; - long i; + zend_long i; double p; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; zend_long ZEPHIR_LAST_CALL_STATUS, _1, _2; diff --git a/ext/stub/multidimarray.zep.c b/ext/stub/multidimarray.zep.c index 67c51d29c..d2effc3f9 100644 --- a/ext/stub/multidimarray.zep.c +++ b/ext/stub/multidimarray.zep.c @@ -165,7 +165,7 @@ PHP_METHOD(Stub_MultiDimArray, generateTyped) j = _5$$3; ZEPHIR_INIT_NVAR(&_7$$4); ZVAL_LONG(&_7$$4, 1); - zephir_array_update_multi(&output, &_7$$4, SL("ll"), 2, i, j); + zephir_array_update_multi(&output, &_7$$4, SL("ll"), 2, (zend_long) i, (zend_long) j); } } } @@ -190,7 +190,7 @@ PHP_METHOD(Stub_MultiDimArray, intKeys) array_init(&a); ZEPHIR_INIT_VAR(&_0); ZVAL_LONG(&_0, 10); - zephir_array_update_multi(&a, &_0, SL("ll"), 2, 0, 1); + zephir_array_update_multi(&a, &_0, SL("ll"), 2, (zend_long) 0, (zend_long) 1); RETURN_CCTOR(&a); } @@ -253,10 +253,10 @@ PHP_METHOD(Stub_MultiDimArray, mixedKeys) array_init(&a); ZEPHIR_INIT_VAR(&_0); ZVAL_LONG(&_0, 1); - zephir_array_update_multi(&a, &_0, SL("sl"), 3, SL("k"), 0); + zephir_array_update_multi(&a, &_0, SL("sl"), 3, SL("k"), (zend_long) 0); ZEPHIR_INIT_NVAR(&_0); ZVAL_LONG(&_0, 2); - zephir_array_update_multi(&a, &_0, SL("ls"), 3, 1, SL("k")); + zephir_array_update_multi(&a, &_0, SL("ls"), 3, (zend_long) 1, SL("k")); RETURN_CCTOR(&a); } @@ -279,10 +279,10 @@ PHP_METHOD(Stub_MultiDimArray, appendFinal) array_init(&a); ZEPHIR_INIT_VAR(&_0); ZVAL_STRING(&_0, "first"); - zephir_array_update_multi(&a, &_0, SL("la"), 2, 0); + zephir_array_update_multi(&a, &_0, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_VAR(&_1); ZVAL_STRING(&_1, "second"); - zephir_array_update_multi(&a, &_1, SL("la"), 2, 0); + zephir_array_update_multi(&a, &_1, SL("la"), 2, (zend_long) 0); ZEPHIR_INIT_VAR(&_2); ZVAL_LONG(&_2, 1); zephir_array_update_multi(&a, &_2, SL("sa"), 3, SL("bag")); @@ -307,10 +307,10 @@ PHP_METHOD(Stub_MultiDimArray, overwriteFinal) array_init(&a); ZEPHIR_INIT_VAR(&_0); ZVAL_LONG(&_0, 1); - zephir_array_update_multi(&a, &_0, SL("ll"), 2, 0, 0); + zephir_array_update_multi(&a, &_0, SL("ll"), 2, (zend_long) 0, (zend_long) 0); ZEPHIR_INIT_NVAR(&_0); ZVAL_LONG(&_0, 2); - zephir_array_update_multi(&a, &_0, SL("ll"), 2, 0, 0); + zephir_array_update_multi(&a, &_0, SL("ll"), 2, (zend_long) 0, (zend_long) 0); RETURN_CCTOR(&a); } diff --git a/ext/stub/nativearray.zep.c b/ext/stub/nativearray.zep.c index a0ec2c87f..c1f046d51 100644 --- a/ext/stub/nativearray.zep.c +++ b/ext/stub/nativearray.zep.c @@ -655,7 +655,7 @@ PHP_METHOD(Stub_NativeArray, testArray28) PHP_METHOD(Stub_NativeArray, testArray29) { - long b = 0; + zend_long b = 0; zval a; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -738,7 +738,7 @@ PHP_METHOD(Stub_NativeArray, testArrayAccess2) PHP_METHOD(Stub_NativeArray, testArrayAccess3) { - long c = 0; + zend_long c = 0; zval a, b, _0; zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; @@ -1313,7 +1313,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate3) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("ll"), 2, 0, 1); + zephir_array_update_multi(&a, &__$null, SL("ll"), 2, (zend_long) 0, (zend_long) 1); RETURN_CCTOR(&a); } @@ -1329,7 +1329,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate4) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("lll"), 3, 0, 1, 2); + zephir_array_update_multi(&a, &__$null, SL("lll"), 3, (zend_long) 0, (zend_long) 1, (zend_long) 2); RETURN_CCTOR(&a); } @@ -1345,7 +1345,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate5) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("sls"), 5, SL("a"), 1, SL("c")); + zephir_array_update_multi(&a, &__$null, SL("sls"), 5, SL("a"), (zend_long) 1, SL("c")); RETURN_CCTOR(&a); } @@ -1361,7 +1361,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate6) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("lsl"), 4, 0, SL("b"), 2); + zephir_array_update_multi(&a, &__$null, SL("lsl"), 4, (zend_long) 0, SL("b"), (zend_long) 2); RETURN_CCTOR(&a); } @@ -1378,8 +1378,8 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate7) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$true, SL("sls"), 5, SL("a"), 1, SL("c")); - zephir_array_update_multi(&a, &__$false, SL("lsl"), 4, 0, SL("b"), 2); + zephir_array_update_multi(&a, &__$true, SL("sls"), 5, SL("a"), (zend_long) 1, SL("c")); + zephir_array_update_multi(&a, &__$false, SL("lsl"), 4, (zend_long) 0, SL("b"), (zend_long) 2); RETURN_CCTOR(&a); } @@ -1397,9 +1397,9 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate8) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), 0); - zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), 1); - zephir_array_update_multi(&a, &__$false, SL("sl"), 3, SL("a"), 2); + zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), (zend_long) 0); + zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), (zend_long) 1); + zephir_array_update_multi(&a, &__$false, SL("sl"), 3, SL("a"), (zend_long) 2); RETURN_CCTOR(&a); } @@ -1417,9 +1417,9 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate9) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), 0); - zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), 1); - zephir_array_update_multi(&a, &__$false, SL("sl"), 3, SL("a"), 1); + zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), (zend_long) 0); + zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), (zend_long) 1); + zephir_array_update_multi(&a, &__$false, SL("sl"), 3, SL("a"), (zend_long) 1); RETURN_CCTOR(&a); } @@ -1437,9 +1437,9 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate10) ZEPHIR_INIT_VAR(&a); array_init(&a); - zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), 0); - zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), 1); - zephir_array_update_multi(&a, &__$false, SL("sls"), 5, SL("a"), 1, SL("c")); + zephir_array_update_multi(&a, &__$null, SL("sl"), 3, SL("a"), (zend_long) 0); + zephir_array_update_multi(&a, &__$true, SL("sl"), 3, SL("a"), (zend_long) 1); + zephir_array_update_multi(&a, &__$false, SL("sls"), 5, SL("a"), (zend_long) 1, SL("c")); RETURN_CCTOR(&a); } @@ -1526,7 +1526,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayUpdate13) add_assoc_long_ex(&_0, SL("b"), 2); add_assoc_long_ex(&_0, SL("c"), 3); zephir_array_update_long(&a, 1, &_0, PH_COPY ZEPHIR_DEBUG_PARAMS_DUMMY); - zephir_array_update_multi(&a, &__$false, SL("lss"), 5, 0, SL("a"), SL("y")); + zephir_array_update_multi(&a, &__$false, SL("lss"), 5, (zend_long) 0, SL("a"), SL("y")); RETURN_CCTOR(&a); } @@ -1545,7 +1545,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayAppend1) array_init(&a); ZEPHIR_INIT_VAR(&b); ZVAL_STRING(&b, "y"); - zephir_array_update_multi(&a, &__$null, SL("lsza"), 5, 0, SL("hello"), &b); + zephir_array_update_multi(&a, &__$null, SL("lsza"), 5, (zend_long) 0, SL("hello"), &b); RETURN_CCTOR(&a); } @@ -1567,7 +1567,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayAppend2) b = 100; ZEPHIR_INIT_VAR(&x); ZVAL_STRING(&x, "hello"); - zephir_array_update_multi(&a, &__$null, SL("lslza"), 6, 0, SL("hello"), b, &x); + zephir_array_update_multi(&a, &__$null, SL("lslza"), 6, (zend_long) 0, SL("hello"), (zend_long) b, &x); RETURN_CCTOR(&a); } @@ -1592,14 +1592,14 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayAppend3) array_init(&a); ZEPHIR_INIT_VAR(&b); ZVAL_STRING(&b, "y"); - zephir_array_update_multi(&a, &__$null, SL("lsz"), 4, 0, SL("hello"), &b); + zephir_array_update_multi(&a, &__$null, SL("lsz"), 4, (zend_long) 0, SL("hello"), &b); ZEPHIR_INIT_VAR(&_0); ZVAL_LONG(&_0, 100); - zephir_array_update_multi(&a, &_0, SL("lsza"), 5, 0, SL("hello"), &b); - zephir_array_update_multi(&a, &__$false, SL("lsza"), 5, 0, SL("hello"), &b); + zephir_array_update_multi(&a, &_0, SL("lsza"), 5, (zend_long) 0, SL("hello"), &b); + zephir_array_update_multi(&a, &__$false, SL("lsza"), 5, (zend_long) 0, SL("hello"), &b); ZEPHIR_INIT_VAR(&_1); ZVAL_DOUBLE(&_1, 1.2); - zephir_array_update_multi(&a, &_1, SL("lsza"), 5, 0, SL("hello"), &b); + zephir_array_update_multi(&a, &_1, SL("lsza"), 5, (zend_long) 0, SL("hello"), &b); ZEPHIR_INIT_VAR(&_2); zephir_create_array(&_2, 3, 0); ZEPHIR_INIT_VAR(&_3); @@ -1611,7 +1611,7 @@ PHP_METHOD(Stub_NativeArray, testMultipleArrayAppend3) ZEPHIR_INIT_NVAR(&_3); ZVAL_LONG(&_3, 3); zephir_array_fast_append(&_2, &_3); - zephir_array_update_multi(&a, &_2, SL("lsza"), 5, 0, SL("hello"), &b); + zephir_array_update_multi(&a, &_2, SL("lsza"), 5, (zend_long) 0, SL("hello"), &b); RETURN_CCTOR(&a); } @@ -1711,7 +1711,7 @@ PHP_METHOD(Stub_NativeArray, issue743a) zephir_get_arrval(¤t743a, current743a_param); ZEPHIR_INIT_VAR(&_0); ZVAL_STRING(&_0, "ok"); - zephir_array_update_multi(¤t743a, &_0, SL("ls"), 3, 42, SL("str")); + zephir_array_update_multi(¤t743a, &_0, SL("ls"), 3, (zend_long) 42, SL("str")); RETURN_CTOR(¤t743a); } @@ -1732,7 +1732,7 @@ PHP_METHOD(Stub_NativeArray, issue743b) zephir_get_arrval(¤t, current_param); ZEPHIR_INIT_VAR(&_0); ZVAL_STRING(&_0, "ok"); - zephir_array_update_multi(¤t, &_0, SL("sl"), 3, SL("str"), 42); + zephir_array_update_multi(¤t, &_0, SL("sl"), 3, SL("str"), (zend_long) 42); RETURN_CTOR(¤t); } diff --git a/ext/stub/quantum.zep.c b/ext/stub/quantum.zep.c index dd5c765d3..98b76034f 100644 --- a/ext/stub/quantum.zep.c +++ b/ext/stub/quantum.zep.c @@ -105,7 +105,7 @@ PHP_METHOD(Stub_Quantum, harmos) x = zephir_get_doubleval(x_param); dx = (0.02); k0 = ((3.0 * 3.14159265358979323846)); - dt = (zephir_safe_div_double_long((dx * dx), 4.0)); + dt = (zephir_safe_div_double_double((dx * dx), 4.0)); ZEPHIR_INIT_VAR(¶mater); zephir_create_array(¶mater, 4, 0); ZEPHIR_INIT_VAR(&_0); @@ -245,7 +245,7 @@ PHP_METHOD(Stub_Quantum, harmos) mul_function(&_29$$6, &_27$$6, &_22$$6); ZEPHIR_INIT_NVAR(&_28$$6); zephir_add_function(&_28$$6, &_25$$6, &_29$$6); - zephir_array_update_multi(&psr, &_28$$6, SL("ll"), 2, i, 1); + zephir_array_update_multi(&psr, &_28$$6, SL("ll"), 2, (zend_long) i, (zend_long) 1); zephir_array_fetch_long(&_22$$6, &psr, i, PH_NOISY | PH_READONLY, "stub/quantum.zep", 53); zephir_array_fetch_long(&_26$$6, &_22$$6, 0, PH_NOISY | PH_READONLY, "stub/quantum.zep", 53); zephir_array_fetch_long(&_30$$6, &psr, i, PH_NOISY | PH_READONLY, "stub/quantum.zep", 53); @@ -279,10 +279,10 @@ PHP_METHOD(Stub_Quantum, harmos) j = _35$$5; ZEPHIR_INIT_NVAR(&_37$$7); ZVAL_LONG(&_37$$7, 0); - zephir_array_update_multi(&psr, &_37$$7, SL("ll"), 2, 0, 1); + zephir_array_update_multi(&psr, &_37$$7, SL("ll"), 2, (zend_long) 0, (zend_long) 1); ZEPHIR_INIT_NVAR(&_37$$7); ZVAL_LONG(&_37$$7, 0); - zephir_array_update_multi(&psr, &_37$$7, SL("ll"), 2, 751, 1); + zephir_array_update_multi(&psr, &_37$$7, SL("ll"), 2, (zend_long) 751, (zend_long) 1); zephir_array_fetch_long(&_38$$7, &psi, j, PH_NOISY | PH_READONLY, "stub/quantum.zep", 58); zephir_array_fetch_long(&_39$$7, &_38$$7, 0, PH_NOISY | PH_READONLY, "stub/quantum.zep", 58); zephir_array_fetch_long(&_40$$7, ¶mater, 2, PH_NOISY | PH_READONLY, "stub/quantum.zep", 58); @@ -320,7 +320,7 @@ PHP_METHOD(Stub_Quantum, harmos) mul_function(&_49$$7, &_47$$7, &_42$$7); ZEPHIR_INIT_NVAR(&_48$$7); zephir_sub_function(&_48$$7, &_45$$7, &_49$$7); - zephir_array_update_multi(&psi, &_48$$7, SL("ll"), 2, j, 1); + zephir_array_update_multi(&psi, &_48$$7, SL("ll"), 2, (zend_long) j, (zend_long) 1); } } _50$$5 = n == 0; @@ -354,10 +354,10 @@ PHP_METHOD(Stub_Quantum, harmos) } zephir_array_fetch_long(&_57$$10, &psi, j, PH_NOISY | PH_READONLY, "stub/quantum.zep", 76); zephir_array_fetch_long(&_58$$10, &_57$$10, 1, PH_NOISY | PH_READONLY, "stub/quantum.zep", 76); - zephir_array_update_multi(&psi, &_58$$10, SL("ll"), 2, j, 0); + zephir_array_update_multi(&psi, &_58$$10, SL("ll"), 2, (zend_long) j, (zend_long) 0); zephir_array_fetch_long(&_58$$10, &psr, j, PH_NOISY | PH_READONLY, "stub/quantum.zep", 77); zephir_array_fetch_long(&_59$$10, &_58$$10, 1, PH_NOISY | PH_READONLY, "stub/quantum.zep", 77); - zephir_array_update_multi(&psr, &_59$$10, SL("ll"), 2, j, 0); + zephir_array_update_multi(&psr, &_59$$10, SL("ll"), 2, (zend_long) j, (zend_long) 0); j++; } } diff --git a/ext/stub/router/route.zep.c b/ext/stub/router/route.zep.c index b9551d919..3512471c1 100644 --- a/ext/stub/router/route.zep.c +++ b/ext/stub/router/route.zep.c @@ -223,7 +223,7 @@ PHP_METHOD(Stub_Router_Route, via) */ PHP_METHOD(Stub_Router_Route, extractNamedParams) { - long _0, _5$$11, _26$$19; + zend_long _0, _5$$11, _26$$19; zval route, item, variable, regexp, _4$$11, _21$$16, _24$$16, _29$$29; zend_bool notValid, _6$$12, _7$$12, _8$$12, _9$$12, _10$$12, _11$$12, _12$$12, _13$$12, _14$$12, _15$$12, _16$$12, _17$$12, _25$$18; zend_long tmp = 0, cursor = 0, cursorVar = 0, marker = 0, bracketCount, parenthesesCount, foundPattern, intermediate, numberMatches; diff --git a/ext/stub/scall.zep.c b/ext/stub/scall.zep.c index 4019f4cb4..44664fc28 100644 --- a/ext/stub/scall.zep.c +++ b/ext/stub/scall.zep.c @@ -333,7 +333,7 @@ PHP_METHOD(Stub_Scall, testCall15) PHP_METHOD(Stub_Scall, testMethod16) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(a) @@ -350,7 +350,7 @@ PHP_METHOD(Stub_Scall, testCall17) zend_long ZEPHIR_LAST_CALL_STATUS, _1; zephir_fcall_cache_entry *_4 = NULL; zval *k_param = NULL, *p, p_sub, _3$$3; - long k, i = 0, j, _2; + zend_long k, i = 0, j, _2; ZVAL_UNDEF(&p_sub); ZVAL_UNDEF(&_3$$3); @@ -391,7 +391,7 @@ PHP_METHOD(Stub_Scall, testCall18) zend_long ZEPHIR_LAST_CALL_STATUS, _1; zephir_fcall_cache_entry *_4 = NULL; zval *k_param = NULL, *p, p_sub, _3$$3; - long k, i = 0, j, _2; + zend_long k, i = 0, j, _2; ZVAL_UNDEF(&p_sub); ZVAL_UNDEF(&_3$$3); diff --git a/ext/stub/scallexternal.zep.c b/ext/stub/scallexternal.zep.c index 323a281bc..e4ae670ef 100644 --- a/ext/stub/scallexternal.zep.c +++ b/ext/stub/scallexternal.zep.c @@ -64,7 +64,7 @@ PHP_METHOD(Stub_ScallExternal, testCall2) PHP_METHOD(Stub_ScallExternal, testMethod3) { zval *a_param = NULL, *b_param = NULL; - long a, b; + zend_long a, b; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_LONG(a) diff --git a/ext/stub/unspacedminus.zep.c b/ext/stub/unspacedminus.zep.c index faeaa807b..62b03d8b7 100644 --- a/ext/stub/unspacedminus.zep.c +++ b/ext/stub/unspacedminus.zep.c @@ -160,7 +160,7 @@ PHP_METHOD(Stub_UnspacedMinus, gluedMod) Z_PARAM_LONG(a) ZEND_PARSE_PARAMETERS_END(); zephir_fetch_params_without_memory_grow(1, 0, &a_param); - RETURN_LONG((int) (zephir_safe_mod_long_long(a, 5))); + RETURN_LONG((zephir_safe_mod_long_long(a, 5))); } PHP_METHOD(Stub_UnspacedMinus, mulByNegative) diff --git a/kernel/array.c b/kernel/array.c index 0117b3464..c227eb77e 100644 --- a/kernel/array.c +++ b/kernel/array.c @@ -222,7 +222,7 @@ int zephir_array_isset_string_fetch(zval *fetched, const zval *arr, char *index, return 0; } -int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long index, int readonly) +int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, zend_long index, int readonly) { zval *zv; @@ -245,7 +245,7 @@ int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long return 0; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), index)) != NULL) { + if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index)) != NULL) { zephir_ensure_array(zv); if (!readonly) { @@ -256,8 +256,8 @@ int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long return 1; } } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - if (zephir_string_offset_isset(arr, (zend_long) index)) { - zephir_string_offset_read(fetched, (zval *) arr, (zend_long) index, 0); + if (zephir_string_offset_isset(arr, index)) { + zephir_string_offset_read(fetched, (zval *) arr, index, 0); return 1; } @@ -347,7 +347,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index return 0; } -int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index) +int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, zend_long index) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev((zval *)arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -361,9 +361,9 @@ int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index return 0; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - return zend_hash_index_exists(Z_ARRVAL_P(arr), index); + return zend_hash_index_exists(Z_ARRVAL_P(arr), (zend_ulong) index); } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - return zephir_string_offset_isset(arr, (zend_long) index); + return zephir_string_offset_isset(arr, index); } return 0; @@ -458,7 +458,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_value_string(const zval *arr, const char return Z_TYPE_P(entry) != IS_NULL; } -int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long index) +int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, zend_long index) { zval *entry; @@ -475,7 +475,7 @@ int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long return 0; } - entry = zend_hash_index_find(Z_ARRVAL_P(arr), index); + entry = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index); if (entry == NULL) { return 0; } @@ -516,7 +516,7 @@ int zephir_isempty_dim(zval *container, zval *offset) return zephir_isempty_dim_fetched(zephir_array_isset_fetch(&fetched, container, offset, 0), &fetched); } -int zephir_isempty_dim_long(zval *container, unsigned long offset) +int zephir_isempty_dim_long(zval *container, zend_long offset) { zval fetched; @@ -617,7 +617,7 @@ int ZEPHIR_FASTCALL zephir_array_unset_string(zval *arr, const char *index, uint return zend_hash_str_del(Z_ARRVAL_P(arr), index, index_length); } -int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int flags) +int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, zend_long index, int flags) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev(arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -642,7 +642,7 @@ int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int SEPARATE_ARRAY(arr); } - return zend_hash_index_del(Z_ARRVAL_P(arr), index); + return zend_hash_index_del(Z_ARRVAL_P(arr), (zend_ulong) index); } int zephir_array_append(zval *arr, zval *value, int flags ZEPHIR_DEBUG_PARAMS) @@ -738,7 +738,7 @@ int zephir_array_fetch(zval *return_value, zval *arr, zval *index, int flags ZEP if ((flags & PH_NOISY) == PH_NOISY) { if (sidx == NULL) { - zend_error(E_NOTICE, "Undefined index: %ld in %s on line %d", uidx, file, line); + zend_error(E_NOTICE, "Undefined index: " ZEND_LONG_FMT " in %s on line %d", (zend_long) uidx, file, line); } else { zend_error(E_NOTICE, "Undefined index: %s in %s on line %d", sidx, file, line); } @@ -809,7 +809,7 @@ int zephir_array_fetch_string(zval *return_value, zval *arr, const char *index, return FAILURE; } -int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, int flags ZEPHIR_DEBUG_PARAMS) +int zephir_array_fetch_long(zval *return_value, zval *arr, zend_long index, int flags ZEPHIR_DEBUG_PARAMS) { zval *zv; @@ -827,7 +827,7 @@ int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, return FAILURE; } else if (EXPECTED(Z_TYPE_P(arr) == IS_ARRAY)) { - if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), index)) != NULL) { + if ((zv = zend_hash_index_find(Z_ARRVAL_P(arr), (zend_ulong) index)) != NULL) { if ((flags & PH_READONLY) == PH_READONLY) { ZVAL_COPY_VALUE(return_value, zv); @@ -837,13 +837,12 @@ int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, return SUCCESS; } if ((flags & PH_NOISY) == PH_NOISY) { - zend_error(E_NOTICE, "Undefined index: %lu", index); + zend_error(E_NOTICE, "Undefined index: " ZEND_LONG_FMT, index); } } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { /* The compiler cannot prove a `var` holds a string, so the string - * offset is dispatched here. `index` is `unsigned long` for historical - * reasons: a negative offset arrives wrapped and the cast restores it. */ - zephir_string_offset_read(return_value, arr, (zend_long) index, flags); + * offset is dispatched here. */ + zephir_string_offset_read(return_value, arr, index, flags); return SUCCESS; } else { @@ -998,7 +997,7 @@ int zephir_array_update_string(zval *arr, const char *index, uint32_t index_leng return zend_hash_str_update(Z_ARRVAL_P(arr), index, index_length, value) ? SUCCESS : FAILURE; } -int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS) +int zephir_array_update_long(zval *arr, zend_long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS) { if (UNEXPECTED(Z_TYPE_P(arr) == IS_OBJECT && zephir_instance_of_ev(arr, (const zend_class_entry *)zend_ce_arrayaccess))) { zend_long ZEPHIR_LAST_CALL_STATUS; @@ -1011,7 +1010,7 @@ int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int fl return FAILURE; } else if (UNEXPECTED(Z_TYPE_P(arr) == IS_STRING)) { - zephir_string_offset_write(arr, (zend_long) index, value); + zephir_string_offset_write(arr, index, value); return EG(exception) ? FAILURE : SUCCESS; } else if (Z_TYPE_P(arr) != IS_ARRAY) { @@ -1032,7 +1031,7 @@ int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int fl SEPARATE_ARRAY(arr); } - return zend_hash_index_update(Z_ARRVAL_P(arr), index, value) ? SUCCESS : FAILURE; + return zend_hash_index_update(Z_ARRVAL_P(arr), (zend_ulong) index, value) ? SUCCESS : FAILURE; } void zephir_array_keys(zval *return_value, zval *input) @@ -1104,7 +1103,11 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int zval *item; zval pzv; zend_array *p; - int i, l, ll, re_update, must_continue, wrap_tmp; + int i, re_update, must_continue, wrap_tmp; + zend_long ll; + /* SL() yields sizeof(...) - 1, a size_t, so that is what the variadic + * slot holds. Reading it back as `int` was reading half of it. */ + size_t l; ZVAL_UNDEF(&pzv); @@ -1128,7 +1131,7 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int case 's': s = va_arg(ap, char*); - l = va_arg(ap, int); + l = va_arg(ap, size_t); /* * Issue #1884: the final offset overwrites its slot regardless of @@ -1173,7 +1176,7 @@ void zephir_array_update_multi_ex(zval *arr, zval *value, const char *types, int break; case 'l': - ll = va_arg(ap, long); + ll = va_arg(ap, zend_long); /* Issue #1884: final offset always overwrites -> store directly. */ if (i == (types_length - 1)) { diff --git a/kernel/array.h b/kernel/array.h index ada7ba3c1..0a78747bc 100644 --- a/kernel/array.h +++ b/kernel/array.h @@ -24,14 +24,24 @@ void ZEPHIR_FASTCALL zephir_create_array(zval *return_value, uint32_t size, int */ void ZEPHIR_FASTCALL zephir_ensure_array(zval *probable_array); +/* + * The `*_long` helpers take a signed `zend_long`, the same width PHP uses for + * an array key or a string offset. They used to take `unsigned long`, which is + * 32-bit on Windows (LLP64): a negative or large offset was truncated there + * before the callee ever saw it, so `s[-1]` read index 4294967295 and a write + * tried to grow the string to 4 GB. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + /** Combined isset/fetch */ int zephir_array_isset_fetch(zval *fetched, const zval *arr, zval *index, int readonly); int zephir_array_isset_string_fetch(zval *fetched, const zval *arr, char *index, uint32_t index_length, int readonly); -int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, unsigned long index, int readonly); +int zephir_array_isset_long_fetch(zval *fetched, const zval *arr, zend_long index, int readonly); /** Check for index existence */ int ZEPHIR_FASTCALL zephir_array_isset(const zval *arr, zval *index); -int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, unsigned long index); +int ZEPHIR_FASTCALL zephir_array_isset_long(const zval *arr, zend_long index); int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index, uint32_t index_length); /** @@ -41,23 +51,23 @@ int ZEPHIR_FASTCALL zephir_array_isset_string(const zval *arr, const char *index * semantics (e.g. array_key_exists). */ int ZEPHIR_FASTCALL zephir_array_isset_value(const zval *arr, zval *index); -int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, unsigned long index); +int ZEPHIR_FASTCALL zephir_array_isset_value_long(const zval *arr, zend_long index); int ZEPHIR_FASTCALL zephir_array_isset_value_string(const zval *arr, const char *index, uint32_t index_length); -/** Unset existing indexes */ /** PHP's silent `empty($container[$offset])` handler */ int zephir_isempty_dim(zval *container, zval *offset); -int zephir_isempty_dim_long(zval *container, unsigned long offset); +int zephir_isempty_dim_long(zval *container, zend_long offset); int zephir_isempty_dim_string(zval *container, char *offset, uint32_t offset_length); +/** Unset existing indexes */ int ZEPHIR_FASTCALL zephir_array_unset(zval *arr, zval *index, int flags); -int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, unsigned long index, int flags); +int ZEPHIR_FASTCALL zephir_array_unset_long(zval *arr, zend_long index, int flags); int ZEPHIR_FASTCALL zephir_array_unset_string(zval *arr, const char *index, uint32_t index_length, int flags); /** Fetch items from arrays */ int zephir_array_fetch(zval *return_value, zval *arr, zval *index, int flags ZEPHIR_DEBUG_PARAMS); int zephir_array_fetch_string(zval *return_value, zval *arr, const char *index, uint32_t index_length, int flags ZEPHIR_DEBUG_PARAMS); -int zephir_array_fetch_long(zval *return_value, zval *arr, unsigned long index, int flags ZEPHIR_DEBUG_PARAMS); +int zephir_array_fetch_long(zval *return_value, zval *arr, zend_long index, int flags ZEPHIR_DEBUG_PARAMS); /** Append elements to arrays */ int zephir_array_append(zval *arr, zval *value, int separate ZEPHIR_DEBUG_PARAMS); @@ -66,7 +76,7 @@ void zephir_merge_append(zval *left, zval *values); /** Modify array */ int zephir_array_update_zval(zval *arr, zval *index, zval *value, int flags); int zephir_array_update_string(zval *arr, const char *index, uint32_t index_length, zval *value, int flags); -int zephir_array_update_long(zval *arr, unsigned long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS); +int zephir_array_update_long(zval *arr, zend_long index, zval *value, int flags ZEPHIR_DEBUG_PARAMS); void zephir_array_keys(zval *return_value, zval *arr); int zephir_array_key_exists(zval *arr, zval *key); diff --git a/kernel/file.c b/kernel/file.c index 16d3ef516..5f79989ee 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -191,7 +191,7 @@ void zephir_file_get_contents(zval *return_value, zval *filename) { zend_string *contents; php_stream *stream; - long maxlen = PHP_STREAM_COPY_ALL; + size_t maxlen = PHP_STREAM_COPY_ALL; zval *zcontext = NULL; php_stream_context *context = NULL; @@ -353,7 +353,7 @@ void zephir_prepare_virtual_path(zval *return_value, zval *path, zval *virtual_s */ void zephir_unique_path_key(zval *return_value, zval *path) { - unsigned long h; + zend_ulong h; char *strKey; if (Z_TYPE_P(path) != IS_STRING) { @@ -363,7 +363,7 @@ void zephir_unique_path_key(zval *return_value, zval *path) h = zend_hash_func(Z_STRVAL_P(path), Z_STRLEN_P(path) + 1); strKey = emalloc(24); - sprintf(strKey, "v%lu", h); + snprintf(strKey, 24, "v" ZEND_ULONG_FMT, h); RETVAL_STRING(strKey); efree(strKey); diff --git a/kernel/main.h b/kernel/main.h index ca4c870c6..d53197bbf 100644 --- a/kernel/main.h +++ b/kernel/main.h @@ -48,6 +48,20 @@ extern zend_string* i_self; #define ZEND_ACC_READONLY 0 #endif +/* The float-to-int coercion PHP applies to a `%` operand. PHP 8.1 started + * deprecating a conversion that loses precision ("Deprecate implicit + * non-integer-compatible float to int conversions"), and carries that + * diagnostic in zend_dval_to_lval_safe(), which does not exist on 8.0. Routing + * through this shim keeps the kernel's `%` byte-identical to the engine's on + * every supported version: silent on 8.0, deprecating from 8.1. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 */ +#if PHP_VERSION_ID >= 80100 + #define ZEPHIR_DVAL_TO_LVAL(d) zend_dval_to_lval_safe(d) +#else + #define ZEPHIR_DVAL_TO_LVAL(d) zend_dval_to_lval(d) +#endif + #define SL(str) ZEND_STRL(str) #define SS(str) ZEND_STRS(str) #define ISL(str) (zephir_interned_##str), (sizeof(#str)-1) diff --git a/kernel/math.c b/kernel/math.c index d41d4c447..aed6a6325 100644 --- a/kernel/math.c +++ b/kernel/math.c @@ -151,7 +151,7 @@ extern double _php_math_round(double value, int places, int mode); void zephir_round(zval *return_value, zval *op1, zval *op2, zval *op3) { int places = 0; - long mode = PHP_ROUND_HALF_UP; + int mode = PHP_ROUND_HALF_UP; double return_val; convert_scalar_to_number_ex(op1); diff --git a/kernel/object.c b/kernel/object.c index 0026bc948..5a0fa0611 100644 --- a/kernel/object.c +++ b/kernel/object.c @@ -1045,13 +1045,14 @@ int zephir_update_property_array_multi(zval *object, const char *property, uint3 va_start(ap, types_count); switch (types[0]) { case 's': { - char *str = va_arg(ap, char*); - int len = va_arg(ap, int); + char *str = va_arg(ap, char*); + /* SL() pushes a size_t; see kernel/array.c. */ + size_t len = va_arg(ap, size_t); ZVAL_STRINGL(&offset, str, len); break; } case 'l': - ZVAL_LONG(&offset, va_arg(ap, long)); + ZVAL_LONG(&offset, va_arg(ap, zend_long)); break; case 'z': ZVAL_COPY(&offset, va_arg(ap, zval*)); diff --git a/kernel/operators.c b/kernel/operators.c index 5bee608fd..bb36f338f 100644 --- a/kernel/operators.c +++ b/kernel/operators.c @@ -23,6 +23,7 @@ #include "kernel/string.h" #include "kernel/operators.h" +#include "Zend/zend_exceptions.h" #include "Zend/zend_operators.h" /** @@ -210,7 +211,7 @@ void zephir_concat_self_long(zval *left, const zend_long right) /** * Natural compare with long operandus on right */ -int zephir_compare_strict_long(zval *op1, long op2) +int zephir_compare_strict_long(zval *op1, zend_long op2) { switch (Z_TYPE_P(op1)) { case IS_LONG: @@ -331,7 +332,7 @@ void zephir_convert_to_object(zval *op) /** * Returns the long value of a zval */ -long zephir_get_intval_ex(const zval *op) +zend_long zephir_get_intval_ex(const zval *op) { int type; double double_value = 0; @@ -358,7 +359,7 @@ long zephir_get_intval_ex(const zval *op) return 0; case IS_DOUBLE: - return (long) Z_DVAL_P(op); + return zend_dval_to_lval(Z_DVAL_P(op)); case IS_STRING: { ASSUME(Z_STRVAL_P(op) != NULL); @@ -369,7 +370,7 @@ long zephir_get_intval_ex(const zval *op) return long_value; case IS_DOUBLE: - return (long) double_value; + return zend_dval_to_lval(double_value); } } } @@ -377,7 +378,7 @@ long zephir_get_intval_ex(const zval *op) return 0; } -long zephir_get_charval_ex(const zval *op) +zend_long zephir_get_charval_ex(const zval *op) { switch (Z_TYPE_P(op)) { case IS_ARRAY: @@ -396,7 +397,7 @@ long zephir_get_charval_ex(const zval *op) return 0; case IS_DOUBLE: - return (long) Z_DVAL_P(op); + return zend_dval_to_lval(Z_DVAL_P(op)); case IS_STRING: { if (Z_STRLEN_P(op) > 0) { @@ -575,7 +576,7 @@ int zephir_less_equal(zval *op1, zval *op2) /** * Check if a zval is less than a long value */ -int zephir_less_long(zval *op1, long op2) +int zephir_less_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -593,7 +594,7 @@ int zephir_less_double(zval *op1, double op2) return Z_TYPE(result) == IS_TRUE; } -int zephir_less_equal_long(zval *op1, long op2) +int zephir_less_equal_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -605,7 +606,7 @@ int zephir_less_equal_long(zval *op1, long op2) /** * Check if a zval is greater than a long value */ -int zephir_greater_long(zval *op1, long op2) +int zephir_greater_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -636,7 +637,7 @@ int zephir_greater_equal(zval *op1, zval *op2) /** * Check for greater/equal */ -int zephir_greater_equal_long(zval *op1, long op2) +int zephir_greater_equal_long(zval *op1, zend_long op2) { zval result, op2_zval; ZVAL_LONG(&op2_zval, op2); @@ -644,27 +645,91 @@ int zephir_greater_equal_long(zval *op1, long op2) return Z_TYPE(result) == IS_FALSE; } +/** + * A zero divisor is a DivisionByZeroError in PHP 8, thrown by + * div_function_base()/mod_function() in Zend/zend_operators.c. + * + * These helpers return a value and have no way to abort their caller, so the + * rest of the generated method body runs with the exception pending and the + * engine discards the return value on the way out. That is the convention the + * kernel already uses for the concat overflow guards above and the + * string-offset guards in kernel/array.c. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ +static double zephir_throw_division_by_zero(void) +{ + zend_throw_exception(zend_ce_division_by_zero_error, "Division by zero", 0); + return 0; +} + +static zend_long zephir_throw_modulo_by_zero(void) +{ + zend_throw_exception(zend_ce_division_by_zero_error, "Modulo by zero", 0); + return 0; +} + +/** + * The operand coercion PHP's `%` performs, in the order it performs it: the + * float-to-int deprecation of an operand fires before the zero divisor is + * inspected (convert_op1_op2_long, then the op2_lval == 0 test). + */ +static zend_long zephir_mod_operand(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_DOUBLE: + return ZEPHIR_DVAL_TO_LVAL(Z_DVAL_P(op)); + + case IS_ARRAY: + case IS_OBJECT: + case IS_RESOURCE: + /* PHP 8 throws a TypeError here instead. See #2676. */ + zend_error(E_WARNING, "Unsupported operand types"); + break; + } + + return zephir_get_intval(op); +} + +/** + * The operand coercion `/` performs. Unlike `%` the result is a double, so a + * non-integral operand is kept as one. + */ +static double zephir_div_operand(zval *op) +{ + switch (Z_TYPE_P(op)) { + case IS_ARRAY: + case IS_OBJECT: + case IS_RESOURCE: + /* PHP 8 throws a TypeError here instead. See #2676. */ + zend_error(E_WARNING, "Unsupported operand types"); + break; + } + + return (double) zephir_get_numberval(op); +} + /** * Do safe divisions between two longs */ -double zephir_safe_div_long_long(long op1, long op2) +double zephir_safe_div_long_long(zend_long op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return (double) op1 / (double) op2; } /** * Do safe divisions between two long/double */ -double zephir_safe_div_long_double(long op1, double op2) +double zephir_safe_div_long_double(zend_long op1, double op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return (double) op1 / op2; } @@ -673,29 +738,24 @@ double zephir_safe_div_long_double(long op1, double op2) */ double zephir_safe_div_double_zval(double op1, zval *op2) { - if (!zephir_get_numberval(op2)) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op2)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + double divisor = zephir_div_operand(op2); + + if (!divisor) { + return zephir_throw_division_by_zero(); } - return op1 / ((double) zephir_get_numberval(op2)); + + return op1 / divisor; } /** * Do safe divisions between two double/long */ -double zephir_safe_div_double_long(double op1, long op2) +double zephir_safe_div_double_long(double op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return op1 / (double) op2; } @@ -705,48 +765,38 @@ double zephir_safe_div_double_long(double op1, long op2) double zephir_safe_div_double_double(double op1, double op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } + return op1 / op2; } /** * Do safe divisions between two zval/long */ -double zephir_safe_div_zval_long(zval *op1, long op2) +double zephir_safe_div_zval_long(zval *op1, zend_long op2) { + double dividend = zephir_div_operand(op1); + if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; + return zephir_throw_division_by_zero(); } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; - } - return ((double) zephir_get_numberval(op1)) / (double) op2; + + return dividend / (double) op2; } /** * Do safe divisions between two long/zval */ -double zephir_safe_div_long_zval(long op1, zval *op2) +double zephir_safe_div_long_zval(zend_long op1, zval *op2) { - if (!zephir_get_numberval(op2)) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op2)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + double divisor = zephir_div_operand(op2); + + if (!divisor) { + return zephir_throw_division_by_zero(); } - return (double) op1 / ((double) zephir_get_numberval(op2)); + + return (double) op1 / divisor; } /** @@ -754,47 +804,105 @@ double zephir_safe_div_long_zval(long op1, zval *op2) */ double zephir_safe_div_zval_double(zval *op1, double op2) { + double dividend = zephir_div_operand(op1); + if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; + return zephir_throw_division_by_zero(); } - return ((double) zephir_get_numberval(op1)) / op2; + + return dividend / op2; } /** - * Do safe divisions between two longs + * Do safe modulo between two longs. + * + * Every other modulo helper funnels through here, so the two guards PHP's + * mod_function() applies live in one place: a zero divisor throws, and a `-1` + * divisor short-circuits to 0 because PHP_INT_MIN % -1 overflows and raises + * SIGFPE on x86. */ -long zephir_safe_mod_long_long(long op1, long op2) +zend_long zephir_safe_mod_long_long(zend_long op1, zend_long op2) { if (!op2) { - zend_error(E_WARNING, "Division by zero"); + return zephir_throw_modulo_by_zero(); + } + + if (op2 == -1) { return 0; } + return op1 % op2; } /** - * Do safe divisions between two zval/long + * Do safe modulo between two long/double */ -long zephir_safe_mod_zval_long(zval *op1, long op2) +zend_long zephir_safe_mod_long_double(zend_long op1, double op2) { - if (!op2) { - zend_error(E_WARNING, "Division by zero"); - return 0; - } - switch (Z_TYPE_P(op1)) { - case IS_ARRAY: - case IS_OBJECT: - case IS_RESOURCE: - zend_error(E_WARNING, "Unsupported operand types"); - break; - } - return ((long) zephir_get_numberval(op1)) % (long) op2; + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(op1, divisor); +} + +/** + * Do safe modulo between two double/long + */ +zend_long zephir_safe_mod_double_long(double op1, zend_long op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + + return zephir_safe_mod_long_long(dividend, op2); +} + +/** + * Do safe modulo between two doubles + */ +zend_long zephir_safe_mod_double_double(double op1, double op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(dividend, divisor); +} + +/** + * Do safe modulo between two zval/long + */ +zend_long zephir_safe_mod_zval_long(zval *op1, zend_long op2) +{ + zend_long dividend = zephir_mod_operand(op1); + + return zephir_safe_mod_long_long(dividend, op2); +} + +/** + * Do safe modulo between two zval/double + */ +zend_long zephir_safe_mod_zval_double(zval *op1, double op2) +{ + zend_long dividend = zephir_mod_operand(op1); + zend_long divisor = ZEPHIR_DVAL_TO_LVAL(op2); + + return zephir_safe_mod_long_long(dividend, divisor); +} + +/** + * Do safe modulo between two long/zval + */ +zend_long zephir_safe_mod_long_zval(zend_long op1, zval *op2) +{ + zend_long divisor = zephir_mod_operand(op2); + + return zephir_safe_mod_long_long(op1, divisor); +} + +/** + * Do safe modulo between two double/zval + */ +zend_long zephir_safe_mod_double_zval(double op1, zval *op2) +{ + zend_long dividend = ZEPHIR_DVAL_TO_LVAL(op1); + zend_long divisor = zephir_mod_operand(op2); + + return zephir_safe_mod_long_long(dividend, divisor); } diff --git a/kernel/operators.h b/kernel/operators.h index cef954027..d2dcd1561 100644 --- a/kernel/operators.h +++ b/kernel/operators.h @@ -68,6 +68,18 @@ #define ZEPHIR_STRING_OFFSET(op1, index) ((index >= 0 && index < Z_STRLEN_P(op1)) ? Z_STRVAL_P(op1)[index] : '\0') +/* + * A PHP integer is a `zend_long`, i.e. `int64_t` on every 64-bit target. A C + * `long` is 64-bit under LP64 (Linux, macOS) but 32-bit under LLP64 (Windows + * x64), so a prototype spelling `long` truncates every value the compiler + * routes through it -- and does so only on Windows, where no CI leg of this + * repo can see it. + * + * Keep `zend_long`/`zend_ulong` in every signature that carries a PHP integer. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + /* concatenation */ void zephir_concat_self(zval *left, zval *right); void zephir_concat_self_str(zval *left, const char *right, int right_length); @@ -76,7 +88,7 @@ void zephir_concat_self_char(zval *left, unsigned char right); /** Strict comparing */ int zephir_compare_strict_string(zval *op1, const char *op2, int op2_length); -int zephir_compare_strict_long(zval *op1, long op2); +int zephir_compare_strict_long(zval *op1, zend_long op2); /** Operator functions */ int zephir_add_function_ex(zval *result, zval *op1, zval *op2); @@ -92,14 +104,14 @@ int zephir_shift_right_function(zval *result, zval *op1, zval *op2); /** Strict comparing */ int zephir_compare_strict_string(zval *op1, const char *op2, int op2_length); -int zephir_compare_strict_long(zval *op1, long op2); +int zephir_compare_strict_long(zval *op1, zend_long op2); int zephir_compare_strict_double(zval *op1, double op2); int zephir_compare_strict_bool(zval *op1, zend_bool op2); void zephir_cast(zval *result, zval *var, uint32_t type); void zephir_convert_to_object(zval *op); -long zephir_get_intval_ex(const zval *op); -long zephir_get_charval_ex(const zval *op); +zend_long zephir_get_intval_ex(const zval *op); +zend_long zephir_get_charval_ex(const zval *op); double zephir_get_doubleval_ex(const zval *op); zend_bool zephir_get_boolval_ex(zval *op); @@ -109,36 +121,47 @@ int zephir_is_equal(zval *op1, zval *op2); int zephir_is_identical(zval *op1, zval *op2); int zephir_less(zval *op1, zval *op2); -int zephir_less_long(zval *op1, long op2); +int zephir_less_long(zval *op1, zend_long op2); int zephir_less_double(zval *op1, double op2); int zephir_greater(zval *op1, zval *op2); -int zephir_greater_long(zval *op1, long op2); +int zephir_greater_long(zval *op1, zend_long op2); int zephir_greater_double(zval *op1, double op2); int zephir_less_equal(zval *op1, zval *op2); -int zephir_less_equal_long(zval *op1, long op2); +int zephir_less_equal_long(zval *op1, zend_long op2); int zephir_greater_equal(zval *op1, zval *op2); -int zephir_greater_equal_long(zval *op1, long op2); +int zephir_greater_equal_long(zval *op1, zend_long op2); -double zephir_safe_div_long_long(long op1, long op2); -double zephir_safe_div_long_double(long op1, double op2); -double zephir_safe_div_double_long(double op1, long op2); +/* + * A zero divisor throws DivisionByZeroError, as PHP 8 does; the helper still + * returns 0 because it has no way to abort its caller, so the rest of the + * generated method body runs with the exception pending and the engine + * discards the return value on the way out. + */ +double zephir_safe_div_long_long(zend_long op1, zend_long op2); +double zephir_safe_div_long_double(zend_long op1, double op2); +double zephir_safe_div_double_long(double op1, zend_long op2); double zephir_safe_div_double_double(double op1, double op2); -double zephir_safe_div_zval_long(zval *op1, long op2); +double zephir_safe_div_zval_long(zval *op1, zend_long op2); double zephir_safe_div_zval_double(zval *op1, double op2); -double zephir_safe_div_long_zval(long op1, zval *op2); +double zephir_safe_div_long_zval(zend_long op1, zval *op2); double zephir_safe_div_double_zval(double op1, zval *op2); -long zephir_safe_mod_long_long(long op1, long op2); -long zephir_safe_mod_long_double(long op1, double op2); -long zephir_safe_mod_double_long(double op1, long op2); -long zephir_safe_mod_double_double(double op1, double op2); -long zephir_safe_mod_zval_long(zval *op1, long op2); -long zephir_safe_mod_zval_double(zval *op1, double op2); -long zephir_safe_mod_long_zval(long op1, zval *op2); -long zephir_safe_mod_double_zval(double op1, zval *op2); +/* + * PHP's `%` converts both operands to `zend_long` and yields a `zend_long`. + * A `-1` divisor short-circuits to 0: `PHP_INT_MIN % -1` overflows and raises + * SIGFPE on x86, which is why php-src special-cases it in mod_function(). + */ +zend_long zephir_safe_mod_long_long(zend_long op1, zend_long op2); +zend_long zephir_safe_mod_long_double(zend_long op1, double op2); +zend_long zephir_safe_mod_double_long(double op1, zend_long op2); +zend_long zephir_safe_mod_double_double(double op1, double op2); +zend_long zephir_safe_mod_zval_long(zval *op1, zend_long op2); +zend_long zephir_safe_mod_zval_double(zval *op1, double op2); +zend_long zephir_safe_mod_long_zval(zend_long op1, zval *op2); +zend_long zephir_safe_mod_double_zval(double op1, zval *op2); #define zephir_get_numberval(z) (Z_TYPE_P(z) == IS_LONG ? Z_LVAL_P(z) : zephir_get_doubleval(z)) #define zephir_get_intval(z) (Z_TYPE_P(z) == IS_LONG ? Z_LVAL_P(z) : zephir_get_intval_ex(z)) diff --git a/kernel/string.c b/kernel/string.c index b949cfa2d..bd350daab 100644 --- a/kernel/string.c +++ b/kernel/string.c @@ -282,7 +282,7 @@ int zephir_end_with_str(const zval *str, char *compared, unsigned int compared_l /** * Makes a substr like the PHP function. This function SUPPORT negative from and length */ -void zephir_substr(zval *return_value, zval *str, long f, long l, int flags) +void zephir_substr(zval *return_value, zval *str, zend_long f, zend_long l, int flags) { zval copy; int use_copy = 0; @@ -712,7 +712,7 @@ int zephir_memnstr_str(const zval *haystack, char *needle, unsigned int needle_l /** * Fast call to explode php function */ -void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, long limit) +void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, zend_long limit) { if (UNEXPECTED(Z_TYPE_P(str) != IS_STRING || Z_TYPE_P(delimiter) != IS_STRING)) { zend_error(E_WARNING, "Invalid arguments supplied for explode()"); @@ -726,7 +726,7 @@ void zephir_fast_explode(zval *return_value, zval *delimiter, zval *str, long li /** * Fast call to explode php function */ -void zephir_fast_explode_str(zval *return_value, const char *delim, int delim_length, zval *str, long limit) +void zephir_fast_explode_str(zval *return_value, const char *delim, int delim_length, zval *str, zend_long limit) { zend_string *delimiter; @@ -1044,7 +1044,7 @@ void zephir_fast_str_replace(zval *return_value_ptr, zval *search, zval *replace /** * Execute preg-match without function lookup in the PHP userland */ -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset) +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset) { int use_copy = 0; zval copy, tmp_matches; @@ -1104,7 +1104,7 @@ void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *mat #else -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset) +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset) { zval tmp_flags; zval tmp_offset; diff --git a/kernel/string.h b/kernel/string.h index 93e911ac0..36bff2f9c 100644 --- a/kernel/string.h +++ b/kernel/string.h @@ -31,8 +31,8 @@ void zephir_fast_strtolower(zval *return_value, zval *str); void zephir_fast_strtoupper(zval *return_value, zval *str); void zephir_fast_join(zval *result, zval *glue, zval *pieces); void zephir_fast_join_str(zval *result, char *glue, unsigned int glue_length, zval *pieces); -void zephir_fast_explode(zval *result, zval *delimiter, zval *str, long limit); -void zephir_fast_explode_str(zval *result, const char *delimiter, int delimiter_length, zval *str, long limit); +void zephir_fast_explode(zval *result, zval *delimiter, zval *str, zend_long limit); +void zephir_fast_explode_str(zval *result, const char *delimiter, int delimiter_length, zval *str, zend_long limit); void zephir_fast_strpos(zval *return_value, const zval *haystack, const zval *needle, unsigned int offset); void zephir_fast_strpos_str(zval *return_value, const zval *haystack, char *needle, unsigned int needle_length); void zephir_fast_trim(zval *return_value, zval *str, zval *charlist, int where); @@ -57,10 +57,10 @@ int zephir_json_encode(zval *return_value, zval *v, int opts); int zephir_json_decode(zval *return_value, zval *v, zend_bool assoc); /* Substr */ -void zephir_substr(zval *return_value, zval *str, long from, long length, int flags); +void zephir_substr(zval *return_value, zval *str, zend_long from, zend_long length, int flags); /** Preg-Match */ -void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, long flags, long offset); +void zephir_preg_match(zval *return_value, zval *regex, zval *subject, zval *matches, int global, zend_long flags, zend_long offset); /** Hash */ void zephir_md5(zval *return_value, zval *str); diff --git a/src/Backend/Backend.php b/src/Backend/Backend.php index dfa41b0a0..9fe6fe1e6 100644 --- a/src/Backend/Backend.php +++ b/src/Backend/Backend.php @@ -2053,12 +2053,20 @@ public function getTypeDefinition($type): array $code = 'unsigned char'; break; + /** + * All four integer types describe a PHP `int`, which the engine + * keeps in a `zend_long` (`int64_t`). A C `long` would instead + * follow the data model: 64-bit under LP64, 32-bit under LLP64 + * (Windows x64). + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ case 'long': - $code = 'long'; + $code = 'zend_long'; break; case 'ulong': - $code = 'unsigned long'; + $code = 'zend_ulong'; break; case 'bool': @@ -2836,6 +2844,27 @@ protected function returnHelper( * * @throws CompilerException */ + /** + * An `l` index reaches zephir_array_update_multi() and friends through a + * variadic slot, where the callee reads it back with `va_arg(ap, + * zend_long)`. An argument passed as a plain C `int` (every integer + * literal is one) therefore has its upper half read as whatever the ABI + * left there: on Windows that produced keys like 140733193388033 instead + * of 1. The cast makes the argument's type match the read on every ABI. + * + * A cast binds tighter than any binary operator, so this relies on the + * offset's code being an atom or already parenthesised. Every producer + * that reaches here satisfies that: integer literals and constants fold to + * a single token, and ArithmeticalBaseOperator wraps its result in + * parentheses. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + private function castMultiIndex(string $code): string + { + return '(zend_long) ' . $code; + } + private function resolveOffsetExprs(array $offsetExprs, CompilationContext $compilationContext): array { $keys = ''; @@ -2855,7 +2884,7 @@ private function resolveOffsetExprs(array $offsetExprs, CompilationContext $comp case 'long': case 'ulong': $keys .= 'l'; - $offsetItems[] = $offsetExpr->getCode(); + $offsetItems[] = $this->castMultiIndex($offsetExpr->getCode()); ++$numberParams; break; @@ -2876,8 +2905,15 @@ private function resolveOffsetExprs(array $offsetExprs, CompilationContext $comp case 'uint': case 'long': case 'ulong': - $keys .= 'l'; - $offsetItems[] = $this->getVariableCode($variableIndex); + $keys .= 'l'; + /** + * A native integer local is the value itself, not + * a zval, so getVariableCode() would hand the + * variadic slot a `&name` pointer for `uint` and + * `ulong` (it only bypasses `int` and `long`). + */ + $variableIndex->setUsed(true); + $offsetItems[] = $this->castMultiIndex($variableIndex->getName()); ++$numberParams; break; case 'string': diff --git a/src/Class/Method/Method.php b/src/Class/Method/Method.php index b8ce3996b..13bc83f4d 100644 --- a/src/Class/Method/Method.php +++ b/src/Class/Method/Method.php @@ -829,8 +829,8 @@ public function assignZvalValue(array $parameter, CompilationContext $compilatio case 'char': case 'uchar': - // zephir_get_charval() yields a long; the declared C type - // (char / unsigned char) narrows it to the byte. See #1629. + // zephir_get_charval() yields a zend_long; the declared C + // type (char / unsigned char) narrows it to the byte. See #1629. return "\t" . $parameter['name'] . ' = zephir_get_charval(' . $parameterCode . ');' . PHP_EOL; case 'bool': diff --git a/src/Code/Builder/Struct.php b/src/Code/Builder/Struct.php index 0b469ced2..f22666372 100644 --- a/src/Code/Builder/Struct.php +++ b/src/Code/Builder/Struct.php @@ -145,16 +145,19 @@ protected function convertToCType(string $type): string { // Mirrors Backend::getTypeDefinition(): `uint`, `ulong` and `uchar` are // Zephir type names, not C ones, and have to be spelled out. See #1629. + // `long`/`ulong` spell out to `zend_long`/`zend_ulong` rather than a C + // `long`, whose width follows the data model. See #2666. return match ($type) { - 'boolean', 'bool' => 'zend_bool', - 'hash' => 'HashTable* ', + 'boolean', 'bool' => 'zend_bool', + 'hash' => 'HashTable* ', // Use a plain C string pointer for struct globals to align with STD_PHP_INI_ENTRY expectations - 'string' => 'char *', - 'uint' => 'zend_ulong', - 'ulong' => 'unsigned long', - 'uchar' => 'unsigned char', - 'int', 'long', 'char', 'double' => $type, - default => throw new InvalidArgumentException( + 'string' => 'char *', + 'uint' => 'zend_ulong', + 'long' => 'zend_long', + 'ulong' => 'zend_ulong', + 'uchar' => 'unsigned char', + 'int', 'char', 'double' => $type, + default => throw new InvalidArgumentException( 'Unknown global type: ' . $type ), }; diff --git a/src/Operators/Arithmetical/DivOperator.php b/src/Operators/Arithmetical/DivOperator.php index e88824dd9..8e503c1ba 100644 --- a/src/Operators/Arithmetical/DivOperator.php +++ b/src/Operators/Arithmetical/DivOperator.php @@ -132,15 +132,14 @@ public function compile($expression, CompilationContext $compilationContext): Co case 'ulong': return new CompiledExpression( 'double', - 'zephir_safe_div_double_long(' . $left->getCode() . ', (double) (' . $right->getCode( - ) . '))', + 'zephir_safe_div_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); case 'double': return new CompiledExpression( 'double', - 'zephir_safe_div_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', + 'zephir_safe_div_double_double(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); @@ -428,13 +427,20 @@ public function compile($expression, CompilationContext $compilationContext): Co case 'uint': case 'long': case 'ulong': - case 'double': return new CompiledExpression( 'double', 'zephir_safe_div_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); + case 'double': + return new CompiledExpression( + 'double', + 'zephir_safe_div_double_double(' + . $left->getCode() . ', ' . $right->getCode() . ')', + $expression + ); + case 'bool': return new CompiledExpression( 'bool', @@ -457,14 +463,14 @@ public function compile($expression, CompilationContext $compilationContext): Co return new CompiledExpression( 'double', 'zephir_safe_div_double_long(' . $variableLeft->getName( - ) . ', (double) ' . $variableRight->getName() . ')', + ) . ', ' . $variableRight->getName() . ')', $expression ); case 'double': return new CompiledExpression( 'double', - 'zephir_safe_div_double_long(' . $variableLeft->getName( + 'zephir_safe_div_double_double(' . $variableLeft->getName( ) . ', ' . $variableRight->getName() . ')', $expression ); diff --git a/src/Operators/Arithmetical/ModOperator.php b/src/Operators/Arithmetical/ModOperator.php index 000dbdf01..fe3cf882f 100644 --- a/src/Operators/Arithmetical/ModOperator.php +++ b/src/Operators/Arithmetical/ModOperator.php @@ -20,6 +20,17 @@ /** * Generates an arithmetical operation according to the operands + * + * PHP's `%` converts both operands to `zend_long` and always yields an `int`, + * so every `zephir_safe_mod_*` result is typed `int` here. Typing it `double` + * -- as this did -- routed the result through a C double and lost every value + * above 2^53 on every platform. + * + * The `$bitOperator` branches below are still wrong for a bool operand: + * they emit a subtraction and type the result `bool`. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + * @see https://github.com/zephir-lang/zephir/issues/2677 */ class ModOperator extends ArithmeticalBaseOperator { @@ -48,14 +59,14 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); case Types::T_DOUBLE: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_double((double) ' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); @@ -79,19 +90,19 @@ public function compile($expression, CompilationContext $compilationContext) Types::T_LONG, Types::T_ULONG, Types::T_BOOL => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $left->getCode() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_DOUBLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_double(' . $left->getCode() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_VARIABLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_zval(' . $left->getCode() . ', ' @@ -125,22 +136,21 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', - 'zephir_safe_mod_double_long(' . $left->getCode() . ', (double) (' . $right->getCode( - ) . '))', + 'int', + 'zephir_safe_mod_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); case Types::T_DOUBLE: return new CompiledExpression( - 'double', - 'zephir_safe_mod_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', + 'int', + 'zephir_safe_mod_double_double(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); case Types::T_BOOL: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_long(' . $left->getCode() . ', ' . $right->getBooleanCode() . ')', $expression ); @@ -158,19 +168,19 @@ public function compile($expression, CompilationContext $compilationContext) Types::T_LONG, Types::T_ULONG, Types::T_BOOL => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_long(' . $left->getCode() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_DOUBLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_double(' . $left->getCode() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_VARIABLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_zval(' . $left->getCode() . ', ' @@ -219,14 +229,14 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); case Types::T_DOUBLE: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_double(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); @@ -244,19 +254,19 @@ public function compile($expression, CompilationContext $compilationContext) Types::T_LONG, Types::T_ULONG, Types::T_BOOL => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $variableLeft->getName() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_DOUBLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_double(' . $variableLeft->getName() . ', ' . $variableRight->getName() . ')', $expression ), Types::T_VARIABLE => new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_zval(' . $variableLeft->getName() . ', ' @@ -313,7 +323,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $variableLeft->getName() . ', ' @@ -324,7 +334,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_DOUBLE: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_double(' . $variableLeft->getName() . ', ' @@ -335,7 +345,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_BOOL: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_long(' . $variableLeft->getName() . ' ' @@ -349,7 +359,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_VARIABLE: $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_long_zval(' . $variableLeft->getName() . ', ' @@ -383,13 +393,20 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_UINT: case Types::T_LONG: case Types::T_ULONG: - case Types::T_DOUBLE: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_long(' . $left->getCode() . ', ' . $right->getCode() . ')', $expression ); + case Types::T_DOUBLE: + return new CompiledExpression( + 'int', + 'zephir_safe_mod_double_double(' + . $left->getCode() . ', ' . $right->getCode() . ')', + $expression + ); + case Types::T_BOOL: return new CompiledExpression( 'bool', @@ -410,16 +427,16 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_long(' . $variableLeft->getName( - ) . ', (double) ' . $variableRight->getName() . ')', + ) . ', ' . $variableRight->getName() . ')', $expression ); case Types::T_DOUBLE: return new CompiledExpression( - 'double', - 'zephir_safe_mod_double_long(' . $variableLeft->getName( + 'int', + 'zephir_safe_mod_double_double(' . $variableLeft->getName( ) . ', ' . $variableRight->getName() . ')', $expression ); @@ -435,7 +452,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_VARIABLE: $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_double_zval(' . $variableLeft->getName() . ', ' @@ -478,7 +495,7 @@ public function compile($expression, CompilationContext $compilationContext) $op2 = $right->getCode(); return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_zval_long(' . $op1 . ', ' . $op2 . ')', $expression ); @@ -488,7 +505,7 @@ public function compile($expression, CompilationContext $compilationContext) $op2 = $right->getCode(); return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_zval_double(' . $op1 . ', ' . $op2 . ')', $expression ); @@ -508,7 +525,7 @@ public function compile($expression, CompilationContext $compilationContext) case Types::T_LONG: case Types::T_ULONG: return new CompiledExpression( - 'double', + 'int', 'zephir_safe_mod_zval_long(' . $op1 . ', ' . $variableRight->getName() . ')', $expression diff --git a/stub/concat.zep b/stub/concat.zep index 067756f89..60d4abf9c 100644 --- a/stub/concat.zep +++ b/stub/concat.zep @@ -86,17 +86,17 @@ class Concat } /** - * `long` is a C `long`, which is 32-bit on Windows LLP64, so this stays a - * local holding a 32-bit-safe value. The 64-bit range is exercised through - * the `int` variant above, which is a `zend_long` on every platform. + * `long` is a `zend_long` on every platform since #2666, so this carries + * the full 64-bit range exactly like the `int` variant above. It used to + * be a C `long`, 32-bit under Windows LLP64, and this method held a + * 32-bit-safe local to keep CI green. * * @link https://github.com/zephir-lang/zephir/issues/2660 - * @see https://github.com/zephir-lang/zephir/issues/2666 + * @link https://github.com/zephir-lang/zephir/issues/2666 */ - public function testConcatStringWithLongVar() -> string + public function testConcatStringWithLongVar(long number) -> string { string retval = "n="; - long number = 2147483647; let retval .= number; return retval; } diff --git a/stub/issue2666.zep b/stub/issue2666.zep new file mode 100644 index 000000000..861812a12 --- /dev/null +++ b/stub/issue2666.zep @@ -0,0 +1,289 @@ +namespace Stub; + +/** + * @issue https://github.com/zephir-lang/zephir/issues/2666 + * + * Zephir's `long`/`ulong` mapped to a C `long`/`unsigned long`, which is + * 32-bit under LLP64 (Windows x64) and 64-bit under LP64 (Linux, macOS), + * while a PHP integer is always a 64-bit `zend_long`. Every method here moves + * a value that only fits in 64 bits across that boundary, and the test asserts + * the result against the same expression evaluated in plain PHP. + * + * The division and modulo methods cover the second half of the fix: the + * `zephir_safe_mod_*` family was declared and emitted but mostly undefined, + * and both families answered a zero divisor with a warning instead of PHP 8's + * DivisionByZeroError. + */ +class Issue2666 +{ + /** + * The reproduction from the issue: `Z_PARAM_LONG(number)` stores a + * `zend_long` through `&number`. + */ + public function longParamToString(long number) -> string + { + string retval = "n="; + let retval .= number; + return retval; + } + + public function ulongParamToString(ulong number) -> string + { + string retval = "n="; + let retval .= number; + return retval; + } + + public function intParamToString(int number) -> string + { + string retval = "n="; + let retval .= number; + return retval; + } + + public function longRoundTrip(long number) -> long + { + return number; + } + + public function ulongRoundTrip(ulong number) -> ulong + { + return number; + } + + /** + * Parameter -> local -> return, so the value passes through a declared + * local rather than staying in the parameter slot. + */ + public function longThroughLocal(long number) -> long + { + long copy; + let copy = number; + return copy; + } + + /** + * `zephir_get_intval()`, the funnel every cast shares. Its `_ex` helper + * returned a C `long`, so this truncated even for Zephir `int`. + */ + public function castToInt(var value) -> int + { + int result; + let result = (int) value; + return result; + } + + public function castToLong(var value) -> long + { + long result; + let result = (long) value; + return result; + } + + /** + * Arithmetic on two 64-bit locals: no kernel helper, but the operands and + * the result must all be 64-bit wide. + */ + public function longAdd(long a, long b) -> long + { + return a + b; + } + + /** Array key built from a `long`: `zephir_array_update_long()`. */ + public function longArrayKey(long key, var value) -> array + { + array result = []; + let result[key] = value; + return result; + } + + /** `zephir_array_fetch_long()` with a 64-bit key. */ + public function longArrayFetch(array! source, long key) + { + return source[key]; + } + + /** `zephir_array_isset_long()`. */ + public function longArrayIsset(array! source, long key) -> bool + { + return isset source[key]; + } + + /** `zephir_less_long()` / `zephir_greater_long()`: zval against a native. */ + public function varLessThanLong(var value, long limit) -> bool + { + return value < limit; + } + + public function varGreaterThanLong(var value, long limit) -> bool + { + return value > limit; + } + + /** `zephir_substr()` took `long from, long length`. */ + public function substrWithLongOffset(string! source, long from, long length) -> string + { + return substr(source, from, length); + } + + /** `zephir_fast_explode()` took a `long limit`. */ + public function explodeWithLongLimit(string! source, long limit) -> array + { + return explode(",", source, limit); + } + + /** + * The eight `zephir_safe_div_*` / `zephir_safe_mod_*` shapes the compiler + * can actually reach. A double *literal* on the right is the only way to + * reach the `*_double` variants: a `double` variable there is rejected by + * ModOperator/DivOperator. + */ + public function divLongLong(long a, long b) + { + return a / b; + } + + public function divLongDouble(long a) + { + return a / 2.5; + } + + public function divDoubleLong(double a, long b) + { + return a / b; + } + + public function divDoubleDouble(double a) + { + return a / 2.5; + } + + public function divVarLong(var a, long b) + { + return a / b; + } + + public function divVarDouble(var a) + { + return a / 2.5; + } + + public function divLongVar(long a, var b) + { + return a / b; + } + + public function divDoubleVar(double a, var b) + { + return a / b; + } + + public function modLongLong(long a, long b) + { + return a % b; + } + + public function modLongDouble(long a) + { + return a % 2.5; + } + + public function modDoubleLong(double a, long b) + { + return a % b; + } + + public function modDoubleDouble(double a) + { + return a % 2.5; + } + + public function modVarLong(var a, long b) + { + return a % b; + } + + public function modVarDouble(var a) + { + return a % 2.5; + } + + public function modLongVar(long a, var b) + { + return a % b; + } + + public function modDoubleVar(double a, var b) + { + return a % b; + } + + /** Literal double on the left: the only shape reaching `*_double_double`. */ + public function divLiteralDoubleByDouble(double b) + { + return 2.5 / b; + } + + public function modLiteralDoubleByDouble(double b) + { + return 2.5 % b; + } + + /** + * Multi-dimensional keys reach `zephir_array_update_multi()` through a + * variadic slot read back as `zend_long`. An integer literal is a C `int`, + * so the upper half of the slot was whatever the ABI left there: on Windows + * `a[0][1]` produced the key 140733193388033. `uint`/`ulong` were worse, + * passing `&i` and using the pointer as the key. + */ + public function multiLiteralKeys() -> array + { + array result = []; + let result[0][1] = "v"; + return result; + } + + public function multiIntKey(int key) -> array + { + array result = []; + let result[key][2] = "v"; + return result; + } + + public function multiUintKey(uint key) -> array + { + array result = []; + let result[key][2] = "v"; + return result; + } + + public function multiLongKey(long key) -> array + { + array result = []; + let result[key][2] = "v"; + return result; + } + + public function multiUlongKey(ulong key) -> array + { + array result = []; + let result[key][2] = "v"; + return result; + } + + /** Enough offsets to spill past the register arguments. */ + public function multiDeepKeys() -> array + { + array result = []; + let result[0][1][2][3][4][5][6][7][8] = "v"; + return result; + } + + /** Mixed string and integer offsets in one call. */ + public function multiMixedKeys(long key) -> array + { + array result = []; + let result["k"][key] = "v"; + return result; + } +} diff --git a/tests/Extension/AssertsPhpParity.php b/tests/Extension/AssertsPhpParity.php new file mode 100644 index 000000000..d91bbc4f7 --- /dev/null +++ b/tests/Extension/AssertsPhpParity.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Extension; + +use Throwable; + +/** + * PHP is the oracle: run the same operation twice, once through the compiled + * extension and once in plain PHP, and compare the whole transcript -- the + * diagnostics raised and the resulting value. + * + * Hard-coding the expected text instead would pin the assertion to one PHP + * version, and PHP moves: string-offset diagnostics changed in 8.1 and again + * in 8.5, implicit float-to-int conversion started deprecating in 8.1. + * Comparing two live evaluations survives all of it. + */ +trait AssertsPhpParity +{ + /** + * Runs both sides and asserts the diagnostics and the value agree. + */ + private function assertMatchesPhp(callable $extension, callable $php): void + { + $this->assertSame( + $this->transcript($php), + $this->transcript($extension), + 'The extension must behave exactly as PHP does' + ); + } + + private function transcript(callable $subject): string + { + $diagnostics = []; + + set_error_handler(static function (int $code, string $message) use (&$diagnostics): bool { + $diagnostics[] = $message; + + return true; + }); + + try { + $value = $subject(); + $result = is_string($value) + ? 'string(' . strlen($value) . ') ' . bin2hex($value) + : var_export($value, true); + } catch (Throwable $e) { + $result = get_class($e) . ': ' . $e->getMessage(); + } finally { + restore_error_handler(); + } + + return implode(' ; ', $diagnostics) . ' => ' . $result; + } +} diff --git a/tests/Extension/ConcatTest.php b/tests/Extension/ConcatTest.php index b667918c6..072f46e5e 100644 --- a/tests/Extension/ConcatTest.php +++ b/tests/Extension/ConcatTest.php @@ -87,17 +87,18 @@ public function testShouldConcatenateStringWithIntVariable(): void } /** - * Zephir's `long` is a C `long`, so it is 32-bit on Windows LLP64 while - * `int` is a 64-bit `zend_long` everywhere. The 64-bit range is asserted on - * the `int` variant above; this one only has to prove the `long` operand - * reaches the same helper. + * `long` is a `zend_long` on every platform since #2666, so it carries the + * same 64-bit range as `int`. Before that it was a C `long`, 32-bit under + * Windows LLP64, and PHP_INT_MAX came back as `n=-1` there. * * @issue https://github.com/zephir-lang/zephir/issues/2660 - * @see https://github.com/zephir-lang/zephir/issues/2666 + * @issue https://github.com/zephir-lang/zephir/issues/2666 */ public function testShouldConcatenateStringWithLongVariable(): void { - $this->assertSame('n=' . 2147483647, $this->test->testConcatStringWithLongVar()); + foreach ([0, 5, -5, 2147483647, PHP_INT_MAX, PHP_INT_MIN] as $number) { + $this->assertSame('n=' . $number, $this->test->testConcatStringWithLongVar($number)); + } } /** diff --git a/tests/Extension/Issue2666Test.php b/tests/Extension/Issue2666Test.php new file mode 100644 index 000000000..09d2db11e --- /dev/null +++ b/tests/Extension/Issue2666Test.php @@ -0,0 +1,475 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Extension; + +use PHPUnit\Framework\TestCase; +use Stub\Issue2666; + +/** + * A PHP integer is a `zend_long`, i.e. `int64_t`, on every 64-bit target. + * Zephir's `long`/`ulong` mapped to a C `long`/`unsigned long`, which is + * 32-bit under LLP64 (Windows x64), so the top half of every value was + * dropped there while Linux and macOS saw nothing wrong. + * + * These assertions pass on LP64 before and after the fix -- they exist to fail + * on the Windows CI legs, which is where the bug lives. The generated C is + * checked from any platform by Zephir\Test\BlackBox\Issue2666Test and + * Zephir\Test\KernelPortabilityTest. + * + * @issue https://github.com/zephir-lang/zephir/issues/2666 + */ +final class Issue2666Test extends TestCase +{ + use AssertsPhpParity; + + private Issue2666 $test; + + protected function setUp(): void + { + $this->test = new Issue2666(); + } + + /** + * Values that survive a 32-bit `long` and values that do not. + */ + public static function sixtyFourBitProvider(): array + { + return [ + 'zero' => [0], + 'one' => [1], + 'minus one' => [-1], + 'int32 max' => [2147483647], + 'int32 min' => [-2147483648], + 'above int32' => [4294967296], + 'below int32' => [-4294967297], + 'int64 max' => [PHP_INT_MAX], + 'int64 min' => [PHP_INT_MIN], + ]; + } + + public static function unsignedProvider(): array + { + return [ + 'zero' => [0], + 'one' => [1], + 'uint32 max' => [4294967295], + 'above int32' => [4294967296], + 'int64 max' => [PHP_INT_MAX], + ]; + } + + /** + * @dataProvider sixtyFourBitProvider + */ + public function testLongParameterKeepsAllSixtyFourBits(int $number): void + { + $this->assertMatchesPhp( + fn () => $this->test->longParamToString($number), + fn () => 'n=' . $number + ); + } + + /** + * Control: `int` was already a `zend_long` on every platform. + * + * @dataProvider sixtyFourBitProvider + */ + public function testIntParameterKeepsAllSixtyFourBits(int $number): void + { + $this->assertMatchesPhp( + fn () => $this->test->intParamToString($number), + fn () => 'n=' . $number + ); + } + + /** + * @dataProvider unsignedProvider + */ + public function testULongParameterKeepsAllSixtyFourBits(int $number): void + { + $this->assertMatchesPhp( + fn () => $this->test->ulongParamToString($number), + fn () => 'n=' . $number + ); + } + + /** + * @dataProvider sixtyFourBitProvider + */ + public function testLongSurvivesParameterLocalAndReturn(int $number): void + { + $this->assertSame($number, $this->test->longRoundTrip($number)); + $this->assertSame($number, $this->test->longThroughLocal($number)); + } + + /** + * @dataProvider unsignedProvider + */ + public function testULongSurvivesParameterAndReturn(int $number): void + { + $this->assertSame($number, $this->test->ulongRoundTrip($number)); + } + + /** + * `zephir_get_intval()` is the funnel shared by every cast, `substr()` + * offset and internal-method coercion. Its `_ex` helper returned a C + * `long`, so Zephir `int` -- which the issue calls safe -- truncated too. + */ + public static function castProvider(): array + { + return [ + 'int64 max' => [PHP_INT_MAX], + 'int64 min' => [PHP_INT_MIN], + 'above int32' => [4294967296], + 'numeric string' => ['9223372036854775807'], + 'negative string' => ['-9223372036854775808'], + 'leading spaces' => [' 4294967296'], + 'trailing garbage' => ['4294967296abc'], + 'float in range' => [4294967296.0], + 'true' => [true], + 'false' => [false], + 'null' => [null], + 'empty array' => [[]], + 'filled array' => [[1, 2]], + ]; + } + + /** + * @dataProvider castProvider + */ + public function testCastToIntMatchesPhp(mixed $value): void + { + $this->assertMatchesPhp( + fn () => $this->test->castToInt($value), + fn () => (int) $value + ); + } + + /** + * @dataProvider castProvider + */ + public function testCastToLongMatchesPhp(mixed $value): void + { + $this->assertMatchesPhp( + fn () => $this->test->castToLong($value), + fn () => (int) $value + ); + } + + public function testArithmeticOnTwoLongsStaysSixtyFourBit(): void + { + $this->assertSame(PHP_INT_MAX, $this->test->longAdd(PHP_INT_MAX - 1, 1)); + $this->assertSame(4294967297, $this->test->longAdd(4294967296, 1)); + } + + /** + * @dataProvider sixtyFourBitProvider + */ + public function testLongArrayKeyMatchesPhp(int $key): void + { + $this->assertMatchesPhp( + fn () => $this->test->longArrayKey($key, 'v'), + fn () => [$key => 'v'] + ); + + $source = [$key => 'v']; + + $this->assertMatchesPhp( + fn () => $this->test->longArrayFetch($source, $key), + fn () => $source[$key] + ); + $this->assertMatchesPhp( + fn () => $this->test->longArrayIsset($source, $key), + fn () => isset($source[$key]) + ); + } + + /** + * @dataProvider sixtyFourBitProvider + */ + public function testComparisonAgainstALongMatchesPhp(int $limit): void + { + foreach ([0, 4294967296, PHP_INT_MAX, PHP_INT_MIN, '4294967296'] as $value) { + $this->assertMatchesPhp( + fn () => $this->test->varLessThanLong($value, $limit), + fn () => $value < $limit + ); + $this->assertMatchesPhp( + fn () => $this->test->varGreaterThanLong($value, $limit), + fn () => $value > $limit + ); + } + } + + public function testSubstrWithLongOffsetMatchesPhp(): void + { + $source = 'abcdef'; + + foreach ([[0, 3], [2, 2], [-3, 2], [1, -1]] as [$from, $length]) { + $this->assertMatchesPhp( + fn () => $this->test->substrWithLongOffset($source, $from, $length), + fn () => substr($source, $from, $length) + ); + } + } + + /** + * Limits of 0, 1 and negatives are deliberately absent: `zephir_fast_explode()` + * hands every limit straight to `php_explode()`, while PHP's own explode() + * routes `limit <= 1` and negative limits elsewhere. That is a separate + * defect from the operand width this issue is about. + * + * @see https://github.com/zephir-lang/zephir/issues/2674 + */ + public function testExplodeWithLongLimitMatchesPhp(): void + { + $source = 'a,b,c,d'; + + foreach ([2, 3, 4294967296, PHP_INT_MAX] as $limit) { + $this->assertMatchesPhp( + fn () => $this->test->explodeWithLongLimit($source, $limit), + fn () => explode(',', $source, $limit) + ); + } + } + + /** + * A multi-dimensional key travels through a variadic slot that + * `zephir_array_update_multi()` reads back as `zend_long`. Passing it as a + * C `int`, which every integer literal is, leaves the upper half of the + * slot undefined; Windows returned keys such as 140733193388033 for 1. + * `uint`/`ulong` offsets were passed as `&i` and used the pointer value. + * + * @dataProvider sixtyFourBitProvider + */ + public function testMultiDimensionalKeysMatchPhp(int $key): void + { + $this->assertMatchesPhp( + fn () => $this->test->multiIntKey($key), + fn () => [$key => [2 => 'v']] + ); + $this->assertMatchesPhp( + fn () => $this->test->multiLongKey($key), + fn () => [$key => [2 => 'v']] + ); + $this->assertMatchesPhp( + fn () => $this->test->multiMixedKeys($key), + fn () => ['k' => [$key => 'v']] + ); + } + + /** + * @dataProvider unsignedProvider + */ + public function testUnsignedMultiDimensionalKeysMatchPhp(int $key): void + { + $this->assertMatchesPhp( + fn () => $this->test->multiUintKey($key), + fn () => [$key => [2 => 'v']] + ); + $this->assertMatchesPhp( + fn () => $this->test->multiUlongKey($key), + fn () => [$key => [2 => 'v']] + ); + } + + public function testLiteralMultiDimensionalKeysMatchPhp(): void + { + $this->assertMatchesPhp( + fn () => $this->test->multiLiteralKeys(), + fn () => [0 => [1 => 'v']] + ); + $this->assertMatchesPhp( + fn () => $this->test->multiDeepKeys(), + fn () => [0 => [1 => [2 => [3 => [4 => [5 => [6 => [7 => [8 => 'v']]]]]]]]] + ); + } + + /** + * Every shape of `%` the compiler can emit, against the identical PHP + * expression. PHP's `%` always yields an `int`, so routing the result + * through a C `double` -- which Zephir did -- loses every value above + * 2^53 on every platform, not just Windows. + */ + public function testModuloMatchesPhp(): void + { + foreach ($this->moduloOperands() as [$a, $b]) { + $this->assertMatchesPhp( + fn () => $this->test->modLongLong($a, $b), + fn () => $a % $b + ); + $this->assertMatchesPhp( + fn () => $this->test->modVarLong($a, $b), + fn () => $a % $b + ); + $this->assertMatchesPhp( + fn () => $this->test->modLongVar($a, $b), + fn () => $a % $b + ); + } + } + + public function testModuloWithADoubleOperandMatchesPhp(): void + { + foreach ([7, -7, 0, PHP_INT_MAX, PHP_INT_MIN] as $a) { + $this->assertMatchesPhp( + fn () => $this->test->modLongDouble($a), + fn () => $a % 2.5 + ); + $this->assertMatchesPhp( + fn () => $this->test->modVarDouble($a), + fn () => $a % 2.5 + ); + } + + foreach ([7.0, -7.5, 0.0, 2.5] as $a) { + $this->assertMatchesPhp( + fn () => $this->test->modDoubleLong($a, 3), + fn () => $a % 3 + ); + $this->assertMatchesPhp( + fn () => $this->test->modDoubleDouble($a), + fn () => $a % 2.5 + ); + $this->assertMatchesPhp( + fn () => $this->test->modDoubleVar($a, 3), + fn () => $a % 3 + ); + $this->assertMatchesPhp( + fn () => $this->test->modLiteralDoubleByDouble($a), + fn () => 2.5 % $a + ); + } + } + + /** + * php-src special-cases a `-1` divisor in `mod_function()` because + * `PHP_INT_MIN % -1` overflows and raises SIGFPE on x86. + */ + public function testModuloByMinusOneDoesNotCrash(): void + { + $this->assertMatchesPhp( + fn () => $this->test->modLongLong(PHP_INT_MIN, -1), + fn () => PHP_INT_MIN % -1 + ); + $this->assertMatchesPhp( + fn () => $this->test->modVarLong(PHP_INT_MIN, -1), + fn () => PHP_INT_MIN % -1 + ); + } + + /** + * PHP 8 throws DivisionByZeroError; Zephir raised an E_WARNING and + * returned 0. + */ + public function testModuloByZeroThrowsAsPhpDoes(): void + { + $this->assertMatchesPhp( + fn () => $this->test->modLongLong(7, 0), + fn () => 7 % 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->modVarLong(7, 0), + fn () => 7 % 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->modLongVar(7, 0), + fn () => 7 % 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->modDoubleLong(7.0, 0), + fn () => 7.0 % 0 + ); + // (int) 0.5 is 0, so PHP throws here too. + $this->assertMatchesPhp( + fn () => $this->test->modLiteralDoubleByDouble(0.5), + fn () => 2.5 % 0.5 + ); + } + + public function testDivisionByZeroThrowsAsPhpDoes(): void + { + $this->assertMatchesPhp( + fn () => $this->test->divLongLong(7, 0), + fn () => 7 / 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->divVarLong(7, 0), + fn () => 7 / 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->divLongVar(7, 0), + fn () => 7 / 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->divDoubleLong(7.0, 0), + fn () => 7.0 / 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->divDoubleVar(7.0, 0), + fn () => 7.0 / 0 + ); + $this->assertMatchesPhp( + fn () => $this->test->divLiteralDoubleByDouble(0.0), + fn () => 2.5 / 0.0 + ); + } + + /** + * A non-exact division is a float in PHP too, so the whole shape set can + * be compared against PHP directly. + */ + public function testNonExactDivisionMatchesPhp(): void + { + $this->assertMatchesPhp(fn () => $this->test->divLongLong(7, 2), fn () => 7 / 2); + $this->assertMatchesPhp(fn () => $this->test->divVarLong(7, 2), fn () => 7 / 2); + $this->assertMatchesPhp(fn () => $this->test->divLongVar(7, 2), fn () => 7 / 2); + $this->assertMatchesPhp(fn () => $this->test->divLongDouble(7), fn () => 7 / 2.5); + $this->assertMatchesPhp(fn () => $this->test->divVarDouble(7), fn () => 7 / 2.5); + $this->assertMatchesPhp(fn () => $this->test->divDoubleLong(7.0, 2), fn () => 7.0 / 2); + $this->assertMatchesPhp(fn () => $this->test->divDoubleDouble(7.0), fn () => 7.0 / 2.5); + $this->assertMatchesPhp(fn () => $this->test->divDoubleVar(7.0, 2), fn () => 7.0 / 2); + $this->assertMatchesPhp(fn () => $this->test->divLiteralDoubleByDouble(2.0), fn () => 2.5 / 2.0); + } + + /** + * Known divergence, pinned so that closing it is a deliberate act: PHP's + * `/` narrows an exact result to `int`, Zephir's helpers always return a + * C double. + * + * @see https://github.com/zephir-lang/zephir/issues/2675 + */ + public function testExactDivisionStillReturnsFloat(): void + { + $this->assertSame(2, 4 / 2); + $this->assertSame(2.0, $this->test->divLongLong(4, 2)); + } + + /** + * @return list + */ + private function moduloOperands(): array + { + return [ + [7, 3], + [-7, 3], + [7, -3], + [PHP_INT_MAX, 10], + [PHP_INT_MIN, 10], + [PHP_INT_MAX, PHP_INT_MIN], + [4294967296, 4294967295], + [PHP_INT_MAX, PHP_INT_MAX], + ]; + } +} diff --git a/tests/Extension/Issue760Test.php b/tests/Extension/Issue760Test.php index 6b756ddb9..02ea38265 100644 --- a/tests/Extension/Issue760Test.php +++ b/tests/Extension/Issue760Test.php @@ -16,7 +16,6 @@ use PHPUnit\Framework\TestCase; use Stub\Issue760; use stdClass; -use Throwable; /** * String offsets must behave exactly as PHP's do. @@ -31,6 +30,8 @@ */ final class Issue760Test extends TestCase { + use AssertsPhpParity; + private const SUBJECT = 'abcdef'; private Issue760 $test; @@ -291,6 +292,15 @@ public function testTraverseStillWalksArrays(): void $this->assertSame([2, 1], $this->test->dynTraverseReverse([1, 2])); } + /** + * The extremes are not padding. These offsets reach the kernel through the + * `*_long` helpers, whose parameter used to be `unsigned long` -- 32-bit on + * Windows, where a negative offset became a huge positive one and a write + * tried to grow the string to 4 GB. Windows CI is the only place that can + * catch a regression here. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ public static function intOffsetProvider(): array { return [ @@ -375,40 +385,4 @@ public static function traverseProvider(): array 'nul' => ["a\0b", ['a', "\0", 'b'], [97, 0, 98]], ]; } - - /** - * Runs both sides and asserts the diagnostics and the value agree. - */ - private function assertMatchesPhp(callable $extension, callable $php): void - { - $this->assertSame( - $this->transcript($php), - $this->transcript($extension), - 'The extension must behave exactly as PHP does' - ); - } - - private function transcript(callable $subject): string - { - $diagnostics = []; - - set_error_handler(static function (int $code, string $message) use (&$diagnostics): bool { - $diagnostics[] = $message; - - return true; - }); - - try { - $value = $subject(); - $result = is_string($value) - ? 'string(' . strlen($value) . ') ' . bin2hex($value) - : var_export($value, true); - } catch (Throwable $e) { - $result = get_class($e) . ': ' . $e->getMessage(); - } finally { - restore_error_handler(); - } - - return implode(' ; ', $diagnostics) . ' => ' . $result; - } } diff --git a/tests/Zephir/Backends/ZendEngine3/BackendTest.php b/tests/Zephir/Backends/ZendEngine3/BackendTest.php index 5a94e16a0..6c7dbed6d 100644 --- a/tests/Zephir/Backends/ZendEngine3/BackendTest.php +++ b/tests/Zephir/Backends/ZendEngine3/BackendTest.php @@ -50,4 +50,32 @@ public function testShouldEscapeStringWithRegexp(string $testString, string $exp $this->assertSame($expected, $actual); } + + /** + * Zephir's four integer types all describe a PHP `int`, which the engine + * stores in a `zend_long` (`int64_t` on every 64-bit target). Mapping any + * of them to a C `long` makes the width follow the data model: 64-bit on + * LP64 (Linux, macOS), 32-bit on LLP64 (Windows x64). + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ + public function integerTypeDefinitionProvider(): array + { + return [ + 'int' => ['int', 'zend_long'], + 'uint' => ['uint', 'zend_ulong'], + 'long' => ['long', 'zend_long'], + 'ulong' => ['ulong', 'zend_ulong'], + ]; + } + + /** + * @dataProvider integerTypeDefinitionProvider + */ + public function testIntegerTypesMapToAFixedWidthCType(string $type, string $expected): void + { + $backend = new Backend(new Config(), '', ''); + + $this->assertSame([null, $expected], $backend->getTypeDefinition($type)); + } } diff --git a/tests/Zephir/BlackBox/Issue2666Test.php b/tests/Zephir/BlackBox/Issue2666Test.php new file mode 100644 index 000000000..a6b77df4e --- /dev/null +++ b/tests/Zephir/BlackBox/Issue2666Test.php @@ -0,0 +1,189 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Zephir\Test\BlackBox; + +use PHPUnit\Framework\TestCase; + +/** + * Zephir's `long`/`ulong` describe a PHP `int`, which the engine keeps in a + * `zend_long` (`int64_t`). Emitting a C `long` instead makes the width follow + * the data model, so the local is 64-bit on LP64 (Linux, macOS) and 32-bit on + * LLP64 (Windows x64). + * + * For a parameter that is worse than a narrowing conversion: `Z_PARAM_LONG(n)` + * expands to `zend_parse_arg_long(arg, &n, ...)`, whose `dest` is a + * `zend_long *`. A `long *` to a 4-byte object receives an 8-byte store. + * + * The assertions here run on every platform because they inspect the generated + * C rather than its behaviour, which is what makes a Windows-only bug testable + * from a Linux container. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ +final class Issue2666Test extends TestCase +{ + use RunsZephirCommands; + + protected function setUp(): void + { + $this->setUpZephirRunner(); + } + + protected function tearDown(): void + { + $this->tearDownZephirRunner(); + } + + public function testLongAndULongEmitFixedWidthCTypes(): void + { + $generated = $this->generate( + 'longwidth', + 'widths.zep', + <<<'ZEP' + namespace Longwidth; + + class Widths + { + public function fromParam(long n) -> string + { + string retval = "n="; + let retval .= n; + return retval; + } + + public function fromUnsignedParam(ulong u) -> string + { + string retval = "u="; + let retval .= u; + return retval; + } + + public function locals(var value) -> long + { + long total = 0; + ulong step = 1; + + let total = (int) value; + let total = total + step; + + return total; + } + } + ZEP, + ); + + $this->assertStringContainsString('zend_long n;', $generated); + $this->assertStringContainsString('zend_ulong u;', $generated); + $this->assertStringContainsString('Z_PARAM_LONG(n)', $generated); + + $this->assertSame( + [], + $this->platformVariantDeclarations($generated), + 'Generated C declares a local whose width follows the data model.', + ); + } + + /** + * `zephir_array_update_multi()` and its two siblings read every `l` offset + * with `va_arg(ap, zend_long)`. A variadic argument passed as a plain C + * `int` therefore has its upper half read as whatever the ABI left in the + * slot, and a `uint`/`ulong` local reached the slot as `&name`. Neither is + * observable on the System V ABI, where the compiler happens to clear the + * upper half, so the contract is asserted on the generated C instead. + */ + public function testMultiDimensionalIndexesArePassedAsZendLong(): void + { + $generated = $this->generate( + 'multiwidth', + 'keys.zep', + <<<'ZEP' + namespace Multiwidth; + + class Keys + { + public function literals() -> array + { + array result = []; + let result[0][1] = "v"; + return result; + } + + public function unsignedLocal(uint key) -> array + { + array result = []; + let result[key][2] = "v"; + return result; + } + } + ZEP, + ); + + $this->assertStringContainsString( + 'zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) 0, (zend_long) 1);', + $generated, + ); + $this->assertStringContainsString( + 'zephir_array_update_multi(&result, &_0, SL("ll"), 2, (zend_long) key, (zend_long) 2);', + $generated, + ); + } + + /** + * Declarations whose C type is a bare `long`/`unsigned long`, i.e. 32-bit + * under LLP64 and 64-bit under LP64. `zend_long`/`zend_ulong` are excluded + * by the leading word boundary. + * + * @return list + */ + private function platformVariantDeclarations(string $source): array + { + preg_match_all( + '/^\h*(?:unsigned\h+)?long\h+[A-Za-z_]\w*\s*[;,=]/m', + $source, + $matches, + ); + + return array_map('trim', $matches[0]); + } + + /** + * Scaffolds a throwaway project, drops a single `.zep` in it, runs + * `zephir generate` and returns the generated C source. + */ + private function generate(string $project, string $fileName, string $source): string + { + $projectDir = $this->outputDir() . '/' . $project; + $this->cleanupPath($projectDir); + + $this->assertSame( + 0, + $this->runZephir('init ' . $project, $this->outputDir())['exitCode'], + ); + + file_put_contents($projectDir . '/' . $project . '/' . $fileName, $source . "\n"); + + $result = $this->runZephir('generate --no-ansi', $projectDir); + $this->assertSame(0, $result['exitCode'], $result['stderr']); + + $generatedFile = sprintf( + '%s/ext/%s/%s.c', + $projectDir, + $project, + basename($fileName, '.zep') . '.zep', + ); + $this->assertFileExists($generatedFile); + + return (string) file_get_contents($generatedFile); + } +} diff --git a/tests/Zephir/KernelPortabilityTest.php b/tests/Zephir/KernelPortabilityTest.php new file mode 100644 index 000000000..dd342cedc --- /dev/null +++ b/tests/Zephir/KernelPortabilityTest.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Zephir\Test; + +use PHPUnit\Framework\TestCase; + +/** + * C `long` is 64-bit under LP64 (Linux, macOS) and 32-bit under LLP64 + * (Windows x64), while `zend_long` is `int64_t` on both. A kernel prototype + * that spells `long` therefore truncates every PHP integer on Windows, and + * nothing on a Linux or macOS CI leg can observe it. + * + * This test is that observation: it reads the headers rather than running + * them, so the portability contract is enforced from any platform. + * + * @see https://github.com/zephir-lang/zephir/issues/2666 + */ +final class KernelPortabilityTest extends TestCase +{ + /** + * `long long` and `unsigned long long` are exactly 64-bit everywhere, so + * they carry no data-model dependency and are allowed. + */ + private const ALLOWED = ['long long', 'unsigned long long']; + + public function kernelHeaderProvider(): array + { + $headers = glob($this->kernelDir() . '/*.h'); + self::assertNotEmpty($headers); + + $cases = []; + foreach ($headers as $header) { + $cases[basename($header)] = [$header]; + } + + return $cases; + } + + /** + * @dataProvider kernelHeaderProvider + */ + public function testKernelHeadersUseNoPlatformVariantIntegerType(string $header): void + { + $this->assertSame( + [], + $this->platformVariantTypes((string) file_get_contents($header)), + sprintf( + '%s uses a C `long`, whose width follows the data model. ' + . 'Use `zend_long`/`zend_ulong` for a PHP integer.', + basename($header), + ), + ); + } + + /** + * Every `long` token that is neither part of `zend_long`/`zend_ulong` + * (blocked by the word boundary) nor one of the fixed-width spellings. + * + * @return list + */ + private function platformVariantTypes(string $source): array + { + $source = $this->stripComments($source); + + preg_match_all( + '/\b(?:unsigned\s+)?long(?:\s+long)?\b/', + $source, + $matches, + PREG_OFFSET_CAPTURE, + ); + + $found = []; + foreach ($matches[0] as [$match, $offset]) { + $normalised = preg_replace('/\s+/', ' ', $match); + if (in_array($normalised, self::ALLOWED, true)) { + continue; + } + + $line = substr_count(substr($source, 0, $offset), "\n") + 1; + $found[] = $line . ': ' . $normalised; + } + + return $found; + } + + /** + * Block and line comments only; the headers contain no string literal + * holding the word `long`. + */ + private function stripComments(string $source): string + { + // Keep the newlines so reported line numbers stay accurate. + return (string) preg_replace_callback( + '#/\*.*?\*/|//[^\n]*#s', + static fn (array $m): string => str_repeat("\n", substr_count($m[0], "\n")), + $source, + ); + } + + private function kernelDir(): string + { + return dirname(__DIR__, 2) . '/kernel'; + } +}