diff options
348 files changed, 19373 insertions, 14081 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..aacb25bc5c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,73 @@ +# Contributing to PHP + +Anybody who programs in PHP can be a contributing member of the community that +develops and deploys it; the task of deploying PHP, documentation and +associated websites is a never ending one. With every release, or release +candidate comes a wave of work, which takes a lot of organization and +co-ordination. + +## Pull requests + +PHP welcomes pull requests to [add tests](#writing-tests), fix bugs and to +implement RFCs. Please be sure to include tests as appropriate! + +If you are fixing a bug, then please submit your PR against the lowest branch +of PHP that the bug affects, or the oldest fully supported version (the first +green branch on +[the supported version page](http://php.net/supported-versions.php). For +example, at the time of writing in mid-2015, this is PHP 5.5, which corresponds +to the `PHP-5.5` branch in Git. Please also make sure you add a link to the PR +in the bug on [the bug tracker](https://bugs.php.net/). + +Pull requests to implement RFCs should be submitted against `master`. + +## Filing bugs + +Bugs can be filed on the [PHP bug tracker](https://bugs.php.net/). If this is +the first time you've filed a bug, we suggest reading the +[guide to reporting a bug](https://bugs.php.net/how-to-report.php). + +Where possible, please include a self-contained reproduction case! + +## Feature requests + +Feature requests are generally submitted in the form of +[Requests for Comment](https://wiki.php.net/rfc/howto), ideally accompanied by +[pull requests](#pull-requests). You can find the extremely large list of RFCs +that have been previously considered on the +[PHP Wiki](https://wiki.php.net/rfc). + +You may want to read +[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process) +for additional notes on the best way to approach submitting an RFC. + +## Writing tests + +We love getting new tests! PHP is a huge project and improving code coverage is +a huge win for every PHP user. + +[Our QA site includes a page detailing how to write test cases.](http://qa.php.net/write-test.php) +Please note that the section on submitting pull requests is outdated: in +addition to the process listed there, you can also +[submit pull requests](#pull-requests). + +## Writing documentation + +There are two ways to contribute to the PHP manual. You can edit the manual and +send patches anonymously via [the online editor](https://edit.php.net/), or you +can check the XML source out from Subversion and edit that and build it +[per the instructions on the documentation site](http://doc.php.net/tutorial/). +Patches created that way should be sent to the +[documentation mailing list](mailto:phpdoc@lists.php.net). + +## Getting help + +If you are having trouble contributing to PHP, or just want to talk to a human +about what you're working on, you can contact us via the +[internals mailing list](mailto:internals@lists.php.net), or the +[documentation mailing list](mailto:phpdoc@lists.php.net) for documentation +issues. + +Although not a formal channel, you can also find a number of core developers on +the #php.pecl channel on [EFnet](http://www.efnet.org/). Similarly, many +documentation writers can be found on #php.doc. diff --git a/Makefile.global b/Makefile.global index f833efa5e7..c571f3455d 100644 --- a/Makefile.global +++ b/Makefile.global @@ -130,5 +130,18 @@ distclean: clean fi $(EGREP) define'.*include/php' $(top_srcdir)/configure | $(SED) 's/.*>//'|xargs rm -f -.PHONY: all clean install distclean test +prof-gen: + CCACHE_DISABLE=1 $(MAKE) PROF_FLAGS=-fprofile-generate all + +prof-clean: + find . -name \*.lo -o -name \*.o | xargs rm -f + find . -name \*.la -o -name \*.a | xargs rm -f + find . -name \*.so | xargs rm -f + rm -f libphp$(PHP_MAJOR_VERSION).la $(SAPI_CLI_PATH) $(SAPI_CGI_PATH) $(SAPI_MILTER_PATH) $(SAPI_LITESPEED_PATH) $(SAPI_FPM_PATH) $(OVERALL_TARGET) modules/* libs/* + +prof-use: + CCACHE_DISABLE=1 $(MAKE) PROF_FLAGS=-fprofile-use all + + +.PHONY: all clean install distclean test prof-gen prof-clean prof-use .NOEXPORT: @@ -1,14 +1,40 @@ -PHP NEWS +PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 20??, PHP 7.0.0 +25 Jun 2015, PHP 7.0.0 Alpha 2 -- CLI server: - . Refactor MIME type handling to use a hash table instead of linear search. - (Adam) - . Update the MIME type list from the one shipped by Apache HTTPD. (Adam) - . Added support for SEARCH WebDav method. (Mats Lindh) +- Core: + . Fixed bug #69823 (PHP 7.0.0alpha1 segmentation fault when exactly 33 + extensions are loaded). (Laruence) + . Fixed bug #69805 (null ptr deref and seg fault in zend_resolve_class_name). + (Laruence) + . Fixed bug #69761 (Serialization of anonymous classes should be prevented). + (Laruence) + . Fixed bug #69551 (parse_ini_file() and parse_ini_string() segmentation + fault). (Christoph M. Becker) + . Fixed bug #69781 (phpinfo() reports Professional Editions of Windows + 7/8/8.1/10 as "Business"). (Christian Wenz) + +- mysqlnd: + . Fixed Bug #69796 (mysqli_stmt::fetch doesn't assign null values to + bound variables). (Laruence); + +- Curl: + . Fixed bug #69831 (Segmentation fault in curl_getinfo). (im dot denisenko at + yahoo dot com) + +- PDO_pgsql: + . Fixed bug #69752 (PDOStatement::execute() leaks memory with DML + Statements when closeCuror() is u). (Philip Hofstetter) + +11 Jun 2015, PHP 7.0.0 Alpha 1 - Core: + . Fixed bug #69767 (Default parameter value with wrong type segfaults). + (cmb, Laruence) + . Fixed bug #69756 (Fatal error: Nesting level too deep - recursive dependency + ? with ===). (Dmitry, Laruence) + . Fixed bug #69758 (Item added to array not being removed by array_pop/shift + ). (Laruence) . Fixed bug #68475 (Add support for $callable() sytnax with 'Class::method'). (Julien, Aaron Piotrowski) . Fixed bug #69485 (Double free on zend_list_dtor). (Laruence) @@ -77,7 +103,16 @@ . Implemented the RFC `Constructor behaviour of internal classes`. (Dan, Dmitry) . Implemented the RFC `Fix "foreach" behavior`. (Dmitry) . Implemented the RFC `Generator Delegation`. (Bob) - . Implemented the RFC ` Anonymous Class Support`. (Joe, Nikita, Dmitry) + . Implemented the RFC `Anonymous Class Support`. (Joe, Nikita, Dmitry) + . Implemented the RFC `Context Sensitive Lexer`. (Marcio Almada) + . Fixed bug #69511 (Off-by-one buffer overflow in php_sys_readlink). + (Jan Starke, Anatol) + +- CLI server: + . Refactor MIME type handling to use a hash table instead of linear search. + (Adam) + . Update the MIME type list from the one shipped by Apache HTTPD. (Adam) + . Added support for SEARCH WebDav method. (Mats Lindh) - Curl: . Fixed bug #68937 (Segfault in curl_multi_exec). (Laruence) @@ -104,6 +139,7 @@ - GD: . Made fontFetch's path parser thread-safe. (Sara) + . Removed T1Lib support. (Kalle) - Fileinfo: . Fixed bug #66242 (libmagic: don't assume char is signed). (ArdB) @@ -117,7 +153,7 @@ . Implement request #67106 (Split main fpm config). (Elan Ruusamäe, Remi) - FTP: - . Fixed bug #69082 FTPS support on Windows + . Fixed bug #69082 (FTPS support on Windows). (Anatol) - Intl: . Removed deprecated aliases datefmt_set_timezone_id() and @@ -135,12 +171,18 @@ - LiteSpeed: . Updated LiteSpeed SAPI code from V5.5 to V6.6. (George Wang) +- libxml: + . Fixed handling of big lines in error messages with libxml >= 2.9.0. + (Christoph M. Becker) + - Mcrypt: . Fixed possible read after end of buffer and use after free. (Dmitry) . Removed mcrypt_generic_end() alias. (Nikita) . Removed mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb(), mcrypt_ofb(). (Nikita) - Opcache: + . Fixed bug #69688 (segfault with eval and opcache fast shutdown). + (Laruence) . Added experimental (disabled by default) file based opcode cache. (Dmitry, Laruence, Anatol) . Fixed bug with try blocks being removed when extended_info opcode @@ -181,6 +223,8 @@ - Reflection . Fixed inheritance chain of Reflector interface. (Tjerk) . Added ReflectionGenerator class. (Bob) + . Added reflection support for return types and type declarations. (Sara, + Matteo) - Session: . Fixed bug #67694 (Regression in session_regenerate_id()). (Tjerk) @@ -198,13 +242,14 @@ nor curruption state). (Julien) . Fixed bug #66405 (RecursiveDirectoryIterator::CURRENT_AS_PATHNAME breaks the RecursiveIterator). (Paul Garvin) - . Fixed bug #68479 (Added escape parameter to SplFileObject::fputcsv). (Salathe) - Sqlite3: . Fixed bug #68260 (SQLite3Result::fetchArray declares wrong required_num_args). (Julien) - Standard: + . Fixed bug #69723 (Passing parameters by reference and array_column). + (Laruence) . Fixed bug #69523 (Cookie name cannot be empty). (Christoph M. Becker) . Fixed bug #69325 (php_copy_file_ex does not pass the argument). (imbolk at gmail dot com) @@ -220,7 +265,11 @@ . Fixed bug #65272 (flock() out parameter not set correctly in windows). (Daniel Lowrey) . Added preg_replace_callback_array function. (Wei Dai) - . Deprecated salt option to password_hash. (Anthony) + . Deprecated salt option to password_hash. (Anthony) + . Fixed bug #69686 (password_verify reports back error on PHP7 will null + string). (Anthony) + . Added Windows support for getrusage(). (Kalle) + . Removed hardcoded limit on number of pipes in proc_open(). (Tony) - Streams: . Fixed bug #68532 (convert.base64-encode omits padding bytes). @@ -461,6 +461,10 @@ Other IntlDateFormatter::setTimeZoneID(). Use datefmt_set_timezone() and IntlDateFormatter::setTimeZone() instead. +- libxml: + . Added LIBXML_BIGLINES parser option. It's available starting with libxml 2.9.0 + and adds suppport for line numbers >16-bit in the error reporting. + - Mcrypt . Removed deprecated mcrypt_generic_end() alias in favor of mcrypt_generic_deinit(). @@ -488,7 +492,7 @@ Other - PCRE: . Removed support for /e (PREG_REPLACE_EVAL) modifier. Use - preg_reaplace_callback() instead. + preg_replace_callback() instead. - PDO_pgsql: . Removed PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT attribute in favor of @@ -530,6 +534,12 @@ Other (RFC: https://wiki.php.net/rfc/combined-comparison-operator) . Added the yield from operator for delegating Generators like coroutines. (RFC: https://wiki.php.net/rfc/generator-delegation) + . Reserved keywords can now be used in various new contexts. + (RFC: https://wiki.php.net/rfc/context_sensitive_lexer) + . Added support for scalar type declatarations and strict mode using + declare(strict_types=1) (RFC: https://wiki.php.net/rfc/scalar_type_hints_v5) + . Added support for cryptographically secure user land RNG + (RFC: https://wiki.php.net/rfc/easy_userland_csprng) - OpenSSL . Added "alpn_protocols" SSL context option allowing encrypted client/server @@ -538,7 +548,12 @@ Other accessible through stream_get_meta_data() output. - Reflection - . Added a ReflectionGenerator class (yield from Traces, current file/line etc.) + . Added a ReflectionGenerator class (yield from Traces, current file/line, + etc.) + . Added a ReflectionType class to better support the new return type and + scalar type declarations features. The new ReflectionParameter::getType() + and ReflectionFunctionAbstract::getReturnType() methods both return an + instance of ReflectionType. ======================================== 3. Changes in SAPI modules @@ -575,6 +590,10 @@ Other acceptable classes: unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]); +- proc_open(): + The maximum number of pipes used by proc_open() was previously limited by + hardcoded value of 16. This limit is now removed and the number of pipes is + effectively limited by the amount of memory available to PHP. ======================================== 6. New Functions @@ -611,6 +630,7 @@ Other - sapi/continuity - sapi/isapi - sapi/milter +- sapi/nsapi - sapi/phttpd - sapi/pi3web - sapi/roxen @@ -618,8 +638,17 @@ Other - sapi/tux - sapi/webjames - ext/mssql +- ext/mysql - ext/sybase_ct -For more details see https://wiki.php.net/rfc/removal_of_dead_sapis_and_exts +- ext/ereg + +For more details see + +https://wiki.php.net/rfc/removal_of_dead_sapis_and_exts +https://wiki.php.net/rfc/remove_deprecated_functionality_in_php7 + +NOTE NSAPI was not voted in the RFC, however it was removed afterwards. It turned +out, that the corresponding SDK isn't available anymore. ======================================== 9. Other Changes to Extensions @@ -643,6 +672,23 @@ For more details see https://wiki.php.net/rfc/removal_of_dead_sapis_and_exts . ZLIB_BLOCK . ZLIB_FINISH +- GD + . T1Lib support removed, thus lifting the optional dependency on T1Lib, the + following is therefore not available anymore: + + Functions: + - imagepsbbox() + - imagepsencodefont() + - imagepsextendedfont() + - imagepsfreefont() + - imagepsloadfont() + - imagepsslantfont() + - imagepstext() + + Resources: + - 'gd PS font' + - 'gd PS encoding' + ======================================== 11. Changes to INI File Handling ======================================== @@ -659,6 +705,7 @@ For more details see https://wiki.php.net/rfc/removal_of_dead_sapis_and_exts - Core . Support for native 64 bit integers in 64 bit builds. . Support for large files in 64 bit builds. + . Support for getrusage() - ftp . The ftp extension is always shipped shared diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 632c87e1c4..60ae493434 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -2,6 +2,7 @@ $Id$ PHP 7.0 INTERNALS UPGRADE NOTES +0. Wiki Examples 1. Internal API changes e. New data types f. zend_parse_parameters() specs @@ -23,6 +24,14 @@ PHP 7.0 INTERNALS UPGRADE NOTES b. Windows build system changes +================ +0. Wiki Examples +================ + +The wiki contains multiple examples and further explanations of the internal +changes. See: https://wiki.php.net/phpng-upgrading + + ======================== 1. Internal API changes ======================== @@ -169,7 +178,7 @@ PHP 7.0 INTERNALS UPGRADE NOTES before any globals was accessed. Porting an extension or SAPI is usually as easy as removing all the TSRMLS_* - ocurrences and integrating the macros mentioned above. However if tsrm_ls + occurrences and integrating the macros mentioned above. However if tsrm_ls is used explicitly, its usage can be considered obsolete in most cases. Additionally, if an extension triggers its own threads, TSRMLS_CACHE shouldn't be passed to that threads directly. @@ -196,10 +205,10 @@ PHP 7.0 INTERNALS UPGRADE NOTES zend_fetch_resource_ex. More details can be found in Zend/zend_list.c. t. Optimized strings concatenation. - ZEND_ADD_STRING/VAR/CHAR are replaced with ZEND_ROPE_INTI, ZEND_ROPE_ADD, + ZEND_ADD_STRING/VAR/CHAR are replaced with ZEND_ROPE_INIT, ZEND_ROPE_ADD, ZEND_ROPE_END. Instead of reallocation and copying string on each ZEND_ADD_STRING/VAR/CAHR, - collect all the strings and then alloca te and construct the resulting string once. + collect all the strings and then allocate and construct the resulting string once. ======================== @@ -230,7 +239,8 @@ PHP 7.0 INTERNALS UPGRADE NOTES which suppresses the most frequent false positive warnings - the --with-mp option will by default utilize all the available cores. It's - enabled by default and can be disabled with the special "disable" keyword. + enabled by default for release builds and can be disabled with the special + "disable" keyword. ======================== 3. Module changes @@ -251,7 +261,7 @@ PHP 7.0 INTERNALS UPGRADE NOTES Do not access PS(id) directly, but use this handler and it's parameter. - PS_UPDATE_TIMESTAMP() defines timestamp updating handler. This handler must update session data timestamp for GC if it is needed. e.g. Memcache - updates timestap on read, so it does not need to update timestamp. Return + updates timestamp on read, so it does not need to update timestamp. Return SUCCESS simply for this case. - PS_CREATE_SID() should check session ID collision. Return NULL for failure. - More documentation can be found in ext/session/mod_files.c as comments. diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 868a04d91e..6104dc0bc9 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -462,4 +462,4 @@ if test "$ZEND_GCC_GLOBAL_REGS" = "yes"; then else HAVE_GCC_GLOBAL_REGS=no fi -AC_MSG_RESULT(ZEND_GCC_GLOBAL_REGS) +AC_MSG_RESULT($ZEND_GCC_GLOBAL_REGS) diff --git a/Zend/bug69756.phpt b/Zend/bug69756.phpt new file mode 100644 index 0000000000..ca638fb2d6 --- /dev/null +++ b/Zend/bug69756.phpt @@ -0,0 +1,12 @@ +--TEST-- +Fixed bug #69756 (Fatal error: Nesting level too deep - recursive dependency? with ===). +--FILE-- +<?php +$arr = range(1, 2); +foreach($arr as &$item ) { + var_dump($arr === array(1, 2)); +} +?> +--EXPECT-- +bool(true) +bool(true) diff --git a/Zend/tests/anon/006.phpt b/Zend/tests/anon/006.phpt index e5dc1226d8..2b8888c497 100644 --- a/Zend/tests/anon/006.phpt +++ b/Zend/tests/anon/006.phpt @@ -10,6 +10,6 @@ namespace { var_dump ($hello); } --EXPECTF-- -object(lone\class@%s)#1 (0) { +object(class@%s)#1 (0) { } diff --git a/Zend/tests/anon/007.phpt b/Zend/tests/anon/007.phpt index 12f4da6653..59d2441760 100644 --- a/Zend/tests/anon/007.phpt +++ b/Zend/tests/anon/007.phpt @@ -18,6 +18,6 @@ namespace lone { new Outer(); } --EXPECTF-- -object(lone\class@%s)#2 (0) { +object(class@%s)#2 (0) { } diff --git a/Zend/tests/array_offset.phpt b/Zend/tests/array_offset.phpt new file mode 100644 index 0000000000..76c25f9298 --- /dev/null +++ b/Zend/tests/array_offset.phpt @@ -0,0 +1,21 @@ +--TEST-- +Ensure "undefined offset" notice formats message corectly when undefined key is negative +--FILE-- +<?php + +[][-1]; +[][-1.1]; +(new ArrayObject)[-1]; +(new ArrayObject)[-1.1]; + +echo "Done\n"; +?> +--EXPECTF-- +Notice: Undefined offset: -1 in %s on line 3 + +Notice: Undefined offset: -1 in %s on line 4 + +Notice: Undefined offset: -1 in %s on line 5 + +Notice: Undefined offset: -1 in %s on line 6 +Done diff --git a/Zend/tests/array_type_hint_001.phpt b/Zend/tests/array_type_hint_001.phpt index 474ffa8e59..2b473c56b4 100644 --- a/Zend/tests/array_type_hint_001.phpt +++ b/Zend/tests/array_type_hint_001.phpt @@ -14,6 +14,6 @@ foo(123); Fatal error: Uncaught TypeError: Argument 1 passed to foo() must be of the type array, integer given, called in %sarray_type_hint_001.php on line 7 and defined in %sarray_type_hint_001.php:2 Stack trace: -#0 %s(%d): foo() +#0 %s(%d): foo(123) #1 {main} thrown in %sarray_type_hint_001.php on line 2 diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index 80f1bd8aa8..030913f7f9 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -144,12 +144,6 @@ assert(0 && ($a = function () { ?> --EXPECTF-- -Warning: Unsupported declare 'A' in %sexpect_015.php on line %d - -Warning: Unsupported declare 'B' in %sexpect_015.php on line %d - -Warning: Unsupported declare 'C' in %sexpect_015.php on line %d - Warning: assert(): assert(0 && ($a = function () { global $a; global $$b; diff --git a/Zend/tests/bug39003.phpt b/Zend/tests/bug39003.phpt index f4f9e4d9b6..add14512c8 100644 --- a/Zend/tests/bug39003.phpt +++ b/Zend/tests/bug39003.phpt @@ -23,6 +23,6 @@ echo "Done\n"; --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to test() must be an instance of OtherClassName, instance of ClassName given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): test() +#0 %s(%d): test(Object(ClassName)) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/bug42802.phpt b/Zend/tests/bug42802.phpt index 6dad5feccb..3b00408e01 100644 --- a/Zend/tests/bug42802.phpt +++ b/Zend/tests/bug42802.phpt @@ -39,6 +39,6 @@ ok Fatal error: Uncaught TypeError: Argument 1 passed to foo\test5() must be an instance of bar, instance of foo\bar given, called in %sbug42802.php on line %d and defined in %sbug42802.php:%d Stack trace: -#0 %s(%d): foo\test5() +#0 %s(%d): foo\test5(Object(foo\bar)) #1 {main} thrown in %sbug42802.php on line %d diff --git a/Zend/tests/bug43332_1.phpt b/Zend/tests/bug43332_1.phpt index 5fe734cfea..fc035ab2b6 100644 --- a/Zend/tests/bug43332_1.phpt +++ b/Zend/tests/bug43332_1.phpt @@ -14,6 +14,6 @@ $foo->bar(new \stdclass); // Error, ok! --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to foobar\foo::bar() must be an instance of foobar\foo, instance of stdClass given, called in %sbug43332_1.php on line 10 and defined in %sbug43332_1.php:5 Stack trace: -#0 %s(%d): foobar\foo->bar() +#0 %s(%d): foobar\foo->bar(Object(stdClass)) #1 {main} thrown in %sbug43332_1.php on line 5 diff --git a/Zend/tests/bug68446.phpt b/Zend/tests/bug68446.phpt index 2dad15d411..994699e5de 100644 --- a/Zend/tests/bug68446.phpt +++ b/Zend/tests/bug68446.phpt @@ -34,7 +34,7 @@ array(1) { Fatal error: Uncaught TypeError: Argument 1 passed to a() must be of the type array, null given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): a() +#0 %s(%d): a(NULL) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/bug69551.phpt b/Zend/tests/bug69551.phpt new file mode 100644 index 0000000000..096852a2fa --- /dev/null +++ b/Zend/tests/bug69551.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #69551 - parse_ini_file() and parse_ini_string() segmentation fault +--FILE-- +<?php +$ini = <<<INI +[Network.eth0] +SubnetMask = " +" +INI; +$settings = parse_ini_string($ini, false, INI_SCANNER_RAW); +var_dump($settings); +?> +--EXPECTF-- +Warning: syntax error, unexpected '"' in Unknown on line %d + in %s on line %d +bool(false) diff --git a/Zend/tests/bug69676.phpt b/Zend/tests/bug69676.phpt new file mode 100644 index 0000000000..54b9d40047 --- /dev/null +++ b/Zend/tests/bug69676.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #69676: Resolution of self::FOO in class constants not correct +--FILE-- +<?php +class A { + const myConst = "const in A"; + const myDynConst = self::myConst; +} + +class B extends A { + const myConst = "const in B"; +} + +var_dump(B::myDynConst); +var_dump(A::myDynConst); +?> +--EXPECT-- +string(10) "const in A" +string(10) "const in A" diff --git a/Zend/tests/bug69732.phpt b/Zend/tests/bug69732.phpt new file mode 100644 index 0000000000..bc6206d113 --- /dev/null +++ b/Zend/tests/bug69732.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bug #69732 (can induce segmentation fault with basic php code) +--FILE-- +<?php +class wpq { + private $unreferenced; + + public function __get($name) { + return $this->$name . "XXX"; + } +} + +function ret_assoc() { + $x = "XXX"; + return array('foo' => 'bar', $x); +} + +$wpq = new wpq; +$wpq->interesting =& ret_assoc(); +$x = $wpq->interesting; +printf("%s\n", $x); +--EXPECTF-- +Notice: Undefined property: wpq::$interesting in %sbug69732.php on line 6 + +Notice: Indirect modification of overloaded property wpq::$interesting has no effect in %sbug69732.php on line 16 + +Notice: Only variables should be assigned by reference in %sbug69732.php on line 16 + +Notice: Undefined property: wpq::$interesting in %sbug69732.php on line 6 +XXX diff --git a/Zend/tests/bug69740.phpt b/Zend/tests/bug69740.phpt new file mode 100644 index 0000000000..c8910b22d8 --- /dev/null +++ b/Zend/tests/bug69740.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #69740: finally in generator (yield) swallows exception in iteration +--FILE-- +<?php + +function generate() { + try { + yield 1; + yield 2; + } finally { + echo "finally\n"; + } +} + +foreach (generate() as $i) { + echo $i, "\n"; + throw new Exception(); +} + +?> +--EXPECTF-- +1 +finally + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/bug69754.phpt b/Zend/tests/bug69754.phpt new file mode 100644 index 0000000000..be55ae2b78 --- /dev/null +++ b/Zend/tests/bug69754.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #69754 (Use of ::class inside array causes compile error) +--FILE-- +<?php + +class Example { + public function test() { + var_dump(static::class); + var_dump(static::class . 'IsAwesome'); + var_dump(static::class . date('Ymd')); + var_dump([static::class]); + } +} + +(new Example)->test(); + +?> +--EXPECTF-- +string(7) "Example" +string(16) "ExampleIsAwesome" +string(15) "Example%d" +array(1) { + [0]=> + string(7) "Example" +} diff --git a/Zend/tests/bug69755.phpt b/Zend/tests/bug69755.phpt new file mode 100644 index 0000000000..67c0ed3383 --- /dev/null +++ b/Zend/tests/bug69755.phpt @@ -0,0 +1,8 @@ +--TEST-- +Bug #69755: segfault in ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDLER +--FILE-- +<?php +c . 10; +?> +--EXPECTF-- +Notice: Use of undefined constant c - assumed 'c' in %sbug69755.php on line 2 diff --git a/Zend/tests/bug69758.phpt b/Zend/tests/bug69758.phpt new file mode 100644 index 0000000000..f0b3588139 --- /dev/null +++ b/Zend/tests/bug69758.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #69758 (Item added to array not being removed by array_pop/shift) +--FILE-- +<?php +$tokens = array(); +$conditions = array(); +for ($i = 0; $i <= 10; $i++) { + $tokens[$i] = $conditions; + + // First integer must be less than 8 + // and second must be 8, 9 or 10 + if ($i !== 0 && $i !== 8) { + continue; + } + + // Add condition and then pop off straight away. + // Can also use array_shift() here. + $conditions[$i] = true; + $oldCondition = array_pop($conditions); +} + +// Conditions should be empty. +var_dump($conditions); +?> +--EXPECT-- +array(0) { +} diff --git a/Zend/tests/bug69761.phpt b/Zend/tests/bug69761.phpt new file mode 100644 index 0000000000..4b7e2787d8 --- /dev/null +++ b/Zend/tests/bug69761.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #69761 (Serialization of anonymous classes should be prevented) +--FILE-- +<?php +$instance = new class('foo') { + public function __construct($i) { + } +}; +var_dump(serialize($instance)); +?> +--EXPECTF-- +Fatal error: Uncaught Exception: Serialization of 'class@%s' is not allowed in %sbug69761.php:%d +Stack trace: +#0 %sbug69761.php(%d): serialize(Object(class@%s + thrown in %sbug69761.php on line %d diff --git a/Zend/tests/bug69767.phpt b/Zend/tests/bug69767.phpt new file mode 100644 index 0000000000..cf4d4e7f93 --- /dev/null +++ b/Zend/tests/bug69767.phpt @@ -0,0 +1,8 @@ +--TEST-- +Bug #69767 (Default parameter value with wrong type segfaults) +--FILE-- +<?php +function foo(String $bar = 0) {} +?> +--EXPECTF-- +Fatal error: Default value for parameters with a string type hint can only be string or NULL in %sbug69767.php on line %d diff --git a/Zend/tests/bug69788.phpt b/Zend/tests/bug69788.phpt new file mode 100644 index 0000000000..e48486625f --- /dev/null +++ b/Zend/tests/bug69788.phpt @@ -0,0 +1,8 @@ +--TEST-- +Bug #69788: Malformed script causes Uncaught EngineException in php-cgi, valgrind SIGILL +--FILE-- +<?php [t.[]]; ?> +--EXPECTF-- +Notice: Array to string conversion in %s on line %d + +Notice: Use of undefined constant t - assumed 't' in %s on line %d diff --git a/Zend/tests/bug69805.phpt b/Zend/tests/bug69805.phpt new file mode 100644 index 0000000000..c3ca62dc11 --- /dev/null +++ b/Zend/tests/bug69805.phpt @@ -0,0 +1,8 @@ +--TEST-- +Bug #69805 (null ptr deref and seg fault in zend_resolve_class_name) +--FILE-- +<?php +class p{public function c(){(0)::t;}}?> +?> +--EXPECTF-- +Fatal error: Illegal class name in %sbug69805.php on line %d diff --git a/Zend/tests/bug69825.phpt b/Zend/tests/bug69825.phpt new file mode 100644 index 0000000000..1349dee5ae --- /dev/null +++ b/Zend/tests/bug69825.phpt @@ -0,0 +1,30 @@ +--TEST-- +Bug #69825 (Short-circuiting failure) +--FILE-- +<?php + +print "AND\n"; +var_dump(0 && 1); +var_dump(0 && 0); +var_dump(1 && 0); +var_dump(1 && 1); + +print "OR\n"; +var_dump(0 || 1); +var_dump(0 || 0); +var_dump(1 || 0); +var_dump(1 || 1); + +?> +--EXPECT-- +AND +bool(false) +bool(false) +bool(false) +bool(true) +OR +bool(true) +bool(false) +bool(true) +bool(true) + diff --git a/Zend/tests/closure_027.phpt b/Zend/tests/closure_027.phpt index a56b78013c..db42ae9307 100644 --- a/Zend/tests/closure_027.phpt +++ b/Zend/tests/closure_027.phpt @@ -30,6 +30,6 @@ NULL Fatal error: Uncaught TypeError: Argument 1 passed to test() must be an instance of Closure, instance of stdClass given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): test() +#0 %s(%d): test(Object(stdClass)) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/gc_024.phpt b/Zend/tests/gc_024.phpt index 9a2ceb88f5..ca78da63d3 100644 --- a/Zend/tests/gc_024.phpt +++ b/Zend/tests/gc_024.phpt @@ -13,5 +13,5 @@ var_dump(gc_collect_cycles()); echo "ok\n"; ?> --EXPECT-- -int(1) +int(2) ok diff --git a/Zend/tests/grammar/regression_001.phpt b/Zend/tests/grammar/regression_001.phpt new file mode 100644 index 0000000000..73d5eacdf6 --- /dev/null +++ b/Zend/tests/grammar/regression_001.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test to check static method calls syntax regression +--FILE-- +<?php + +class Foo { + public static function function(){ echo __METHOD__, PHP_EOL; } +} + +Foo::function(); + +Foo:: +function(); + +Foo:: + function(); + + +Foo:: + function( + +); + +echo "\nDone\n"; + +--EXPECTF-- + +Foo::function +Foo::function +Foo::function +Foo::function + +Done diff --git a/Zend/tests/grammar/regression_002.phpt b/Zend/tests/grammar/regression_002.phpt new file mode 100644 index 0000000000..dd307c99d8 --- /dev/null +++ b/Zend/tests/grammar/regression_002.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test to ensure ::class still works +--FILE-- +<?php + +class Foo {} + +var_dump(Foo::class); + +var_dump(Foo:: class); + +var_dump(Foo:: CLASS); + +var_dump(Foo:: + +CLASS); + +--EXPECTF-- +string(3) "Foo" +string(3) "Foo" +string(3) "Foo" +string(3) "Foo" diff --git a/Zend/tests/grammar/regression_003.phpt b/Zend/tests/grammar/regression_003.phpt new file mode 100644 index 0000000000..e475754ccd --- /dev/null +++ b/Zend/tests/grammar/regression_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test to ensure ::class is still reserved in obj scope +--FILE-- +<?php + +class Obj +{ + const CLASS = 'class'; +} + +?> +--EXPECTF-- +Fatal error: A class constant must not be called 'class'; it is reserved for class name fetching in %s on line %d diff --git a/Zend/tests/grammar/regression_004.phpt b/Zend/tests/grammar/regression_004.phpt new file mode 100644 index 0000000000..e95674d8c9 --- /dev/null +++ b/Zend/tests/grammar/regression_004.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test possible function naming regression on procedural scope +--FILE-- +<?php + +class Obj +{ + function echo(){} // valid + function return(){} // valid +} + +function echo(){} // not valid + +--EXPECTF-- +Parse error: syntax error, unexpected 'echo' (T_ECHO), expecting identifier (T_STRING) or '(' in %s on line 9 diff --git a/Zend/tests/grammar/regression_005.phpt b/Zend/tests/grammar/regression_005.phpt new file mode 100644 index 0000000000..7704375d6e --- /dev/null +++ b/Zend/tests/grammar/regression_005.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test possible constant naming regression on procedural scope +--FILE-- +<?php + +class Obj +{ + const return = 'yep'; +} + +const return = 'nope'; + +--EXPECTF-- +Parse error: syntax error, unexpected 'return' (T_RETURN), expecting identifier (T_STRING) in %s on line 8 diff --git a/Zend/tests/grammar/regression_006.phpt b/Zend/tests/grammar/regression_006.phpt new file mode 100644 index 0000000000..6aae0ba24b --- /dev/null +++ b/Zend/tests/grammar/regression_006.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test to ensure const list syntax declaration works +--FILE-- +<?php + +class Obj +{ + const DECLARE = 'declare', + RETURN = 'return', + FUNCTION = 'function', + USE = 'use'; +} + +echo Obj::DECLARE, PHP_EOL; +echo Obj::RETURN, PHP_EOL; +echo Obj::FUNCTION, PHP_EOL; +echo Obj::USE, PHP_EOL; +echo Obj:: + + USE, PHP_EOL; +echo "\nDone\n"; + +--EXPECTF-- +declare +return +function +use +use + +Done diff --git a/Zend/tests/grammar/regression_007.phpt b/Zend/tests/grammar/regression_007.phpt new file mode 100644 index 0000000000..92b22531a4 --- /dev/null +++ b/Zend/tests/grammar/regression_007.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test to ensure semi reserved words allow deference +--FILE-- +<?php + +class Foo { + const use = 'yay'; + + public static function new() { + echo __METHOD__, PHP_EOL; + return new static(); + } + + public function self() { + echo __METHOD__, PHP_EOL; + return $this; + } +} + +Foo::new()::new()::new(); + +var_dump( + (new Foo)->self()::new()->self()->self()::use +); + +Foo::{'new'}(); + +var_dump(Foo::use); + +echo "\nDone\n"; + +--EXPECTF-- +Foo::new +Foo::new +Foo::new +Foo::self +Foo::new +Foo::self +Foo::self +string(3) "yay" +Foo::new +string(3) "yay" + +Done diff --git a/Zend/tests/grammar/regression_008.phpt b/Zend/tests/grammar/regression_008.phpt new file mode 100644 index 0000000000..7741ed036c --- /dev/null +++ b/Zend/tests/grammar/regression_008.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test to check regressions on string interpolation with class members access +--FILE-- +<?php + +class Friday { + public $require = "fun"; +} + +$friday = new Friday; + +echo "$friday->require ($friday->require) {$friday->require}", PHP_EOL; + +echo "\nDone\n"; + + +--EXPECTF-- + +fun (fun) fun + +Done diff --git a/Zend/tests/grammar/regression_009.phpt b/Zend/tests/grammar/regression_009.phpt new file mode 100644 index 0000000000..589d90316b --- /dev/null +++ b/Zend/tests/grammar/regression_009.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test to check regressions on use statements and lexer state +--FILE-- +<?php + +use A\B\C\D; + +class Foo +{ + private static $foo; + +} + +echo PHP_EOL, "Done", PHP_EOL; + +--EXPECTF-- + +Done diff --git a/Zend/tests/grammar/regression_010.phpt b/Zend/tests/grammar/regression_010.phpt new file mode 100644 index 0000000000..5dc90f288a --- /dev/null +++ b/Zend/tests/grammar/regression_010.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test to check regressions on T_IMPLEMENTS followed by a T_NS_SEPARATOR +--FILE-- +<?php + +interface A{} + +class B implements\A {} + +echo "Done", PHP_EOL; + +--EXPECTF-- + +Done diff --git a/Zend/tests/grammar/regression_011.phpt b/Zend/tests/grammar/regression_011.phpt new file mode 100644 index 0000000000..c79c077187 --- /dev/null +++ b/Zend/tests/grammar/regression_011.phpt @@ -0,0 +1,18 @@ +--TEST-- +Testing instantiation using namespace:: prefix +--FILE-- +<?php + +namespace foo; + +class bar { +} + +class_alias('foo\bar', 'foo\baz'); + +var_dump(new namespace\baz); + +?> +--EXPECTF-- +object(foo\bar)#%d (0) { +} diff --git a/Zend/tests/grammar/regression_012.phpt b/Zend/tests/grammar/regression_012.phpt new file mode 100644 index 0000000000..3b4925afa6 --- /dev/null +++ b/Zend/tests/grammar/regression_012.phpt @@ -0,0 +1,13 @@ +--TEST-- +Testing for regression on const list syntax and arrays +--FILE-- +<?php + +class A { + const A = [1, FOREACH]; +} + +?> +--EXPECTF-- + +Parse error: syntax error, unexpected 'FOREACH' (T_FOREACH), expecting ']' in %s on line %d diff --git a/Zend/tests/grammar/regression_013.phpt b/Zend/tests/grammar/regression_013.phpt new file mode 100644 index 0000000000..1c60ffc273 --- /dev/null +++ b/Zend/tests/grammar/regression_013.phpt @@ -0,0 +1,13 @@ +--TEST-- +Testing for regression with encapsed variables in class declaration context +--FILE-- +<?php + +class A { function foo() { "{${$a}}"; } function list() {} } + +echo "Done", PHP_EOL; + +?> +--EXPECTF-- + +Done diff --git a/Zend/tests/grammar/semi_reserved_001.phpt b/Zend/tests/grammar/semi_reserved_001.phpt new file mode 100644 index 0000000000..c6bfd46611 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_001.phpt @@ -0,0 +1,209 @@ +--TEST-- +Test semi-reserved words as class methods +--FILE-- +<?php + +class Obj +{ + function empty(){ echo __METHOD__, PHP_EOL; } + function callable(){ echo __METHOD__, PHP_EOL; } + function trait(){ echo __METHOD__, PHP_EOL; } + function extends(){ echo __METHOD__, PHP_EOL; } + function implements(){ echo __METHOD__, PHP_EOL; } + function const(){ echo __METHOD__, PHP_EOL; } + function enddeclare(){ echo __METHOD__, PHP_EOL; } + function endfor(){ echo __METHOD__, PHP_EOL; } + function endforeach(){ echo __METHOD__, PHP_EOL; } + function endif(){ echo __METHOD__, PHP_EOL; } + function endwhile(){ echo __METHOD__, PHP_EOL; } + function and(){ echo __METHOD__, PHP_EOL; } + function global(){ echo __METHOD__, PHP_EOL; } + function goto(){ echo __METHOD__, PHP_EOL; } + function instanceof(){ echo __METHOD__, PHP_EOL; } + function insteadof(){ echo __METHOD__, PHP_EOL; } + function interface(){ echo __METHOD__, PHP_EOL; } + function new(){ echo __METHOD__, PHP_EOL; } + function or(){ echo __METHOD__, PHP_EOL; } + function xor(){ echo __METHOD__, PHP_EOL; } + function try(){ echo __METHOD__, PHP_EOL; } + function use(){ echo __METHOD__, PHP_EOL; } + function var(){ echo __METHOD__, PHP_EOL; } + function exit(){ echo __METHOD__, PHP_EOL; } + function list(){ echo __METHOD__, PHP_EOL; } + function clone(){ echo __METHOD__, PHP_EOL; } + function include(){ echo __METHOD__, PHP_EOL; } + function include_once(){ echo __METHOD__, PHP_EOL; } + function throw(){ echo __METHOD__, PHP_EOL; } + function array(){ echo __METHOD__, PHP_EOL; } + function print(){ echo __METHOD__, PHP_EOL; } + function echo(){ echo __METHOD__, PHP_EOL; } + function require(){ echo __METHOD__, PHP_EOL; } + function require_once(){ echo __METHOD__, PHP_EOL; } + function return(){ echo __METHOD__, PHP_EOL; } + function else(){ echo __METHOD__, PHP_EOL; } + function elseif(){ echo __METHOD__, PHP_EOL; } + function default(){ echo __METHOD__, PHP_EOL; } + function break(){ echo __METHOD__, PHP_EOL; } + function continue(){ echo __METHOD__, PHP_EOL; } + function switch(){ echo __METHOD__, PHP_EOL; } + function yield(){ echo __METHOD__, PHP_EOL; } + function function(){ echo __METHOD__, PHP_EOL; } + function if(){ echo __METHOD__, PHP_EOL; } + function endswitch(){ echo __METHOD__, PHP_EOL; } + function finally(){ echo __METHOD__, PHP_EOL; } + function for(){ echo __METHOD__, PHP_EOL; } + function foreach(){ echo __METHOD__, PHP_EOL; } + function declare(){ echo __METHOD__, PHP_EOL; } + function case(){ echo __METHOD__, PHP_EOL; } + function do(){ echo __METHOD__, PHP_EOL; } + function while(){ echo __METHOD__, PHP_EOL; } + function as(){ echo __METHOD__, PHP_EOL; } + function catch(){ echo __METHOD__, PHP_EOL; } + function die(){ echo __METHOD__, PHP_EOL; } + function self(){ echo __METHOD__, PHP_EOL; } + function parent(){ echo __METHOD__, PHP_EOL; } + function public(){ echo __METHOD__, PHP_EOL; } + function protected(){ echo __METHOD__, PHP_EOL; } + function private(){ echo __METHOD__, PHP_EOL; } + function static(){ echo __METHOD__, PHP_EOL; } + function abstract(){ echo __METHOD__, PHP_EOL; } + function final(){ echo __METHOD__, PHP_EOL; } + function class(){ echo __METHOD__, PHP_EOL; } +} + +$obj = new Obj; + +$obj->empty(); +$obj->callable(); +$obj->trait(); +$obj->extends(); +$obj->implements(); +$obj->const(); +$obj->enddeclare(); +$obj->endfor(); +$obj->endforeach(); +$obj->endif(); +$obj->endwhile(); +$obj->and(); +$obj->global(); +$obj->goto(); +$obj->instanceof(); +$obj->insteadof(); +$obj->interface(); +$obj->new(); +$obj->or(); +$obj->xor(); +$obj->try(); +$obj->use(); +$obj->var(); +$obj->exit(); +$obj->list(); +$obj->clone(); +$obj->include(); +$obj->include_once(); +$obj->throw(); +$obj->array(); +$obj->print(); +$obj->echo(); +$obj->require(); +$obj->require_once(); +$obj->return(); +$obj->else(); +$obj->elseif(); +$obj->default(); +$obj->break(); +$obj->continue(); +$obj->switch(); +$obj->yield(); +$obj->function(); +$obj->if(); +$obj->endswitch(); +$obj->finally(); +$obj->for(); +$obj->foreach(); +$obj->declare(); +$obj->case(); +$obj->do(); +$obj->while(); +$obj->as(); +$obj->catch(); +$obj->die(); +$obj->self(); +$obj->parent(); +$obj->public(); +$obj->protected(); +$obj->private(); +$obj->static(); +$obj->abstract(); +$obj->final(); +$obj->class(); + +echo "\nDone\n"; + +--EXPECTF-- +Obj::empty +Obj::callable +Obj::trait +Obj::extends +Obj::implements +Obj::const +Obj::enddeclare +Obj::endfor +Obj::endforeach +Obj::endif +Obj::endwhile +Obj::and +Obj::global +Obj::goto +Obj::instanceof +Obj::insteadof +Obj::interface +Obj::new +Obj::or +Obj::xor +Obj::try +Obj::use +Obj::var +Obj::exit +Obj::list +Obj::clone +Obj::include +Obj::include_once +Obj::throw +Obj::array +Obj::print +Obj::echo +Obj::require +Obj::require_once +Obj::return +Obj::else +Obj::elseif +Obj::default +Obj::break +Obj::continue +Obj::switch +Obj::yield +Obj::function +Obj::if +Obj::endswitch +Obj::finally +Obj::for +Obj::foreach +Obj::declare +Obj::case +Obj::do +Obj::while +Obj::as +Obj::catch +Obj::die +Obj::self +Obj::parent +Obj::public +Obj::protected +Obj::private +Obj::static +Obj::abstract +Obj::final +Obj::class + +Done diff --git a/Zend/tests/grammar/semi_reserved_002.phpt b/Zend/tests/grammar/semi_reserved_002.phpt new file mode 100644 index 0000000000..b2c20028ca --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_002.phpt @@ -0,0 +1,207 @@ +--TEST-- +Test semi-reserved words as static class methods +--FILE-- +<?php + +class Obj +{ + static function empty(){ echo __METHOD__, PHP_EOL; } + static function callable(){ echo __METHOD__, PHP_EOL; } + static function trait(){ echo __METHOD__, PHP_EOL; } + static function extends(){ echo __METHOD__, PHP_EOL; } + static function implements(){ echo __METHOD__, PHP_EOL; } + static function const(){ echo __METHOD__, PHP_EOL; } + static function enddeclare(){ echo __METHOD__, PHP_EOL; } + static function endfor(){ echo __METHOD__, PHP_EOL; } + static function endforeach(){ echo __METHOD__, PHP_EOL; } + static function endif(){ echo __METHOD__, PHP_EOL; } + static function endwhile(){ echo __METHOD__, PHP_EOL; } + static function and(){ echo __METHOD__, PHP_EOL; } + static function global(){ echo __METHOD__, PHP_EOL; } + static function goto(){ echo __METHOD__, PHP_EOL; } + static function instanceof(){ echo __METHOD__, PHP_EOL; } + static function insteadof(){ echo __METHOD__, PHP_EOL; } + static function interface(){ echo __METHOD__, PHP_EOL; } + static function new(){ echo __METHOD__, PHP_EOL; } + static function or(){ echo __METHOD__, PHP_EOL; } + static function xor(){ echo __METHOD__, PHP_EOL; } + static function try(){ echo __METHOD__, PHP_EOL; } + static function use(){ echo __METHOD__, PHP_EOL; } + static function var(){ echo __METHOD__, PHP_EOL; } + static function exit(){ echo __METHOD__, PHP_EOL; } + static function list(){ echo __METHOD__, PHP_EOL; } + static function clone(){ echo __METHOD__, PHP_EOL; } + static function include(){ echo __METHOD__, PHP_EOL; } + static function include_once(){ echo __METHOD__, PHP_EOL; } + static function throw(){ echo __METHOD__, PHP_EOL; } + static function array(){ echo __METHOD__, PHP_EOL; } + static function print(){ echo __METHOD__, PHP_EOL; } + static function echo(){ echo __METHOD__, PHP_EOL; } + static function require(){ echo __METHOD__, PHP_EOL; } + static function require_once(){ echo __METHOD__, PHP_EOL; } + static function return(){ echo __METHOD__, PHP_EOL; } + static function else(){ echo __METHOD__, PHP_EOL; } + static function elseif(){ echo __METHOD__, PHP_EOL; } + static function default(){ echo __METHOD__, PHP_EOL; } + static function break(){ echo __METHOD__, PHP_EOL; } + static function continue(){ echo __METHOD__, PHP_EOL; } + static function switch(){ echo __METHOD__, PHP_EOL; } + static function yield(){ echo __METHOD__, PHP_EOL; } + static function function(){ echo __METHOD__, PHP_EOL; } + static function if(){ echo __METHOD__, PHP_EOL; } + static function endswitch(){ echo __METHOD__, PHP_EOL; } + static function finally(){ echo __METHOD__, PHP_EOL; } + static function for(){ echo __METHOD__, PHP_EOL; } + static function foreach(){ echo __METHOD__, PHP_EOL; } + static function declare(){ echo __METHOD__, PHP_EOL; } + static function case(){ echo __METHOD__, PHP_EOL; } + static function do(){ echo __METHOD__, PHP_EOL; } + static function while(){ echo __METHOD__, PHP_EOL; } + static function as(){ echo __METHOD__, PHP_EOL; } + static function catch(){ echo __METHOD__, PHP_EOL; } + static function die(){ echo __METHOD__, PHP_EOL; } + static function self(){ echo __METHOD__, PHP_EOL; } + static function parent(){ echo __METHOD__, PHP_EOL; } + static function public(){ echo __METHOD__, PHP_EOL; } + static function protected(){ echo __METHOD__, PHP_EOL; } + static function private(){ echo __METHOD__, PHP_EOL; } + static function static(){ echo __METHOD__, PHP_EOL; } + static function abstract(){ echo __METHOD__, PHP_EOL; } + static function final(){ echo __METHOD__, PHP_EOL; } + static function class(){ echo __METHOD__, PHP_EOL; } +} + +Obj::empty(); +Obj::callable(); +Obj::trait(); +Obj::extends(); +Obj::implements(); +Obj::const(); +Obj::enddeclare(); +Obj::endfor(); +Obj::endforeach(); +Obj::endif(); +Obj::endwhile(); +Obj::and(); +Obj::global(); +Obj::goto(); +Obj::instanceof(); +Obj::insteadof(); +Obj::interface(); +Obj::new(); +Obj::or(); +Obj::xor(); +Obj::try(); +Obj::use(); +Obj::var(); +Obj::exit(); +Obj::list(); +Obj::clone(); +Obj::include(); +Obj::include_once(); +Obj::throw(); +Obj::array(); +Obj::print(); +Obj::echo(); +Obj::require(); +Obj::require_once(); +Obj::return(); +Obj::else(); +Obj::elseif(); +Obj::default(); +Obj::break(); +Obj::continue(); +Obj::switch(); +Obj::yield(); +Obj::function(); +Obj::if(); +Obj::endswitch(); +Obj::finally(); +Obj::for(); +Obj::foreach(); +Obj::declare(); +Obj::case(); +Obj::do(); +Obj::while(); +Obj::as(); +Obj::catch(); +Obj::die(); +Obj::self(); +Obj::parent(); +Obj::public(); +Obj::protected(); +Obj::private(); +Obj::static(); +Obj::abstract(); +Obj::final(); +Obj::class(); + +echo "\nDone\n"; + +--EXPECTF-- +Obj::empty +Obj::callable +Obj::trait +Obj::extends +Obj::implements +Obj::const +Obj::enddeclare +Obj::endfor +Obj::endforeach +Obj::endif +Obj::endwhile +Obj::and +Obj::global +Obj::goto +Obj::instanceof +Obj::insteadof +Obj::interface +Obj::new +Obj::or +Obj::xor +Obj::try +Obj::use +Obj::var +Obj::exit +Obj::list +Obj::clone +Obj::include +Obj::include_once +Obj::throw +Obj::array +Obj::print +Obj::echo +Obj::require +Obj::require_once +Obj::return +Obj::else +Obj::elseif +Obj::default +Obj::break +Obj::continue +Obj::switch +Obj::yield +Obj::function +Obj::if +Obj::endswitch +Obj::finally +Obj::for +Obj::foreach +Obj::declare +Obj::case +Obj::do +Obj::while +Obj::as +Obj::catch +Obj::die +Obj::self +Obj::parent +Obj::public +Obj::protected +Obj::private +Obj::static +Obj::abstract +Obj::final +Obj::class + +Done diff --git a/Zend/tests/grammar/semi_reserved_003.phpt b/Zend/tests/grammar/semi_reserved_003.phpt new file mode 100644 index 0000000000..fe2c44dc4c --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_003.phpt @@ -0,0 +1,210 @@ +--TEST-- +Test semi-reserved words as class properties +--FILE-- +<?php + +class Obj +{ + var $empty = 'empty'; + var $callable = 'callable'; + var $class = 'class'; + var $trait = 'trait'; + var $extends = 'extends'; + var $implements = 'implements'; + var $static = 'static'; + var $abstract = 'abstract'; + var $final = 'final'; + var $public = 'public'; + var $protected = 'protected'; + var $private = 'private'; + var $const = 'const'; + var $enddeclare = 'enddeclare'; + var $endfor = 'endfor'; + var $endforeach = 'endforeach'; + var $endif = 'endif'; + var $endwhile = 'endwhile'; + var $and = 'and'; + var $global = 'global'; + var $goto = 'goto'; + var $instanceof = 'instanceof'; + var $insteadof = 'insteadof'; + var $interface = 'interface'; + var $namespace = 'namespace'; + var $new = 'new'; + var $or = 'or'; + var $xor = 'xor'; + var $try = 'try'; + var $use = 'use'; + var $var = 'var'; + var $exit = 'exit'; + var $list = 'list'; + var $clone = 'clone'; + var $include = 'include'; + var $include_once = 'include_once'; + var $throw = 'throw'; + var $array = 'array'; + var $print = 'print'; + var $echo = 'echo'; + var $require = 'require'; + var $require_once = 'require_once'; + var $return = 'return'; + var $else = 'else'; + var $elseif = 'elseif'; + var $default = 'default'; + var $break = 'break'; + var $continue = 'continue'; + var $switch = 'switch'; + var $yield = 'yield'; + var $function = 'function'; + var $if = 'if'; + var $endswitch = 'endswitch'; + var $finally = 'finally'; + var $for = 'for'; + var $foreach = 'foreach'; + var $declare = 'declare'; + var $case = 'case'; + var $do = 'do'; + var $while = 'while'; + var $as = 'as'; + var $catch = 'catch'; + var $die = 'die'; + var $self = 'self'; +} + +$obj = new Obj; + +echo $obj->empty, PHP_EOL; +echo $obj->callable, PHP_EOL; +echo $obj->class, PHP_EOL; +echo $obj->trait, PHP_EOL; +echo $obj->extends, PHP_EOL; +echo $obj->implements, PHP_EOL; +echo $obj->static, PHP_EOL; +echo $obj->abstract, PHP_EOL; +echo $obj->final, PHP_EOL; +echo $obj->public, PHP_EOL; +echo $obj->protected, PHP_EOL; +echo $obj->private, PHP_EOL; +echo $obj->const, PHP_EOL; +echo $obj->enddeclare, PHP_EOL; +echo $obj->endfor, PHP_EOL; +echo $obj->endforeach, PHP_EOL; +echo $obj->endif, PHP_EOL; +echo $obj->endwhile, PHP_EOL; +echo $obj->and, PHP_EOL; +echo $obj->global, PHP_EOL; +echo $obj->goto, PHP_EOL; +echo $obj->instanceof, PHP_EOL; +echo $obj->insteadof, PHP_EOL; +echo $obj->interface, PHP_EOL; +echo $obj->namespace, PHP_EOL; +echo $obj->new, PHP_EOL; +echo $obj->or, PHP_EOL; +echo $obj->xor, PHP_EOL; +echo $obj->try, PHP_EOL; +echo $obj->use, PHP_EOL; +echo $obj->var, PHP_EOL; +echo $obj->exit, PHP_EOL; +echo $obj->list, PHP_EOL; +echo $obj->clone, PHP_EOL; +echo $obj->include, PHP_EOL; +echo $obj->include_once, PHP_EOL; +echo $obj->throw, PHP_EOL; +echo $obj->array, PHP_EOL; +echo $obj->print, PHP_EOL; +echo $obj->echo, PHP_EOL; +echo $obj->require, PHP_EOL; +echo $obj->require_once, PHP_EOL; +echo $obj->return, PHP_EOL; +echo $obj->else, PHP_EOL; +echo $obj->elseif, PHP_EOL; +echo $obj->default, PHP_EOL; +echo $obj->break, PHP_EOL; +echo $obj->continue, PHP_EOL; +echo $obj->switch, PHP_EOL; +echo $obj->yield, PHP_EOL; +echo $obj->function, PHP_EOL; +echo $obj->if, PHP_EOL; +echo $obj->endswitch, PHP_EOL; +echo $obj->finally, PHP_EOL; +echo $obj->for, PHP_EOL; +echo $obj->foreach, PHP_EOL; +echo $obj->declare, PHP_EOL; +echo $obj->case, PHP_EOL; +echo $obj->do, PHP_EOL; +echo $obj->while, PHP_EOL; +echo $obj->as, PHP_EOL; +echo $obj->catch, PHP_EOL; +echo $obj->die, PHP_EOL; +echo $obj->self, PHP_EOL; + +echo "\nDone\n"; + +?> +--EXPECTF-- +empty +callable +class +trait +extends +implements +static +abstract +final +public +protected +private +const +enddeclare +endfor +endforeach +endif +endwhile +and +global +goto +instanceof +insteadof +interface +namespace +new +or +xor +try +use +var +exit +list +clone +include +include_once +throw +array +print +echo +require +require_once +return +else +elseif +default +break +continue +switch +yield +function +if +endswitch +finally +for +foreach +declare +case +do +while +as +catch +die +self + +Done diff --git a/Zend/tests/grammar/semi_reserved_004.phpt b/Zend/tests/grammar/semi_reserved_004.phpt new file mode 100644 index 0000000000..40c5df14ef --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_004.phpt @@ -0,0 +1,210 @@ +--TEST-- +Test semi-reserved words as static class properties +--FILE-- +<?php + +class Obj +{ + static $empty = 'empty'; + static $callable = 'callable'; + static $class = 'class'; + static $trait = 'trait'; + static $extends = 'extends'; + static $implements = 'implements'; + static $static = 'static'; + static $abstract = 'abstract'; + static $final = 'final'; + static $public = 'public'; + static $protected = 'protected'; + static $private = 'private'; + static $const = 'const'; + static $enddeclare = 'enddeclare'; + static $endfor = 'endfor'; + static $endforeach = 'endforeach'; + static $endif = 'endif'; + static $endwhile = 'endwhile'; + static $and = 'and'; + static $global = 'global'; + static $goto = 'goto'; + static $instanceof = 'instanceof'; + static $insteadof = 'insteadof'; + static $interface = 'interface'; + static $namespace = 'namespace'; + static $new = 'new'; + static $or = 'or'; + static $xor = 'xor'; + static $try = 'try'; + static $use = 'use'; + static $var = 'var'; + static $exit = 'exit'; + static $list = 'list'; + static $clone = 'clone'; + static $include = 'include'; + static $include_once = 'include_once'; + static $throw = 'throw'; + static $array = 'array'; + static $print = 'print'; + static $echo = 'echo'; + static $require = 'require'; + static $require_once = 'require_once'; + static $return = 'return'; + static $else = 'else'; + static $elseif = 'elseif'; + static $default = 'default'; + static $break = 'break'; + static $continue = 'continue'; + static $switch = 'switch'; + static $yield = 'yield'; + static $function = 'function'; + static $if = 'if'; + static $endswitch = 'endswitch'; + static $finally = 'finally'; + static $for = 'for'; + static $foreach = 'foreach'; + static $declare = 'declare'; + static $case = 'case'; + static $do = 'do'; + static $while = 'while'; + static $as = 'as'; + static $catch = 'catch'; + static $die = 'die'; + static $self = 'self'; + static $parent = 'parent'; +} + +echo Obj::$empty, PHP_EOL; +echo Obj::$callable, PHP_EOL; +echo Obj::$class, PHP_EOL; +echo Obj::$trait, PHP_EOL; +echo Obj::$extends, PHP_EOL; +echo Obj::$implements, PHP_EOL; +echo Obj::$static, PHP_EOL; +echo Obj::$abstract, PHP_EOL; +echo Obj::$final, PHP_EOL; +echo Obj::$public, PHP_EOL; +echo Obj::$protected, PHP_EOL; +echo Obj::$private, PHP_EOL; +echo Obj::$const, PHP_EOL; +echo Obj::$enddeclare, PHP_EOL; +echo Obj::$endfor, PHP_EOL; +echo Obj::$endforeach, PHP_EOL; +echo Obj::$endif, PHP_EOL; +echo Obj::$endwhile, PHP_EOL; +echo Obj::$and, PHP_EOL; +echo Obj::$global, PHP_EOL; +echo Obj::$goto, PHP_EOL; +echo Obj::$instanceof, PHP_EOL; +echo Obj::$insteadof, PHP_EOL; +echo Obj::$interface, PHP_EOL; +echo Obj::$namespace, PHP_EOL; +echo Obj::$new, PHP_EOL; +echo Obj::$or, PHP_EOL; +echo Obj::$xor, PHP_EOL; +echo Obj::$try, PHP_EOL; +echo Obj::$use, PHP_EOL; +echo Obj::$var, PHP_EOL; +echo Obj::$exit, PHP_EOL; +echo Obj::$list, PHP_EOL; +echo Obj::$clone, PHP_EOL; +echo Obj::$include, PHP_EOL; +echo Obj::$include_once, PHP_EOL; +echo Obj::$throw, PHP_EOL; +echo Obj::$array, PHP_EOL; +echo Obj::$print, PHP_EOL; +echo Obj::$echo, PHP_EOL; +echo Obj::$require, PHP_EOL; +echo Obj::$require_once, PHP_EOL; +echo Obj::$return, PHP_EOL; +echo Obj::$else, PHP_EOL; +echo Obj::$elseif, PHP_EOL; +echo Obj::$default, PHP_EOL; +echo Obj::$break, PHP_EOL; +echo Obj::$continue, PHP_EOL; +echo Obj::$switch, PHP_EOL; +echo Obj::$yield, PHP_EOL; +echo Obj::$function, PHP_EOL; +echo Obj::$if, PHP_EOL; +echo Obj::$endswitch, PHP_EOL; +echo Obj::$finally, PHP_EOL; +echo Obj::$for, PHP_EOL; +echo Obj::$foreach, PHP_EOL; +echo Obj::$declare, PHP_EOL; +echo Obj::$case, PHP_EOL; +echo Obj::$do, PHP_EOL; +echo Obj::$while, PHP_EOL; +echo Obj::$as, PHP_EOL; +echo Obj::$catch, PHP_EOL; +echo Obj::$die, PHP_EOL; +echo Obj::$self, PHP_EOL; +echo Obj::$parent, PHP_EOL; + +echo "\nDone\n"; + +--EXPECTF-- +empty +callable +class +trait +extends +implements +static +abstract +final +public +protected +private +const +enddeclare +endfor +endforeach +endif +endwhile +and +global +goto +instanceof +insteadof +interface +namespace +new +or +xor +try +use +var +exit +list +clone +include +include_once +throw +array +print +echo +require +require_once +return +else +elseif +default +break +continue +switch +yield +function +if +endswitch +finally +for +foreach +declare +case +do +while +as +catch +die +self +parent + +Done diff --git a/Zend/tests/grammar/semi_reserved_005.phpt b/Zend/tests/grammar/semi_reserved_005.phpt new file mode 100644 index 0000000000..3ad0830b09 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_005.phpt @@ -0,0 +1,207 @@ +--TEST-- +Test semi-reserved words as class constants +--FILE-- +<?php + +class Obj +{ + const EMPTY = 'empty'; + const CALLABLE = 'callable'; + const TRAIT = 'trait'; + const EXTENDS = 'extends'; + const IMPLEMENTS = 'implements'; + const CONST = 'const'; + const ENDDECLARE = 'enddeclare'; + const ENDFOR = 'endfor'; + const ENDFOREACH = 'endforeach'; + const ENDIF = 'endif'; + const ENDWHILE = 'endwhile'; + const AND = 'and'; + const GLOBAL = 'global'; + const GOTO = 'goto'; + const INSTANCEOF = 'instanceof'; + const INSTEADOF = 'insteadof'; + const INTERFACE = 'interface'; + const NAMESPACE = 'namespace'; + const NEW = 'new'; + const OR = 'or'; + const XOR = 'xor'; + const TRY = 'try'; + const USE = 'use'; + const VAR = 'var'; + const EXIT = 'exit'; + const LIST = 'list'; + const CLONE = 'clone'; + const INCLUDE = 'include'; + const INCLUDE_ONCE = 'include_once'; + const THROW = 'throw'; + const ARRAY = 'array'; + const PRINT = 'print'; + const ECHO = 'echo'; + const REQUIRE = 'require'; + const REQUIRE_ONCE = 'require_once'; + const RETURN = 'return'; + const ELSE = 'else'; + const ELSEIF = 'elseif'; + const DEFAULT = 'default'; + const BREAK = 'break'; + const CONTINUE = 'continue'; + const SWITCH = 'switch'; + const YIELD = 'yield'; + const FUNCTION = 'function'; + const IF = 'if'; + const ENDSWITCH = 'endswitch'; + const FINALLY = 'finally'; + const FOR = 'for'; + const FOREACH = 'foreach'; + const DECLARE = 'declare'; + const CASE = 'case'; + const DO = 'do'; + const WHILE = 'while'; + const AS = 'as'; + const CATCH = 'catch'; + const DIE = 'die'; + const SELF = 'self'; + const PARENT = 'parent'; + const PUBLIC = 'public'; + const PROTECTED = 'protected'; + const PRIVATE = 'private'; + const STATIC = 'static'; + const ABSTRACT = 'abstract'; + const FINAL = 'final'; +} + +echo Obj::EMPTY, PHP_EOL; +echo Obj::CALLABLE, PHP_EOL; +echo Obj::TRAIT, PHP_EOL; +echo Obj::EXTENDS, PHP_EOL; +echo Obj::IMPLEMENTS, PHP_EOL; +echo Obj::CONST, PHP_EOL; +echo Obj::ENDDECLARE, PHP_EOL; +echo Obj::ENDFOR, PHP_EOL; +echo Obj::ENDFOREACH, PHP_EOL; +echo Obj::ENDIF, PHP_EOL; +echo Obj::ENDWHILE, PHP_EOL; +echo Obj::AND, PHP_EOL; +echo Obj::GLOBAL, PHP_EOL; +echo Obj::GOTO, PHP_EOL; +echo Obj::INSTANCEOF, PHP_EOL; +echo Obj::INSTEADOF, PHP_EOL; +echo Obj::INTERFACE, PHP_EOL; +echo Obj::NAMESPACE, PHP_EOL; +echo Obj::NEW, PHP_EOL; +echo Obj::OR, PHP_EOL; +echo Obj::XOR, PHP_EOL; +echo Obj::TRY, PHP_EOL; +echo Obj::USE, PHP_EOL; +echo Obj::VAR, PHP_EOL; +echo Obj::EXIT, PHP_EOL; +echo Obj::LIST, PHP_EOL; +echo Obj::CLONE, PHP_EOL; +echo Obj::INCLUDE, PHP_EOL; +echo Obj::INCLUDE_ONCE, PHP_EOL; +echo Obj::THROW, PHP_EOL; +echo Obj::ARRAY, PHP_EOL; +echo Obj::PRINT, PHP_EOL; +echo Obj::ECHO, PHP_EOL; +echo Obj::REQUIRE, PHP_EOL; +echo Obj::REQUIRE_ONCE, PHP_EOL; +echo Obj::RETURN, PHP_EOL; +echo Obj::ELSE, PHP_EOL; +echo Obj::ELSEIF, PHP_EOL; +echo Obj::DEFAULT, PHP_EOL; +echo Obj::BREAK, PHP_EOL; +echo Obj::CONTINUE, PHP_EOL; +echo Obj::SWITCH, PHP_EOL; +echo Obj::YIELD, PHP_EOL; +echo Obj::FUNCTION, PHP_EOL; +echo Obj::IF, PHP_EOL; +echo Obj::ENDSWITCH, PHP_EOL; +echo Obj::FINALLY, PHP_EOL; +echo Obj::FOR, PHP_EOL; +echo Obj::FOREACH, PHP_EOL; +echo Obj::DECLARE, PHP_EOL; +echo Obj::CASE, PHP_EOL; +echo Obj::DO, PHP_EOL; +echo Obj::WHILE, PHP_EOL; +echo Obj::AS, PHP_EOL; +echo Obj::CATCH, PHP_EOL; +echo Obj::DIE, PHP_EOL; +echo Obj::SELF, PHP_EOL; +echo Obj::PARENT, PHP_EOL; +echo Obj::PUBLIC, PHP_EOL; +echo Obj::PROTECTED, PHP_EOL; +echo Obj::PRIVATE, PHP_EOL; +echo Obj::STATIC, PHP_EOL; +echo Obj::ABSTRACT, PHP_EOL; +echo Obj::FINAL, PHP_EOL; + +echo "\nDone\n"; + +--EXPECTF-- +empty +callable +trait +extends +implements +const +enddeclare +endfor +endforeach +endif +endwhile +and +global +goto +instanceof +insteadof +interface +namespace +new +or +xor +try +use +var +exit +list +clone +include +include_once +throw +array +print +echo +require +require_once +return +else +elseif +default +break +continue +switch +yield +function +if +endswitch +finally +for +foreach +declare +case +do +while +as +catch +die +self +parent +public +protected +private +static +abstract +final + +Done diff --git a/Zend/tests/grammar/semi_reserved_006.phpt b/Zend/tests/grammar/semi_reserved_006.phpt new file mode 100644 index 0000000000..334d09ac36 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_006.phpt @@ -0,0 +1,80 @@ +--TEST-- +Test semi-reserved method and constant names and trait conflict resolution +--FILE-- +<?php + +trait TraitA +{ + public function catch(){ echo __METHOD__, PHP_EOL; } + private function list(){ echo __METHOD__, PHP_EOL; } +} + +trait TraitB +{ + static $list = ['a' => ['b' => ['c']]]; + + public static function catch(){ echo __METHOD__, PHP_EOL; } + private static function throw(){ echo __METHOD__, PHP_EOL; } + private static function self(){ echo __METHOD__, PHP_EOL; } +} + +trait TraitC +{ + public static function exit(){ echo __METHOD__, PHP_EOL; } + protected static function try(){ echo __METHOD__, PHP_EOL; } +} + +class Foo +{ + use TraitA, TraitB { + TraitA + :: + catch insteadof namespace\TraitB; + TraitA::list as public foreach; + TraitB::throw as public; + TraitB::self as public; + } + + use TraitC { + try as public attempt; + exit as die; + \TraitC::exit as bye; + namespace\TraitC::exit as byebye; + TraitC + :: + exit as farewell; + } +} + +(new Foo)->catch(); +(new Foo)->foreach(); +Foo::throw(); +Foo::self(); +var_dump(Foo::$list['a']); +Foo::attempt(); +Foo::die(); +Foo::bye(); +Foo::byebye(); +Foo::farewell(); + +echo "\nDone\n"; + +--EXPECTF-- +TraitA::catch +TraitA::list +TraitB::throw +TraitB::self +array(1) { + ["b"]=> + array(1) { + [0]=> + string(1) "c" + } +} +TraitC::try +TraitC::exit +TraitC::exit +TraitC::exit +TraitC::exit + +Done diff --git a/Zend/tests/grammar/semi_reserved_007.phpt b/Zend/tests/grammar/semi_reserved_007.phpt new file mode 100644 index 0000000000..5105629cbe --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_007.phpt @@ -0,0 +1,37 @@ +--TEST-- +Edge case: self::self, self::parent, parent::self semi reserved constants access +--FILE-- +<?php + +class Foo { + const self = "self"; + const parent = "parent"; + public function __construct() { + echo "From ", __METHOD__, ":", PHP_EOL; + echo self::self, PHP_EOL; + echo self::parent, PHP_EOL; + } +} + +class Bar extends Foo { + public function __construct() { + parent::__construct(); + echo "From ", __METHOD__, ":", PHP_EOL; + echo parent::self, PHP_EOL; + echo parent::parent, PHP_EOL; + } +} + +new Bar; + +echo "\nDone\n"; + +--EXPECTF-- +From Foo::__construct: +self +parent +From Bar::__construct: +self +parent + +Done
\ No newline at end of file diff --git a/Zend/tests/grammar/semi_reserved_008.phpt b/Zend/tests/grammar/semi_reserved_008.phpt new file mode 100644 index 0000000000..43218b1b05 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_008.phpt @@ -0,0 +1,68 @@ +--TEST-- +Testing with comments around semi-reserved names (not intended to be legible) +--FILE-- +<?php + +trait TraitA +{ + public static function list(){ echo __METHOD__, PHP_EOL; } + public static function /* comment */ catch(){ echo __METHOD__, PHP_EOL; } + private static function // comment + throw(){ echo __METHOD__, PHP_EOL; } + private static function + # comment + self(){ echo __METHOD__, PHP_EOL; } +} + +trait TraitB +{ + public static function exit(){ echo __METHOD__, PHP_EOL; } + protected static function try(){ echo __METHOD__, PHP_EOL; } +} + +class Foo +{ + use TraitA { + TraitA:: + // + /** doc comment */ + # + catch /* comment */ + // comment + # comment + insteadof TraitB; + + TraitA::list as public /**/ foreach; + } + + use TraitB { + try /*comment*/ as public attempt; + exit // comment + as/*comment*/die; // non qualified + \TraitB::exit as bye; // full qualified + namespace\TraitB::exit # + as byebye; // even more full qualified + TraitB + :: + /** */ + exit as farewell; // full qualified with weird spacing + } +} + +Foo /**/ +# +// +/** */ +:: +/**/ +# +// +/** */ +attempt(); + +echo PHP_EOL, "Done", PHP_EOL; + +--EXPECTF-- +TraitB::try + +Done diff --git a/Zend/tests/grammar/semi_reserved_009.phpt b/Zend/tests/grammar/semi_reserved_009.phpt new file mode 100644 index 0000000000..1a7b0fc371 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_009.phpt @@ -0,0 +1,25 @@ +--TEST-- +Edge case: T_STRING<as> as T_STRING<?> +--FILE-- +<?php + +trait TraitA +{ + public static function as(){ echo __METHOD__, PHP_EOL; } +} + +class Foo +{ + use TraitA { + as as try; + } +} + +Foo::try(); + +echo PHP_EOL, "Done", PHP_EOL; + +--EXPECTF-- +TraitA::as + +Done diff --git a/Zend/tests/grammar/semi_reserved_010.phpt b/Zend/tests/grammar/semi_reserved_010.phpt new file mode 100644 index 0000000000..508a7867a4 --- /dev/null +++ b/Zend/tests/grammar/semi_reserved_010.phpt @@ -0,0 +1,31 @@ +--TEST-- +Edge case: T_STRING<insteadof> insteadof T_STRING<?> +--FILE-- +<?php + +trait TraitA +{ + public static function insteadof(){ echo __METHOD__, PHP_EOL; } +} + +trait TraitB +{ + public static function insteadof(){ echo __METHOD__, PHP_EOL; } +} + +class Foo +{ + use TraitA , TraitB { + TraitB::insteadof + insteadof TraitA; + } +} + +Foo::insteadof(); + +echo PHP_EOL, "Done", PHP_EOL; + +--EXPECTF-- +TraitB::insteadof + +Done diff --git a/Zend/tests/loop_free_on_return.phpt b/Zend/tests/loop_free_on_return.phpt new file mode 100644 index 0000000000..525a50955a --- /dev/null +++ b/Zend/tests/loop_free_on_return.phpt @@ -0,0 +1,16 @@ +--TEST-- +Break out of while loop that is followed by a return statement and inside a foreach loop +--FILE-- +<?php + +$a = [42]; +foreach ($a as $b) { + while (1) { + break 2; + } + return; +} +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/ns_071.phpt b/Zend/tests/ns_071.phpt index 53ff7f018a..2f2fcfad1a 100644 --- a/Zend/tests/ns_071.phpt +++ b/Zend/tests/ns_071.phpt @@ -20,6 +20,6 @@ NULL Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must be of the type array, object given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): foo\bar->__construct() +#0 %s(%d): foo\bar->__construct(Object(stdClass)) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/ns_072.phpt b/Zend/tests/ns_072.phpt index 877095df4a..6375682890 100644 --- a/Zend/tests/ns_072.phpt +++ b/Zend/tests/ns_072.phpt @@ -32,6 +32,6 @@ NULL Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must implement interface foo\foo, instance of stdClass given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): foo\bar->__construct() +#0 %s(%d): foo\bar->__construct(Object(stdClass)) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/objects_022.phpt b/Zend/tests/objects_022.phpt index 913de7d990..01f961bae1 100644 --- a/Zend/tests/objects_022.phpt +++ b/Zend/tests/objects_022.phpt @@ -38,6 +38,6 @@ object(baz)#%d (0) { Fatal error: Uncaught TypeError: Argument 1 passed to foo::testFoo() must be an instance of foo, instance of stdClass given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): foo->testFoo() +#0 %s(%d): foo->testFoo(Object(stdClass)) #1 {main} thrown in %s on line %d diff --git a/Zend/tests/return_types/028.phpt b/Zend/tests/return_types/028.phpt new file mode 100644 index 0000000000..d2a78ede4a --- /dev/null +++ b/Zend/tests/return_types/028.phpt @@ -0,0 +1,20 @@ +--TEST-- +Memory leak when returning TMP/VAR with wrong return type +--FILE-- +<?php + +function foo(): stdClass { + $a = new stdClass; + $b = []; + return [$a, $b]; +} + +try { + foo(); +} catch (Error $e) { + print $e->getMessage(); +} + +?> +--EXPECTF-- +Return value of foo() must be an instance of stdClass, array returned in %s on line %d diff --git a/Zend/tests/typehints/explicit_weak_include_strict.phpt b/Zend/tests/typehints/explicit_weak_include_strict.phpt index 1593c70950..fb53d8ce11 100644 --- a/Zend/tests/typehints/explicit_weak_include_strict.phpt +++ b/Zend/tests/typehints/explicit_weak_include_strict.phpt @@ -13,7 +13,7 @@ require 'weak_include_strict_2.inc'; --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sweak_include_strict_2.inc on line 9 and defined in %sweak_include_strict_2.inc:5 Stack trace: -#0 %s(%d): takes_int() +#0 %s(%d): takes_int(1) #1 %s(%d): require('%s') #2 {main} thrown in %sweak_include_strict_2.inc on line 5 diff --git a/Zend/tests/typehints/strict_call_weak.phpt b/Zend/tests/typehints/strict_call_weak.phpt index 8ebed2216b..e3a606ec12 100644 --- a/Zend/tests/typehints/strict_call_weak.phpt +++ b/Zend/tests/typehints/strict_call_weak.phpt @@ -15,7 +15,7 @@ function_declared_in_weak_mode(1.0); --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to function_declared_in_weak_mode() must be of the type integer, float given, called in %sstrict_call_weak.php on line 10 and defined in %sstrict_call_weak_2.inc:5 Stack trace: -#0 %s(%d): function_declared_in_weak_mode() +#0 %s(%d): function_declared_in_weak_mode(1) #1 {main} thrown in %sstrict_call_weak_2.inc on line 5 diff --git a/Zend/tests/typehints/strict_call_weak_explicit.phpt b/Zend/tests/typehints/strict_call_weak_explicit.phpt index 215a0b1fcb..22c5b4319c 100644 --- a/Zend/tests/typehints/strict_call_weak_explicit.phpt +++ b/Zend/tests/typehints/strict_call_weak_explicit.phpt @@ -15,7 +15,7 @@ function_declared_in_weak_mode(1.0); --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to function_declared_in_weak_mode() must be of the type integer, float given, called in %sstrict_call_weak_explicit.php on line 10 and defined in %sstrict_call_weak_explicit_2.inc:5 Stack trace: -#0 %s(%d): function_declared_in_weak_mode() +#0 %s(%d): function_declared_in_weak_mode(1) #1 {main} thrown in %sstrict_call_weak_explicit_2.inc on line 5 diff --git a/Zend/tests/typehints/weak_include_strict.phpt b/Zend/tests/typehints/weak_include_strict.phpt index 5cd1895d66..e49485dbe5 100644 --- a/Zend/tests/typehints/weak_include_strict.phpt +++ b/Zend/tests/typehints/weak_include_strict.phpt @@ -13,7 +13,7 @@ require 'weak_include_strict_2.inc'; --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to takes_int() must be of the type integer, float given, called in %sweak_include_strict_2.inc on line 9 and defined in %sweak_include_strict_2.inc:5 Stack trace: -#0 %s(%d): takes_int() +#0 %s(%d): takes_int(1) #1 %s(%d): require('%s') #2 {main} thrown in %sweak_include_strict_2.inc on line 5 diff --git a/Zend/tests/variadic/closure_invoke.phpt b/Zend/tests/variadic/closure_invoke.phpt new file mode 100644 index 0000000000..6641ff8fb0 --- /dev/null +++ b/Zend/tests/variadic/closure_invoke.phpt @@ -0,0 +1,17 @@ +--TEST-- +Closure::__invoke() with variadic parameter +--FILE-- +<?php + +$closure = function(&...$refs) {}; +$closure->__invoke( + $v1, $v2, $v3, $v4, + $v5, $v6, $v7, $v8, + $v9, $v10, $v11, $v12, + $v13 +); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/variadic/typehint_error.phpt b/Zend/tests/variadic/typehint_error.phpt index 9f9a97bc56..26842bbcc2 100644 --- a/Zend/tests/variadic/typehint_error.phpt +++ b/Zend/tests/variadic/typehint_error.phpt @@ -35,6 +35,6 @@ array(3) { Fatal error: Uncaught TypeError: Argument 3 passed to test() must be of the type array, integer given, called in %s:%d Stack trace: -#0 %s(%d): test(Array, Array) +#0 %s(%d): test(Array, Array, 2) #1 {main} thrown in %s on line %d diff --git a/Zend/zend.c b/Zend/zend.c index 1c2cfd6c77..7fd6f7fc7b 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -727,6 +727,9 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) / ini_scanner_globals_ctor(&ini_scanner_globals); php_scanner_globals_ctor(&language_scanner_globals); zend_set_default_compile_time_values(); +#ifdef ZEND_WIN32 + zend_get_windows_version_info(&EG(windows_version_info)); +#endif #endif EG(error_reporting) = E_ALL & ~E_NOTICE; @@ -798,9 +801,6 @@ void zend_post_startup(void) /* {{{ */ global_persistent_list = &EG(persistent_list); zend_copy_ini_directives(); #else -#ifdef ZEND_WIN32 - zend_get_windows_version_info(&EG(windows_version_info)); -#endif virtual_cwd_deactivate(); #endif } diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 7f8587ab4f..f49957a7ff 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -476,7 +476,7 @@ ZEND_API int ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest zval_dtor(arg); ZVAL_NULL(arg); if (!zend_make_printable_zval(z, arg)) { - ZVAL_ZVAL(arg, z, 1, 1); + ZVAL_COPY_VALUE(arg, z); } *dest = Z_STR_P(arg); return 1; @@ -1118,104 +1118,84 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */ } /* }}} */ -static int zval_update_class_constant(zval *pp, int is_static, uint32_t offset) /* {{{ */ -{ - ZVAL_DEREF(pp); - if (Z_CONSTANT_P(pp)) { - zend_class_entry **scope = EG(current_execute_data) ? &EG(scope) : &CG(active_class_entry); - - if ((*scope)->parent) { - zend_class_entry *ce = *scope; - zend_property_info *prop_info; - - do { - ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) { - if (is_static == ((prop_info->flags & ZEND_ACC_STATIC) != 0) && - offset == prop_info->offset) { - int ret; - zend_class_entry *old_scope = *scope; - *scope = prop_info->ce; - ret = zval_update_constant_ex(pp, 1, NULL); - *scope = old_scope; - return ret; - } - } ZEND_HASH_FOREACH_END(); - ce = ce->parent; - } while (ce); - - } - return zval_update_constant_ex(pp, 1, NULL); - } - return SUCCESS; -} -/* }}} */ - ZEND_API int zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ { - int i; - - /* initialize static members of internal class */ - if (!CE_STATIC_MEMBERS(class_type) && class_type->default_static_members_count) { - zval *p; + if (!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; if (class_type->parent) { if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { return FAILURE; } } + + if (!CE_STATIC_MEMBERS(class_type) && class_type->default_static_members_count) { + /* initialize static members of internal class */ + int i; + zval *p; + #if ZTS - CG(static_members_table)[(zend_intptr_t)(class_type->static_members_table)] = emalloc(sizeof(zval) * class_type->default_static_members_count); + CG(static_members_table)[(zend_intptr_t)(class_type->static_members_table)] = emalloc(sizeof(zval) * class_type->default_static_members_count); #else - class_type->static_members_table = emalloc(sizeof(zval) * class_type->default_static_members_count); + class_type->static_members_table = emalloc(sizeof(zval) * class_type->default_static_members_count); #endif - for (i = 0; i < class_type->default_static_members_count; i++) { - p = &class_type->default_static_members_table[i]; - if (Z_ISREF_P(p) && - class_type->parent && - i < class_type->parent->default_static_members_count && - p == &class_type->parent->default_static_members_table[i] && - Z_TYPE(CE_STATIC_MEMBERS(class_type->parent)[i]) != IS_UNDEF - ) { - zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i]; - - ZVAL_NEW_REF(q, q); - ZVAL_COPY_VALUE(&CE_STATIC_MEMBERS(class_type)[i], q); - Z_ADDREF_P(q); - } else { - ZVAL_DUP(&CE_STATIC_MEMBERS(class_type)[i], p); - } - } - } - - if ((class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) == 0) { - zend_class_entry **scope = EG(current_execute_data) ? &EG(scope) : &CG(active_class_entry); - zend_class_entry *old_scope = *scope; - zval *val; - - *scope = class_type; - - ZEND_HASH_FOREACH_VAL(&class_type->constants_table, val) { - if (UNEXPECTED(zval_update_constant_ex(val, 1, class_type) != SUCCESS)) { - return FAILURE; + for (i = 0; i < class_type->default_static_members_count; i++) { + p = &class_type->default_static_members_table[i]; + if (Z_ISREF_P(p) && + class_type->parent && + i < class_type->parent->default_static_members_count && + p == &class_type->parent->default_static_members_table[i] && + Z_TYPE(CE_STATIC_MEMBERS(class_type->parent)[i]) != IS_UNDEF + ) { + zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i]; + + ZVAL_NEW_REF(q, q); + ZVAL_COPY_VALUE(&CE_STATIC_MEMBERS(class_type)[i], q); + Z_ADDREF_P(q); + } else { + ZVAL_DUP(&CE_STATIC_MEMBERS(class_type)[i], p); + } } - } ZEND_HASH_FOREACH_END(); + } else { + zend_class_entry **scope = EG(current_execute_data) ? &EG(scope) : &CG(active_class_entry); + zend_class_entry *old_scope = *scope; + zend_class_entry *ce; + zval *val; + zend_property_info *prop_info; - for (i = 0; i < class_type->default_properties_count; i++) { - if (Z_TYPE(class_type->default_properties_table[i]) != IS_UNDEF) { - if (UNEXPECTED(zval_update_class_constant(&class_type->default_properties_table[i], 0, OBJ_PROP_TO_OFFSET(i)) != SUCCESS)) { - return FAILURE; + *scope = class_type; + ZEND_HASH_FOREACH_VAL(&class_type->constants_table, val) { + ZVAL_DEREF(val); + if (Z_CONSTANT_P(val)) { + if (UNEXPECTED(zval_update_constant_ex(val, 1, class_type) != SUCCESS)) { + return FAILURE; + } } - } - } + } ZEND_HASH_FOREACH_END(); - for (i = 0; i < class_type->default_static_members_count; i++) { - if (UNEXPECTED(zval_update_class_constant(&CE_STATIC_MEMBERS(class_type)[i], 1, i) != SUCCESS)) { - return FAILURE; + ce = class_type; + while (ce) { + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) { + if (prop_info->ce == ce) { + if (prop_info->flags & ZEND_ACC_STATIC) { + val = CE_STATIC_MEMBERS(class_type) + prop_info->offset; + } else { + val = (zval*)((char*)class_type->default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0)); + } + ZVAL_DEREF(val); + if (Z_CONSTANT_P(val)) { + *scope = ce; + if (UNEXPECTED(zval_update_constant_ex(val, 1, NULL) != SUCCESS)) { + return FAILURE; + } + } + } + } ZEND_HASH_FOREACH_END(); + ce = ce->parent; } - } - *scope = old_scope; - class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + *scope = old_scope; + } } return SUCCESS; } @@ -1313,10 +1293,12 @@ ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *class_type return FAILURE; } - if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) { - ZVAL_NULL(arg); - Z_OBJ_P(arg) = NULL; - return FAILURE; + if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) { + ZVAL_NULL(arg); + Z_OBJ_P(arg) = NULL; + return FAILURE; + } } if (class_type->create_object == NULL) { @@ -3632,13 +3614,16 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z if (ce->type == ZEND_INTERNAL_CLASS) { property_info = pemalloc(sizeof(zend_property_info), 1); + if ((access_type & ZEND_ACC_STATIC) || Z_CONSTANT_P(property)) { + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + } } else { property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info)); + if (Z_CONSTANT_P(property)) { + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + } } - if (Z_CONSTANT_P(property)) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - } if (!(access_type & ZEND_ACC_PPP_MASK)) { access_type |= ZEND_ACC_PUBLIC; } @@ -3680,18 +3665,13 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z break; } } - switch (access_type & ZEND_ACC_PPP_MASK) { - case ZEND_ACC_PRIVATE: { - property_info->name = zend_mangle_property_name(ce->name->val, ce->name->len, name->val, name->len, ce->type & ZEND_INTERNAL_CLASS); - } - break; - case ZEND_ACC_PROTECTED: { - property_info->name = zend_mangle_property_name("*", 1, name->val, name->len, ce->type & ZEND_INTERNAL_CLASS); - } - break; - case ZEND_ACC_PUBLIC: - property_info->name = zend_string_copy(name); - break; + if (access_type & ZEND_ACC_PUBLIC) { + property_info->name = zend_string_copy(name); + } else if (access_type & ZEND_ACC_PRIVATE) { + property_info->name = zend_mangle_property_name(ce->name->val, ce->name->len, name->val, name->len, ce->type & ZEND_INTERNAL_CLASS); + } else { + ZEND_ASSERT(access_type & ZEND_ACC_PROTECTED); + property_info->name = zend_mangle_property_name("*", 1, name->val, name->len, ce->type & ZEND_INTERNAL_CLASS); } property_info->name = zend_new_interned_string(property_info->name); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index f61091ae72..83e5265fd4 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -68,9 +68,9 @@ typedef struct _zend_fcall_info_cache { #define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name)) #define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name)) -#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, +#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags }, -#define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags }, +#define ZEND_RAW_FENTRY(zend_name, name, arg_info, flags) { zend_name, name, arg_info, (uint32_t) (sizeof(arg_info)/sizeof(struct _zend_internal_arg_info)-1), flags }, #define ZEND_RAW_NAMED_FE(zend_name, name, arg_info) ZEND_RAW_FENTRY(#zend_name, name, arg_info, 0) #define ZEND_NAMED_FE(zend_name, name, arg_info) ZEND_FENTRY(zend_name, name, arg_info, 0) @@ -593,20 +593,17 @@ END_EXTERN_C() zval *__z = (z); \ zval *__zv = (zv); \ if (EXPECTED(!Z_ISREF_P(__zv))) { \ - ZVAL_COPY_VALUE(__z, __zv); \ - } else { \ - ZVAL_COPY_VALUE(__z, \ - Z_REFVAL_P(__zv)); \ - } \ - if (copy) { \ - zval_opt_copy_ctor(__z); \ - } \ - if (dtor) { \ - if (!copy) { \ - ZVAL_NULL(__zv); \ + if (copy && !dtor) { \ + ZVAL_COPY(__z, __zv); \ + } else { \ + ZVAL_COPY_VALUE(__z, __zv); \ + } \ + } else { \ + ZVAL_COPY(__z, Z_REFVAL_P(__zv)); \ + if (dtor || !copy) { \ + zval_ptr_dtor(__zv); \ } \ - zval_ptr_dtor(__zv); \ - } \ + } \ } while (0) #define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b) @@ -645,18 +642,6 @@ END_EXTERN_C() #define RETURN_FALSE { RETVAL_FALSE; return; } #define RETURN_TRUE { RETVAL_TRUE; return; } -#define RETVAL_ZVAL_FAST(z) do { \ - zval *_z = (z); \ - if (Z_ISREF_P(_z)) { \ - RETVAL_ZVAL(_z, 1, 0); \ - } else { \ - zval_ptr_dtor(return_value); \ - ZVAL_COPY(return_value, _z); \ - } \ -} while (0) - -#define RETURN_ZVAL_FAST(z) { RETVAL_ZVAL_FAST(z); return; } - #define HASH_OF(p) (Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties((p)) : NULL))) #define ZVAL_IS_NULL(z) (Z_TYPE_P(z) == IS_NULL) @@ -774,6 +759,8 @@ ZEND_API void ZEND_FASTCALL zend_wrong_callback_error(int severity, int num, cha #define Z_PARAM_PROLOGUE(separate) \ ++_i; \ + ZEND_ASSERT(_i <= _min_num_args || _optional==1); \ + ZEND_ASSERT(_i > _min_num_args || _optional==0); \ if (_optional) { \ if (UNEXPECTED(_i >_num_args)) break; \ } \ @@ -1010,7 +997,10 @@ ZEND_API void ZEND_FASTCALL zend_wrong_callback_error(int severity, int num, cha Z_PARAM_PROLOGUE(separate); \ zend_parse_arg_zval_deref(_arg, &dest, check_null); \ } else { \ - if (UNEXPECTED(++_i >_num_args)) break; \ + ++_i; \ + ZEND_ASSERT(_i <= _min_num_args || _optional==1); \ + ZEND_ASSERT(_i > _min_num_args || _optional==0); \ + if (_optional && UNEXPECTED(_i >_num_args)) break; \ _real_arg++; \ zend_parse_arg_zval(_real_arg, &dest, check_null); \ } diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 8f88368bc3..2d47b643c3 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2048,6 +2048,7 @@ void zend_mm_shutdown(zend_mm_heap *heap, int full, int silent) #endif #if ZEND_MM_STAT heap->real_peak = ZEND_MM_CHUNK_SIZE; + heap->size = heap->peak = 0; #endif } } diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 34a2e132f9..fe78bfd31b 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -394,7 +394,12 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc zval tmp; zend_fetch_dimension_by_zval(&tmp, &op1, &op2); - ZVAL_ZVAL(result, &tmp, 1, 1); + if (UNEXPECTED(Z_ISREF(tmp))) { + ZVAL_DUP(result, Z_REFVAL(tmp)); + } else { + ZVAL_DUP(result, &tmp); + } + zval_ptr_dtor(&tmp); zval_dtor(&op1); zval_dtor(&op2); } @@ -1149,9 +1154,6 @@ simple_list: case ZEND_AST_CONST: zend_ast_export_ns_name(str, ast->child[0], 0, indent); break; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); - APPEND_STR("::class"); case ZEND_AST_UNPACK: smart_str_appends(str, "..."); ast = ast->child[0]; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 33aa292fb7..2b8d4d37b8 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -66,7 +66,6 @@ enum _zend_ast_kind { /* 1 child node */ ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_CONST, - ZEND_AST_RESOLVE_CLASS_NAME, ZEND_AST_UNPACK, ZEND_AST_UNARY_PLUS, ZEND_AST_UNARY_MINUS, diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 7313b32065..8989e93fa6 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -459,7 +459,8 @@ ZEND_FUNCTION(func_get_arg) } else { arg = ZEND_CALL_ARG(ex, requested_offset + 1); } - RETURN_ZVAL_FAST(arg); + ZVAL_DEREF(arg); + ZVAL_COPY(return_value, arg); } /* }}} */ @@ -1105,8 +1106,10 @@ ZEND_FUNCTION(get_class_vars) RETURN_FALSE; } else { array_init(return_value); - if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { - return; + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) { + return; + } } add_class_vars(ce, 0, return_value); add_class_vars(ce, 1, return_value); @@ -1684,7 +1687,7 @@ ZEND_FUNCTION(set_error_handler) } if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { - RETVAL_ZVAL(&EG(user_error_handler), 1, 0); + ZVAL_COPY(return_value, &EG(user_error_handler)); zend_stack_push(&EG(user_error_handlers_error_reporting), &EG(user_error_handler_error_reporting)); zend_stack_push(&EG(user_error_handlers), &EG(user_error_handler)); @@ -1695,7 +1698,7 @@ ZEND_FUNCTION(set_error_handler) return; } - ZVAL_DUP(&EG(user_error_handler), error_handler); + ZVAL_COPY(&EG(user_error_handler), error_handler); EG(user_error_handler_error_reporting) = (int)error_type; } /* }}} */ @@ -1752,7 +1755,7 @@ ZEND_FUNCTION(set_exception_handler) } if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { - RETVAL_ZVAL(&EG(user_exception_handler), 1, 0); + ZVAL_COPY(return_value, &EG(user_exception_handler)); zend_stack_push(&EG(user_exception_handlers), &EG(user_exception_handler)); } @@ -1762,7 +1765,7 @@ ZEND_FUNCTION(set_exception_handler) return; } - ZVAL_DUP(&EG(user_exception_handler), exception_handler); + ZVAL_COPY(&EG(user_exception_handler), exception_handler); } /* }}} */ diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index e656f8d6ca..2eeaec639d 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -203,6 +203,8 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* { { zend_closure *closure = (zend_closure *)object; zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function)); + const uint32_t keep_flags = + ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE; invoke->common = closure->func.common; /* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the @@ -210,7 +212,8 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* { * This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set, * and we won't check arguments on internal function */ invoke->type = ZEND_INTERNAL_FUNCTION; - invoke->internal_function.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & ZEND_ACC_RETURN_REFERENCE); + invoke->internal_function.fn_flags = + ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags); invoke->internal_function.handler = ZEND_MN(Closure___invoke); invoke->internal_function.module = 0; invoke->internal_function.scope = zend_ce_closure; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 78f8ae3207..bd7b65f1e8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -102,19 +102,12 @@ static zend_string *zend_build_runtime_definition_key(zend_string *name, unsigne { zend_string *result; char char_pos_buf[32]; - size_t filename_len, char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); + size_t char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); + zend_string *filename = CG(active_op_array)->filename; - const char *filename; - if (CG(active_op_array)->filename) { - filename = CG(active_op_array)->filename->val; - filename_len = CG(active_op_array)->filename->len; - } else { - filename = "-"; - filename_len = sizeof("-") - 1; - } /* NULL, name length, filename length, last accepting char position length */ - result = zend_string_alloc(1 + name->len + filename_len + char_pos_len, 0); - sprintf(result->val, "%c%s%s%s", '\0', name->val, filename, char_pos_buf); + result = zend_string_alloc(1 + name->len + filename->len + char_pos_len, 0); + sprintf(result->val, "%c%s%s%s", '\0', name->val, filename->val, char_pos_buf); return zend_new_interned_string(result); } /* }}} */ @@ -179,13 +172,13 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *name) /* {{{ */ } /* }}} */ -typedef struct _scalar_typehint_info { +typedef struct _builtin_type_info { const char* name; const size_t name_len; const zend_uchar type; -} scalar_typehint_info; +} builtin_type_info; -static const scalar_typehint_info scalar_typehints[] = { +static const builtin_type_info builtin_types[] = { {"int", sizeof("int") - 1, IS_LONG}, {"float", sizeof("float") - 1, IS_DOUBLE}, {"string", sizeof("string") - 1, IS_STRING}, @@ -193,31 +186,20 @@ static const scalar_typehint_info scalar_typehints[] = { {NULL, 0, IS_UNDEF} }; -static zend_always_inline const scalar_typehint_info* zend_find_scalar_typehint(const zend_string *name) /* {{{ */ + +static zend_always_inline zend_uchar zend_lookup_builtin_type_by_name(const zend_string *name) /* {{{ */ { - const scalar_typehint_info *info = &scalar_typehints[0]; + const builtin_type_info *info = &builtin_types[0]; for (; info->name; ++info) { if (name->len == info->name_len && zend_binary_strcasecmp(name->val, name->len, info->name, info->name_len) == 0 ) { - return info; + return info->type; } } - return NULL; -} -/* }}} */ - -static zend_always_inline zend_uchar zend_lookup_scalar_typehint_by_name(const zend_string *const_name) /* {{{ */ -{ - const scalar_typehint_info *info = zend_find_scalar_typehint(const_name); - - if (info) { - return info->type; - } else { - return 0; - } + return 0; } /* }}} */ @@ -568,34 +550,44 @@ static int zend_add_const_name_literal(zend_op_array *op_array, zend_string *nam op.constant = zend_add_literal(CG(active_op_array), &_c); \ } while (0) -void zend_stop_lexing(void) { +void zend_stop_lexing(void) +{ + if(LANG_SCNG(on_event)) LANG_SCNG(on_event)(ON_STOP, END, 0); + LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit); } -static inline void zend_begin_loop(void) /* {{{ */ +static inline void zend_begin_loop(const znode *loop_var) /* {{{ */ { zend_brk_cont_element *brk_cont_element; - int parent; + int parent = CG(context).current_brk_cont; - parent = CG(context).current_brk_cont; CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont; brk_cont_element = get_next_brk_cont_element(CG(active_op_array)); - brk_cont_element->start = get_next_op_number(CG(active_op_array)); brk_cont_element->parent = parent; + + if (loop_var) { + zend_stack_push(&CG(loop_var_stack), loop_var); + brk_cont_element->start = get_next_op_number(CG(active_op_array)); + } else { + /* The start field is used to free temporary variables in case of exceptions. + * We won't try to free something of we don't have loop variable. */ + brk_cont_element->start = -1; + } } /* }}} */ -static inline void zend_end_loop(int cont_addr, int has_loop_var) /* {{{ */ +static inline void zend_end_loop(int cont_addr) /* {{{ */ { - if (!has_loop_var) { - /* The start fileld is used to free temporary variables in case of exceptions. - * We won't try to free something of we don't have loop variable. - */ - CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont].start = -1; + zend_brk_cont_element *brk_cont_element + = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont]; + brk_cont_element->cont = cont_addr; + brk_cont_element->brk = get_next_op_number(CG(active_op_array)); + CG(context).current_brk_cont = brk_cont_element->parent; + + if (brk_cont_element->start >= 0) { + zend_stack_del_top(&CG(loop_var_stack)); } - CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont].cont = cont_addr; - CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont].brk = get_next_op_number(CG(active_op_array)); - CG(context).current_brk_cont = CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont].parent; } /* }}} */ @@ -863,8 +855,11 @@ zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* {{{ */ zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ { - zend_string *name = zend_ast_get_str(ast); - return zend_resolve_class_name(name, ast->attr); + zval *class_name = zend_ast_get_zval(ast); + if (Z_TYPE_P(class_name) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); + } + return zend_resolve_class_name(Z_STR_P(class_name), ast->attr); } /* }}} */ @@ -1325,14 +1320,10 @@ static inline zend_bool zend_is_scope_known() /* {{{ */ return 0; } - if (!CG(active_op_array)->function_name) { - /* A file/eval will be run in the including/eval'ing scope */ - return 0; - } - if (!CG(active_class_entry)) { - /* Not being in a scope is a known scope */ - return 1; + /* The scope is known if we're in a free function (no scope), but not if we're in + * a file/eval (which inherits including/eval'ing scope). */ + return CG(active_op_array)->function_name != NULL; } /* For traits self etc refers to the using class, not the trait itself */ @@ -1353,6 +1344,88 @@ static inline zend_bool class_name_refers_to_active_ce(zend_string *class_name, } /* }}} */ +uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */ +{ + if (zend_string_equals_literal_ci(name, "self")) { + return ZEND_FETCH_CLASS_SELF; + } else if (zend_string_equals_literal_ci(name, "parent")) { + return ZEND_FETCH_CLASS_PARENT; + } else if (zend_string_equals_literal_ci(name, "static")) { + return ZEND_FETCH_CLASS_STATIC; + } else { + return ZEND_FETCH_CLASS_DEFAULT; + } +} +/* }}} */ + +static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ +{ + /* Fully qualified names are always default refs */ + if (name_ast->attr == ZEND_NAME_FQ) { + return ZEND_FETCH_CLASS_DEFAULT; + } + + return zend_get_class_fetch_type(zend_ast_get_str(name_ast)); +} +/* }}} */ + +static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ +{ + if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", + fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : + fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); + } +} +/* }}} */ + +static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_ast *name_ast, zend_bool constant) /* {{{ */ +{ + uint32_t fetch_type; + + if (name_ast->kind != ZEND_AST_ZVAL) { + return 0; + } + + if (!zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { + return 0; + } + + if (class_ast->kind != ZEND_AST_ZVAL) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } + + fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + zend_ensure_valid_class_fetch_type(fetch_type); + + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + if (constant || (CG(active_class_entry) && zend_is_scope_known())) { + ZVAL_STR_COPY(zv, CG(active_class_entry)->name); + } else { + ZVAL_NULL(zv); + } + return 1; + case ZEND_FETCH_CLASS_STATIC: + case ZEND_FETCH_CLASS_PARENT: + if (constant) { + zend_error_noreturn(E_COMPILE_ERROR, + "%s::class cannot be used for compile-time class name resolution", + fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); + } else { + ZVAL_NULL(zv); + } + return 1; + case ZEND_FETCH_CLASS_DEFAULT: + ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast)); + return 1; + EMPTY_SWITCH_DEFAULT_CASE() + } +} +/* }}} */ + static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend_string *name) /* {{{ */ { uint32_t fetch_type = zend_get_class_fetch_type(class_name); @@ -1385,18 +1458,7 @@ static zend_bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, } /* }}} */ -void zend_init_list(void *result, void *item) /* {{{ */ -{ - void** list = emalloc(sizeof(void*) * 2); - - list[0] = item; - list[1] = NULL; - - *(void**)result = list; -} -/* }}} */ - -void zend_add_to_list(void *result, void *item) /* {{{ */ +static void zend_add_to_list(void *result, void *item) /* {{{ */ { void** list = *(void**)result; size_t n = 0; @@ -1633,41 +1695,6 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify } /* }}} */ -uint32_t zend_get_class_fetch_type(zend_string *name) /* {{{ */ -{ - if (zend_string_equals_literal_ci(name, "self")) { - return ZEND_FETCH_CLASS_SELF; - } else if (zend_string_equals_literal_ci(name, "parent")) { - return ZEND_FETCH_CLASS_PARENT; - } else if (zend_string_equals_literal_ci(name, "static")) { - return ZEND_FETCH_CLASS_STATIC; - } else { - return ZEND_FETCH_CLASS_DEFAULT; - } -} -/* }}} */ - -static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ -{ - /* Fully qualified names are always default refs */ - if (name_ast->attr == ZEND_NAME_FQ) { - return ZEND_FETCH_CLASS_DEFAULT; - } - - return zend_get_class_fetch_type(zend_ast_get_str(name_ast)); -} -/* }}} */ - -static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ -{ - if (fetch_type != ZEND_FETCH_CLASS_DEFAULT && !CG(active_class_entry) && zend_is_scope_known()) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"%s\" when no class scope is active", - fetch_type == ZEND_FETCH_CLASS_SELF ? "self" : - fetch_type == ZEND_FETCH_CLASS_PARENT ? "parent" : "static"); - } -} -/* }}} */ - ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var) /* {{{ */ { return op_array->vars[EX_VAR_TO_NUM(var)]; @@ -2172,14 +2199,6 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint znode name_node; zend_op *opline; - /* there is a chance someone is accessing $this */ - if (ast->kind != ZEND_AST_ZVAL - && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1 - ) { - zend_string *key = zend_string_init("this", sizeof("this") - 1, 0); - CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key); - } - zend_compile_expr(&name_node, name_ast); if (name_node.op_type == IS_CONST) { convert_to_string(&name_node.u.constant); @@ -2191,10 +2210,18 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL); } - opline->extended_value = ZEND_FETCH_LOCAL; - if (name_node.op_type == IS_CONST) { - if (zend_is_auto_global(Z_STR(name_node.u.constant))) { - opline->extended_value = ZEND_FETCH_GLOBAL; + if (name_node.op_type == IS_CONST && + zend_is_auto_global(Z_STR(name_node.u.constant))) { + + opline->extended_value = ZEND_FETCH_GLOBAL; + } else { + opline->extended_value = ZEND_FETCH_LOCAL; + /* there is a chance someone is accessing $this */ + if (ast->kind != ZEND_AST_ZVAL + && CG(active_op_array)->scope && CG(active_op_array)->this_var == (uint32_t)-1 + ) { + zend_string *key = zend_string_init("this", sizeof("this") - 1, 0); + CG(active_op_array)->this_var = lookup_cv(CG(active_op_array), key); } } @@ -2873,7 +2900,13 @@ int zend_compile_func_strlen(znode *result, zend_ast_list *args) /* {{{ */ } zend_compile_expr(&arg_node, args->child[0]); - zend_emit_op_tmp(result, ZEND_STRLEN, &arg_node, NULL); + if (arg_node.op_type == IS_CONST && Z_TYPE(arg_node.u.constant) == IS_STRING) { + result->op_type = IS_CONST; + ZVAL_LONG(&result->u.constant, Z_STRLEN(arg_node.u.constant)); + zval_dtor(&arg_node.u.constant); + } else { + zend_emit_op_tmp(result, ZEND_STRLEN, &arg_node, NULL); + } return SUCCESS; } /* }}} */ @@ -3452,17 +3485,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */ static void zend_free_foreach_and_switch_variables(void) /* {{{ */ { - uint32_t opnum_start, opnum_end, i; - - opnum_start = get_next_op_number(CG(active_op_array)); - zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var); - - opnum_end = get_next_op_number(CG(active_op_array)); - - for (i = opnum_start; i < opnum_end; ++i) { - CG(active_op_array)->opcodes[i].extended_value |= EXT_TYPE_FREE_ON_RETURN; - } } /* }}} */ @@ -3535,26 +3558,27 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ { zend_ast *depth_ast = ast->child[0]; - znode depth_node; zend_op *opline; + int depth; ZEND_ASSERT(ast->kind == ZEND_AST_BREAK || ast->kind == ZEND_AST_CONTINUE); if (depth_ast) { + zval *depth_zv; if (depth_ast->kind != ZEND_AST_ZVAL) { zend_error_noreturn(E_COMPILE_ERROR, "'%s' operator with non-constant operand " "is no longer supported", ast->kind == ZEND_AST_BREAK ? "break" : "continue"); } - zend_compile_expr(&depth_node, depth_ast); - - if (Z_TYPE(depth_node.u.constant) != IS_LONG || Z_LVAL(depth_node.u.constant) < 1) { + depth_zv = zend_ast_get_zval(depth_ast); + if (Z_TYPE_P(depth_zv) != IS_LONG || Z_LVAL_P(depth_zv) < 1) { zend_error_noreturn(E_COMPILE_ERROR, "'%s' operator accepts only positive numbers", ast->kind == ZEND_AST_BREAK ? "break" : "continue"); } + + depth = Z_LVAL_P(depth_zv); } else { - depth_node.op_type = IS_CONST; - ZVAL_LONG(&depth_node.u.constant, 1); + depth = 1; } if (CG(context).current_brk_cont == -1) { @@ -3562,20 +3586,27 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ ast->kind == ZEND_AST_BREAK ? "break" : "continue"); } else { int array_offset = CG(context).current_brk_cont; - zend_long nest_level = Z_LVAL(depth_node.u.constant); + zend_long nest_level = depth; + znode *loop_var = zend_stack_top(&CG(loop_var_stack)); do { if (array_offset == -1) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s", ast->kind == ZEND_AST_BREAK ? "break" : "continue", - Z_LVAL(depth_node.u.constant), (Z_LVAL(depth_node.u.constant) == 1) ? "" : "s"); + depth, depth == 1 ? "" : "s"); } + + if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) { + generate_free_loop_var(loop_var); + loop_var--; + } + array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent; } while (--nest_level > 0); } - opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, - NULL, &depth_node); - opline->op1.opline_num = CG(context).current_brk_cont; + opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL); + opline->op1.num = CG(context).current_brk_cont; + opline->op2.num = depth; } /* }}} */ @@ -3620,7 +3651,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(); + zend_begin_loop(NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3631,7 +3662,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start); - zend_end_loop(opnum_cond, 0); + zend_end_loop(opnum_cond); } /* }}} */ @@ -3643,7 +3674,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */ znode cond_node; uint32_t opnum_start, opnum_cond; - zend_begin_loop(); + zend_begin_loop(NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3653,7 +3684,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start); - zend_end_loop(opnum_cond, 0); + zend_end_loop(opnum_cond); } /* }}} */ @@ -3694,7 +3725,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */ opnum_jmp = zend_emit_jump(0); - zend_begin_loop(); + zend_begin_loop(NULL); opnum_start = get_next_op_number(CG(active_op_array)); zend_compile_stmt(stmt_ast); @@ -3709,7 +3740,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */ zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start); - zend_end_loop(opnum_loop, 0); + zend_end_loop(opnum_loop); } /* }}} */ @@ -3753,9 +3784,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); - reset_node.flag = 1; /* generate FE_FREE */ - zend_stack_push(&CG(loop_var_stack), &reset_node); - opnum_fetch = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(NULL, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); @@ -3779,7 +3807,8 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_emit_assign_znode(key_ast, &key_node); } - zend_begin_loop(); + reset_node.flag = 1; /* generate FE_FREE */ + zend_begin_loop(&reset_node); zend_compile_stmt(stmt_ast); @@ -3791,10 +3820,9 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opline = &CG(active_op_array)->opcodes[opnum_fetch]; opline->extended_value = get_next_op_number(CG(active_op_array)); - zend_end_loop(opnum_fetch, 1); + zend_end_loop(opnum_fetch); generate_free_loop_var(&reset_node); - zend_stack_del_top(&CG(loop_var_stack)); } /* }}} */ @@ -3855,10 +3883,8 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); - expr_node.flag = 0; - zend_stack_push(&CG(loop_var_stack), &expr_node); - - zend_begin_loop(); + expr_node.flag = 0; /* Generate normal FREE */ + zend_begin_loop(&expr_node); case_node.op_type = IS_TMP_VAR; case_node.u.op.var = get_temporary_variable(CG(active_op_array)); @@ -3917,16 +3943,14 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_update_jump_target_to_next(opnum_default_jmp); } - zend_end_loop(get_next_op_number(CG(active_op_array)), 1); + zend_end_loop(get_next_op_number(CG(active_op_array))); if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) { - zend_emit_op(NULL, ZEND_FREE, - &expr_node, NULL); + zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL); } else if (expr_node.op_type == IS_CONST) { zval_dtor(&expr_node.u.constant); } - zend_stack_del_top(&CG(loop_var_stack)); efree(jmpnz_opnums); } /* }}} */ @@ -4198,6 +4222,37 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ } /* }}} */ + +static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{ */ +{ + if (ast->kind == ZEND_AST_TYPE) { + arg_info->type_hint = ast->attr; + } else { + zend_string *class_name = zend_ast_get_str(ast); + zend_uchar type = zend_lookup_builtin_type_by_name(class_name); + + if (type != 0) { + arg_info->type_hint = type; + } else { + uint32_t fetch_type = zend_get_class_fetch_type_ast(ast); + if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { + class_name = zend_resolve_class_name_ast(ast); + zend_assert_valid_class_name(class_name); + arg_info->lower_class_name = zend_string_tolower(class_name); + } else { + zend_ensure_valid_class_fetch_type(fetch_type); + zend_string_addref(class_name); + arg_info->lower_class_name = NULL; + } + + arg_info->type_hint = IS_OBJECT; + arg_info->class_name = class_name; + } + } +} +/* }}} */ + + void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); @@ -4215,28 +4270,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_infos->allow_null = 0; arg_infos->class_name = NULL; - if (return_type_ast->kind == ZEND_AST_TYPE) { - arg_infos->type_hint = return_type_ast->attr; - } else { - zend_string *class_name = zend_ast_get_str(return_type_ast); - zend_uchar type = zend_lookup_scalar_typehint_by_name(class_name); - - if (type != 0) { - arg_infos->type_hint = type; - } else { - uint32_t fetch_type = zend_get_class_fetch_type_ast(return_type_ast); - if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { - class_name = zend_resolve_class_name_ast(return_type_ast); - zend_assert_valid_class_name(class_name); - } else { - zend_ensure_valid_class_fetch_type(fetch_type); - zend_string_addref(class_name); - } - - arg_infos->type_hint = IS_OBJECT; - arg_infos->class_name = class_name; - } - } + zend_compile_typename(return_type_ast, arg_infos); arg_infos++; op_array->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE; @@ -4327,8 +4361,9 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; arg_info->allow_null = has_null_default; + zend_compile_typename(type_ast, arg_info); + if (type_ast->kind == ZEND_AST_TYPE) { - arg_info->type_hint = type_ast->attr; if (arg_info->type_hint == IS_ARRAY) { if (default_ast && !has_null_default && Z_TYPE(default_node.u.constant) != IS_ARRAY @@ -4344,32 +4379,14 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ } } } else { - zend_string *class_name = zend_ast_get_str(type_ast); - zend_uchar type; - - type = zend_lookup_scalar_typehint_by_name(class_name); - if (type != 0) { - arg_info->type_hint = type; - } else { - uint32_t fetch_type = zend_get_class_fetch_type_ast(type_ast); - if (fetch_type == ZEND_FETCH_CLASS_DEFAULT) { - class_name = zend_resolve_class_name_ast(type_ast); - zend_assert_valid_class_name(class_name); - } else { - zend_ensure_valid_class_fetch_type(fetch_type); - zend_string_addref(class_name); - } - - arg_info->type_hint = IS_OBJECT; - arg_info->class_name = class_name; - } if (default_ast && !has_null_default && !Z_CONSTANT(default_node.u.constant)) { if (arg_info->class_name) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with a class type hint can only be NULL"); } else if (!ZEND_SAME_FAKE_TYPE(arg_info->type_hint, Z_TYPE(default_node.u.constant))) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " - "with a %s type hint can only be %s or NULL", class_name->val, class_name->val); + "with a %s type hint can only be %s or NULL", + zend_get_type_by_const(arg_info->type_hint), zend_get_type_by_const(arg_info->type_hint)); } } } @@ -4774,6 +4791,11 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ zend_string *name = zend_ast_get_str(name_ast); zval value_zv; + if (zend_string_equals_literal_ci(name, "class")) { + zend_error(E_COMPILE_ERROR, + "A class constant must not be called 'class'; it is reserved for class name fetching"); + } + zend_const_expr_to_zval(&value_zv, value_ast); name = zend_new_interned_string_safe(name); @@ -4953,19 +4975,12 @@ static zend_string *zend_generate_anon_class_name(unsigned char *lex_pos) /* {{{ { zend_string *result; char char_pos_buf[32]; - size_t filename_len, char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); + size_t char_pos_len = zend_sprintf(char_pos_buf, "%p", lex_pos); + zend_string *filename = CG(active_op_array)->filename; - const char *filename; - if (CG(active_op_array)->filename) { - filename = CG(active_op_array)->filename->val; - filename_len = CG(active_op_array)->filename->len; - } else { - filename = "-"; - filename_len = sizeof("-") - 1; - } /* NULL, name length, filename length, last accepting char position length */ - result = zend_string_alloc(sizeof("class@anonymous") + filename_len + char_pos_len, 0); - sprintf(result->val, "class@anonymous%c%s%s", '\0', filename, char_pos_buf); + result = zend_string_alloc(sizeof("class@anonymous") + filename->len + char_pos_len, 0); + sprintf(result->val, "class@anonymous%c%s%s", '\0', filename->val, char_pos_buf); return zend_new_interned_string(result); } /* }}} */ @@ -4976,8 +4991,7 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ zend_ast *extends_ast = decl->child[0]; zend_ast *implements_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; - - zend_string *name = decl->name, *lcname, *import_name = NULL; + zend_string *name, *lcname, *import_name = NULL; zend_class_entry *ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); zend_op *opline; znode declare_node, extends_node; @@ -4985,43 +4999,37 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ zend_class_entry *original_ce = CG(active_class_entry); znode original_implementing_class = FC(implementing_class); - if (decl->flags & ZEND_ACC_ANON_CLASS) { - decl->name = name = zend_generate_anon_class_name(decl->lex_pos); - - /* Serialization is not supported for anonymous classes */ - ce->serialize = zend_class_serialize_deny; - ce->unserialize = zend_class_unserialize_deny; - } - - if (CG(active_class_entry) && !(decl->flags & ZEND_ACC_ANON_CLASS)) { - zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); - } - - zend_assert_valid_class_name(name); + if (EXPECTED((decl->flags & ZEND_ACC_ANON_CLASS) == 0)) { + if (CG(active_class_entry)) { + zend_error_noreturn(E_COMPILE_ERROR, "Class declarations may not be nested"); + } + name = decl->name; + zend_assert_valid_class_name(name); + lcname = zend_string_tolower(name); + if (FC(current_namespace)) { + name = zend_prefix_with_ns(name); - lcname = zend_string_tolower(name); + zend_string_release(lcname); + lcname = zend_string_tolower(name); + } else { + zend_string_addref(name); + } - if (FC(imports)) { - import_name = zend_hash_find_ptr(FC(imports), lcname); - } + if (FC(imports)) { + import_name = zend_hash_find_ptr(FC(imports), lcname); + } - if (FC(current_namespace)) { - name = zend_prefix_with_ns(name); + if (import_name && !zend_string_equals_ci(lcname, import_name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " + "because the name is already in use", name->val); + } - zend_string_release(lcname); - lcname = zend_string_tolower(name); + name = zend_new_interned_string(name); + lcname = zend_new_interned_string(lcname); } else { - zend_string_addref(name); - } - - if (import_name && !zend_string_equals_ci(lcname, import_name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare class %s " - "because the name is already in use", name->val); + lcname = name = zend_generate_anon_class_name(decl->lex_pos); } - name = zend_new_interned_string(name); - lcname = zend_new_interned_string(lcname); - ce->type = ZEND_USER_CLASS; ce->name = name; zend_initialize_class_data(ce, 1); @@ -5030,10 +5038,17 @@ void zend_compile_class_decl(zend_ast *ast) /* {{{ */ ce->info.user.filename = zend_get_compiled_filename(); ce->info.user.line_start = decl->start_lineno; ce->info.user.line_end = decl->end_lineno; + if (decl->doc_comment) { ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } + if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { + /* Serialization is not supported for anonymous classes */ + ce->serialize = zend_class_serialize_deny; + ce->unserialize = zend_class_unserialize_deny; + } + if (extends_ast) { if (!zend_is_const_default_class_ref(extends_ast)) { zend_string *extends_name = zend_ast_get_str(extends_ast); @@ -5576,6 +5591,13 @@ static inline void zend_ct_eval_binary_op(zval *result, uint32_t opcode, zval *o } /* }}} */ +static inline void zend_ct_eval_unary_op(zval *result, uint32_t opcode, zval *op) /* {{{ */ +{ + unary_op_type fn = get_unary_op(opcode); + fn(result, op); +} +/* }}} */ + static inline void zend_ct_eval_unary_pm(zval *result, zend_ast_kind kind, zval *op) /* {{{ */ { binary_op_type fn = kind == ZEND_AST_UNARY_PLUS @@ -5719,6 +5741,15 @@ void zend_compile_binary_op(znode *result, zend_ast *ast) /* {{{ */ } } } + if (opcode == ZEND_CONCAT) { + /* convert constant operands to strings at compile-time */ + if (left_node.op_type == IS_CONST) { + convert_to_string(&left_node.u.constant); + } + if (right_node.op_type == IS_CONST) { + convert_to_string(&right_node.u.constant); + } + } zend_emit_op_tmp(result, opcode, &left_node, &right_node); } while (0); } @@ -5760,6 +5791,14 @@ void zend_compile_unary_op(znode *result, zend_ast *ast) /* {{{ */ znode expr_node; zend_compile_expr(&expr_node, expr_ast); + if (expr_node.op_type == IS_CONST) { + result->op_type = IS_CONST; + zend_ct_eval_unary_op(&result->u.constant, opcode, + &expr_node.u.constant); + zval_ptr_dtor(&expr_node.u.constant); + return; + } + zend_emit_op_tmp(result, opcode, &expr_node, NULL); } /* }}} */ @@ -5801,6 +5840,28 @@ void zend_compile_short_circuiting(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(&left_node, left_ast); + if (left_node.op_type == IS_CONST) { + if ((ast->kind == ZEND_AST_AND && !zend_is_true(&left_node.u.constant)) + || (ast->kind == ZEND_AST_OR && zend_is_true(&left_node.u.constant))) { + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, zend_is_true(&left_node.u.constant)); + } else { + zend_compile_expr(&right_node, right_ast); + + if (right_node.op_type == IS_CONST) { + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, zend_is_true(&right_node.u.constant)); + + zval_ptr_dtor(&right_node.u.constant); + } else { + zend_emit_op(result, ZEND_BOOL, &right_node, NULL); + } + } + + zval_ptr_dtor(&left_node.u.constant); + return; + } + opnum_jmpz = get_next_op_number(CG(active_op_array)); opline_jmpz = zend_emit_op(NULL, ast->kind == ZEND_AST_AND ? ZEND_JMPZ_EX : ZEND_JMPNZ_EX, &left_node, NULL); @@ -5811,10 +5872,26 @@ void zend_compile_short_circuiting(znode *result, zend_ast *ast) /* {{{ */ opline_jmpz->result.var = get_temporary_variable(CG(active_op_array)); opline_jmpz->result_type = IS_TMP_VAR; } - GET_NODE(result, opline_jmpz->result); zend_compile_expr(&right_node, right_ast); + if (right_node.op_type == IS_CONST && opnum_jmpz == get_next_op_number(CG(active_op_array)) - 1) { + if ((ast->kind == ZEND_AST_AND && !zend_is_true(&right_node.u.constant)) + || (ast->kind == ZEND_AST_OR && zend_is_true(&right_node.u.constant))) { + CG(active_op_array)->last--; + result->op_type = IS_CONST; + ZVAL_BOOL(&result->u.constant, zend_is_true(&right_node.u.constant)); + } else { + opline_jmpz->opcode = ZEND_BOOL; + zend_make_var_result(result, opline_jmpz); + } + + zval_ptr_dtor(&right_node.u.constant); + return; + } + + GET_NODE(result, opline_jmpz->result); + opline_bool = zend_emit_op(NULL, ZEND_BOOL, &right_node, NULL); SET_NODE(opline_bool->result, result); @@ -6340,6 +6417,16 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ zend_op *opline; zend_string *resolved_name; + if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) { + if (Z_TYPE(result->u.constant) == IS_NULL) { + zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); + opline->extended_value = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + } else { + result->op_type = IS_CONST; + } + return; + } + zend_eval_const_expr(&class_ast); zend_eval_const_expr(&const_ast); @@ -6351,6 +6438,10 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ return; } } + if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } if (zend_is_const_default_class_ref(class_ast)) { class_node.op_type = IS_CONST; @@ -6563,7 +6654,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_RESOLVE_CLASS_NAME || kind == ZEND_AST_MAGIC_CONST; + || kind == ZEND_AST_MAGIC_CONST; } /* }}} */ @@ -6582,6 +6673,11 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ "Dynamic class names are not allowed in compile-time class constant references"); } + if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, const_ast, 1)) { + *ast_ptr = zend_ast_create_zval(&result); + return; + } + class_name = zend_ast_get_str(class_ast); fetch_type = zend_get_class_fetch_type(class_name); @@ -6637,36 +6733,6 @@ void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */ } /* }}} */ -void zend_compile_const_expr_resolve_class_name(zend_ast **ast_ptr) /* {{{ */ -{ - zend_ast *ast = *ast_ptr; - zend_ast *name_ast = ast->child[0]; - zval result; - uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(name_ast)); - zend_ensure_valid_class_fetch_type(fetch_type); - - switch (fetch_type) { - case ZEND_FETCH_CLASS_SELF: - ZVAL_STR_COPY(&result, CG(active_class_entry)->name); - break; - case ZEND_FETCH_CLASS_STATIC: - case ZEND_FETCH_CLASS_PARENT: - zend_error_noreturn(E_COMPILE_ERROR, - "%s::class cannot be used for compile-time class name resolution", - fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" - ); - break; - case ZEND_FETCH_CLASS_DEFAULT: - ZVAL_STR(&result, zend_resolve_class_name_ast(name_ast)); - break; - EMPTY_SWITCH_DEFAULT_CASE() - } - - zend_ast_destroy(ast); - *ast_ptr = zend_ast_create_zval(&result); -} -/* }}} */ - void zend_compile_const_expr_magic_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; @@ -6705,9 +6771,6 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_CONST: zend_compile_const_expr_const(ast_ptr); break; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_compile_const_expr_resolve_class_name(ast_ptr); - break; case ZEND_AST_MAGIC_CONST: zend_compile_const_expr_magic_const(ast_ptr); break; @@ -6982,9 +7045,6 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_CLASS_CONST: zend_compile_class_const(result, ast); return; - case ZEND_AST_RESOLVE_CLASS_NAME: - zend_compile_resolve_class_name(result, ast); - return; case ZEND_AST_ENCAPS_LIST: zend_compile_encaps_list(result, ast); return; @@ -7154,9 +7214,22 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ zend_ast *name_ast = ast->child[1]; zend_string *resolved_name; + if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 0)) { + if (Z_TYPE(result) == IS_NULL) { + return; + } else { + break; + } + } + zend_eval_const_expr(&class_ast); zend_eval_const_expr(&name_ast); + if (name_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { + zend_error_noreturn(E_COMPILE_ERROR, + "Dynamic class names are not allowed in compile-time ::class fetch"); + } + if (class_ast->kind != ZEND_AST_ZVAL || name_ast->kind != ZEND_AST_ZVAL) { return; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 2027bfdfd2..eccbb84aff 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -183,16 +183,6 @@ typedef struct _zend_try_catch_element { uint32_t finally_end; } zend_try_catch_element; -#if SIZEOF_ZEND_LONG == 8 -# ifdef _WIN32 -# define THIS_HASHVAL 6385726429Ui64 -# else -# define THIS_HASHVAL 6385726429ULL -# endif -#else -#define THIS_HASHVAL 2090759133UL -#endif - /* method flags (types) */ #define ZEND_ACC_STATIC 0x01 #define ZEND_ACC_ABSTRACT 0x02 @@ -307,6 +297,7 @@ typedef struct _zend_internal_arg_info { zend_uchar pass_by_reference; zend_bool allow_null; zend_bool is_variadic; + void *reserved; /* to align with zend_arg_info */ } zend_internal_arg_info; /* arg_info for user functions */ @@ -317,6 +308,7 @@ typedef struct _zend_arg_info { zend_uchar pass_by_reference; zend_bool allow_null; zend_bool is_variadic; + zend_string *lower_class_name; } zend_arg_info; /* the following structure repeats the layout of zend_internal_arg_info, @@ -711,10 +703,6 @@ ZEND_API zend_class_entry *do_bind_class(const zend_op_array *op_array, const ze ZEND_API zend_class_entry *do_bind_inherited_class(const zend_op_array *op_array, const zend_op *opline, HashTable *class_table, zend_class_entry *parent_ce, zend_bool compile_time); ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array); -/* Functions for a null terminated pointer list, used for traits parsing and compilation */ -void zend_init_list(void *result, void *item); -void zend_add_to_list(void *result, void *item); - void zend_do_extended_info(void); void zend_do_extended_fcall_begin(void); void zend_do_extended_fcall_end(void); @@ -884,8 +872,6 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_FETCH_ARG_MASK 0x000fffff -#define EXT_TYPE_FREE_ON_RETURN (1<<2) - #define ZEND_MEMBER_FUNC_CALL 1<<0 #define ZEND_ARG_SEND_BY_REF (1<<0) @@ -961,6 +947,11 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf, #define ZEND_ARRAY_NOT_PACKED (1<<1) #define ZEND_ARRAY_SIZE_SHIFT 2 +/* Pseudo-opcodes that are used only temporarily during compilation */ +#define ZEND_BRK 254 +#define ZEND_CONT 255 + + END_EXTERN_C() #define ZEND_CLONE_FUNC_NAME "__clone" diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 422c4bb5fd..ae55c1fcea 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -38,7 +38,7 @@ static zend_class_entry *type_error_ce; static zend_object_handlers default_exception_handlers; ZEND_API void (*zend_throw_exception_hook)(zval *ex); -static zend_class_entry *zend_get_exception_base(zval *object) +static inline zend_class_entry *zend_get_exception_base(zval *object) { return instanceof_function(Z_OBJCE_P(object), default_exception_ce) ? default_exception_ce : error_ce; } @@ -131,7 +131,7 @@ ZEND_API void zend_throw_exception_internal(zval *exception) /* {{{ */ if (!EG(current_execute_data)->func || !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) || - (EG(current_execute_data)->opline+1)->opcode == ZEND_HANDLE_EXCEPTION) { + EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { /* no need to rethrow the exception */ return; } @@ -573,7 +573,7 @@ ZEND_METHOD(exception, getTraceAsString) object = getThis(); base_ce = zend_get_exception_base(object); - trace = zend_read_property(base_ce, getThis(), "trace", sizeof("trace")-1, 1, &rv); + trace = zend_read_property(base_ce, object, "trace", sizeof("trace")-1, 1, &rv); if (Z_TYPE_P(trace) != IS_ARRAY) { RETURN_FALSE; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d29014d3b2..e63fc2dd32 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -57,6 +57,7 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *); #define get_zval_ptr_ptr(op_type, node, ex, should_free, type) _get_zval_ptr_ptr(op_type, node, ex, should_free, type) #define get_zval_ptr_ptr_undef(op_type, node, ex, should_free, type) _get_zval_ptr_ptr(op_type, node, ex, should_free, type) #define get_obj_zval_ptr(op_type, node, ex, should_free, type) _get_obj_zval_ptr(op_type, node, ex, should_free, type) +#define get_obj_zval_ptr_undef(op_type, node, ex, should_free, type) _get_obj_zval_ptr_undef(op_type, node, ex, should_free, type) #define get_obj_zval_ptr_ptr(op_type, node, ex, should_free, type) _get_obj_zval_ptr_ptr(op_type, node, ex, should_free, type) /* Prototypes */ @@ -516,6 +517,15 @@ static inline zval *_get_obj_zval_ptr(int op_type, znode_op op, zend_execute_dat return get_zval_ptr(op_type, op, execute_data, should_free, type); } +static inline zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, zend_execute_data *execute_data, zend_free_op *should_free, int type) +{ + if (op_type == IS_UNUSED) { + *should_free = NULL; + return &EX(This); + } + return get_zval_ptr_undef(op_type, op, execute_data, should_free, type); +} + static inline zval *_get_obj_zval_ptr_ptr(int op_type, znode_op node, zend_execute_data *execute_data, zend_free_op *should_free, int type) { if (op_type == IS_UNUSED) { @@ -577,7 +587,12 @@ ZEND_API char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info ZEND_API char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce) { - *pce = zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD)); + /* optimization to not always recalculate the lowercase name and hash */ + if (cur_arg_info->lower_class_name) { + *pce = zend_hash_find_ptr(EG(class_table), cur_arg_info->lower_class_name); + } else { /* "extra" fetch type */ + *pce = zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD)); + } *class_name = (*pce) ? (*pce)->name->val : cur_arg_info->class_name->val; if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) { @@ -593,7 +608,6 @@ ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, c const char *fname = zf->common.function_name->val; const char *fsep; const char *fclass; - zval old_arg; if (zf->common.scope) { fsep = "::"; @@ -604,11 +618,6 @@ ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, c } if (zf->common.type == ZEND_USER_FUNCTION) { - if (arg) { - ZVAL_COPY_VALUE(&old_arg, arg); - ZVAL_UNDEF(arg); - } - if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, @@ -616,10 +625,6 @@ ZEND_API void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, c } else { zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } - - if (arg) { - ZVAL_COPY_VALUE(arg, &old_arg); - } } else { zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } @@ -1246,6 +1251,133 @@ static void zend_assign_to_string_offset(zval *str, zend_long offset, zval *valu } } +static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc, zval *result) +{ + if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { + zval rv, obj; + zval *z; + zval z_copy; + + ZVAL_OBJ(&obj, Z_OBJ_P(object)); + Z_ADDREF(obj); + z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); + if (UNEXPECTED(EG(exception))) { + OBJ_RELEASE(Z_OBJ(obj)); + return; + } + + if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + if (z == &rv) { + zval_ptr_dtor(&rv); + } + ZVAL_COPY_VALUE(z, value); + } + ZVAL_COPY(result, z); + ZVAL_DUP(&z_copy, z); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + OBJ_RELEASE(Z_OBJ(obj)); + zval_ptr_dtor(&z_copy); + zval_ptr_dtor(z); + } else { + zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); + ZVAL_NULL(result); + } +} + +static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc, zval *result) +{ + zval rv; + + if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { + zval *z, obj; + + ZVAL_OBJ(&obj, Z_OBJ_P(object)); + Z_ADDREF(obj); + z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); + if (UNEXPECTED(EG(exception))) { + OBJ_RELEASE(Z_OBJ(obj)); + return; + } + + if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + + if (z == &rv) { + zval_ptr_dtor(&rv); + } + ZVAL_COPY_VALUE(z, value); + } + ZVAL_DEREF(z); + SEPARATE_ZVAL_NOREF(z); + if (inc) { + increment_function(z); + } else { + decrement_function(z); + } + if (UNEXPECTED(result)) { + ZVAL_COPY(result, z); + } + Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + OBJ_RELEASE(Z_OBJ(obj)); + zval_ptr_dtor(z); + } else { + zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); + if (UNEXPECTED(result)) { + ZVAL_NULL(result); + } + } +} + +static zend_never_inline void zend_assign_op_overloaded_property(zval *object, zval *property, void **cache_slot, zval *value, binary_op_type binary_op, zval *result) +{ + zval *z; + zval rv, obj; + zval *zptr; + + ZVAL_OBJ(&obj, Z_OBJ_P(object)); + Z_ADDREF(obj); + if (Z_OBJ_HT(obj)->read_property && + (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv)) != NULL) { + if (UNEXPECTED(EG(exception))) { + OBJ_RELEASE(Z_OBJ(obj)); + return; + } + if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + + if (z == &rv) { + zval_ptr_dtor(&rv); + } + ZVAL_COPY_VALUE(z, value); + } + zptr = z; + ZVAL_DEREF(z); + SEPARATE_ZVAL_NOREF(z); + binary_op(z, z, value); + Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + if (UNEXPECTED(result)) { + ZVAL_COPY(result, z); + } + zval_ptr_dtor(zptr); + } else { + zend_error(E_WARNING, "Attempt to assign property of non-object"); + if (UNEXPECTED(result)) { + ZVAL_NULL(result); + } + } + OBJ_RELEASE(Z_OBJ(obj)); +} + /* Utility Functions for Extensions */ static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array) { @@ -1311,14 +1443,14 @@ num_index: if (retval == NULL) { switch (type) { case BP_VAR_R: - zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, hval); + zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); /* break missing intentionally */ case BP_VAR_UNSET: case BP_VAR_IS: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined offset: " ZEND_ULONG_FMT, hval); + zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); /* break missing intentionally */ case BP_VAR_W: retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval)); @@ -1458,8 +1590,8 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * { zval *retval; -try_again: if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { +try_array: SEPARATE_ARRAY(container); fetch_from_array: if (dim == NULL) { @@ -1472,7 +1604,14 @@ fetch_from_array: retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type); } ZVAL_INDIRECT(result, retval); - } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { + return; + } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto try_array; + } + } + if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) { zval_ptr_dtor_nogc(container); convert_to_array: @@ -1534,9 +1673,6 @@ convert_to_array: /* for read-mode only */ ZVAL_NULL(result); } - } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { - container = Z_REFVAL_P(container); - goto try_again; } else { if (type == BP_VAR_UNSET) { zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); @@ -1567,11 +1703,18 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z { zval *retval; -try_again: if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { +try_array: retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type); ZVAL_COPY(result, retval); - } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { + return; + } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto try_array; + } + } + if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { zend_long offset; try_string_offset: @@ -1637,9 +1780,6 @@ try_string_offset: ZVAL_NULL(result); } } - } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { - container = Z_REFVAL_P(container); - goto try_again; } else { ZVAL_NULL(result); } @@ -1742,21 +1882,17 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of do { ZEND_ASSERT(array_offset != -1); jmp_to = &op_array->brk_cont_array[array_offset]; - if (nest_levels>1) { + if (nest_levels > 1 && jmp_to->start >= 0) { zend_op *brk_opline = &op_array->opcodes[jmp_to->brk]; if (brk_opline->opcode == ZEND_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } + zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } else if (brk_opline->opcode == ZEND_FE_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval *var = EX_VAR(brk_opline->op1.var); - if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { - zend_hash_iterator_del(Z_FE_ITER_P(var)); - } - zval_ptr_dtor_nogc(var); + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); } + zval_ptr_dtor_nogc(var); } } array_offset = jmp_to->parent; diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 706d913f72..9687a556af 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -176,8 +176,10 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ if (finally_op_num) { zval *fast_call = ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[finally_op_end].op1.var); - Z_OBJ_P(fast_call) = NULL; + Z_OBJ_P(fast_call) = EG(exception); + EG(exception) = NULL; fast_call->u2.lineno = (uint32_t)-1; + ex->opline = &ex->func->op_array.opcodes[finally_op_num]; generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator); @@ -817,7 +819,10 @@ ZEND_METHOD(Generator, current) root = zend_generator_get_current(generator); if (Z_TYPE(root->value) != IS_UNDEF) { - RETURN_ZVAL_FAST(&root->value); + zval *value = &root->value; + + ZVAL_DEREF(value); + ZVAL_COPY(return_value, value); } } /* }}} */ @@ -838,7 +843,10 @@ ZEND_METHOD(Generator, key) root = zend_generator_get_current(generator); if (Z_TYPE(root->key) != IS_UNDEF) { - RETURN_ZVAL_FAST(&root->key); + zval *key = &root->key; + + ZVAL_DEREF(key); + ZVAL_COPY(return_value, key); } } /* }}} */ @@ -892,7 +900,10 @@ ZEND_METHOD(Generator, send) root = zend_generator_get_current(generator); if (Z_TYPE(root->value) != IS_UNDEF) { - RETURN_ZVAL_FAST(&root->value); + zval *value = &root->value; + + ZVAL_DEREF(value); + ZVAL_COPY(return_value, value); } } /* }}} */ @@ -923,7 +934,10 @@ ZEND_METHOD(Generator, throw) root = zend_generator_get_current(generator); if (Z_TYPE(root->value) != IS_UNDEF) { - RETURN_ZVAL_FAST(&root->value); + zval *value = &root->value; + + ZVAL_DEREF(value); + ZVAL_COPY(return_value, value); } } else { /* If the generator is already closed throw the exception in the @@ -1021,7 +1035,10 @@ static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval root = zend_generator_get_current(generator); if (Z_TYPE(root->key) != IS_UNDEF) { - ZVAL_ZVAL(key, &root->key, 1, 0); + zval *zv = &root->key; + + ZVAL_DEREF(zv); + ZVAL_COPY(key, zv); } else { ZVAL_NULL(key); } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 326955a103..28487a2a4a 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -249,6 +249,12 @@ struct _zend_ini_scanner_globals { int scanner_mode; }; +typedef enum { + ON_TOKEN, + ON_FEEDBACK, + ON_STOP +} zend_php_scanner_event; + struct _zend_php_scanner_globals { zend_file_handle *yy_in; zend_file_handle *yy_out; @@ -278,6 +284,9 @@ struct _zend_php_scanner_globals { /* initial string length after scanning to first variable */ int scanned_string_len; + + /* hooks */ + void (* on_event)(zend_php_scanner_event event, int token, int line); }; #endif /* ZEND_GLOBALS_H */ diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 75bcb2142d..05426412fe 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -28,7 +28,7 @@ # define HT_ASSERT(c) ZEND_ASSERT(c) #else # define HT_ASSERT(c) -#endif +#endif #if ZEND_DEBUG /* @@ -71,7 +71,7 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin #define HASH_PROTECT_RECURSION(ht) \ if ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) { \ - if ((ht)->u.flags >= (3 << 8)) { \ + if (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >= (3 << 8)) { \ zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");\ } \ ZEND_HASH_INC_APPLY_COUNT(ht); \ @@ -125,13 +125,13 @@ static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed) HT_ASSERT(GC_REFCOUNT(ht) == 1); ZEND_ASSERT(!((ht)->u.flags & HASH_FLAG_INITIALIZED)); if (packed) { - (ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT)); + (ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; HT_HASH_RESET_PACKED(ht); } else { - (ht)->u.flags |= HASH_FLAG_INITIALIZED; (ht)->nTableMask = -(ht)->nTableSize; HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT)); + (ht)->u.flags |= HASH_FLAG_INITIALIZED; HT_HASH_RESET(ht); } } @@ -219,7 +219,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht) ZEND_API void ZEND_FASTCALL _zend_hash_init_ex(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent, zend_bool bApplyProtection ZEND_FILE_LINE_DC) { - _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_CC); + _zend_hash_init(ht, nSize, pDestructor, persistent ZEND_FILE_LINE_RELAY_CC); if (!bApplyProtection) { ht->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; } @@ -567,7 +567,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_add_new(HashTable *ht, zend_string *key, ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_or_update(HashTable *ht, const char *str, size_t len, zval *pData, uint32_t flag ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); - zval *ret = _zend_hash_add_or_update_i(ht, key, pData, flag ZEND_FILE_LINE_CC); + zval *ret = _zend_hash_add_or_update_i(ht, key, pData, flag ZEND_FILE_LINE_RELAY_CC); zend_string_release(key); return ret; } @@ -575,7 +575,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_or_update(HashTable *ht, const c ZEND_API zval* ZEND_FASTCALL _zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); - zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE ZEND_FILE_LINE_CC); + zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE ZEND_FILE_LINE_RELAY_CC); zend_string_release(key); return ret; } @@ -583,7 +583,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_str_update(HashTable *ht, const char *st ZEND_API zval* ZEND_FASTCALL _zend_hash_str_update_ind(HashTable *ht, const char *str, size_t len, zval *pData ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); - zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_CC); + zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_UPDATE | HASH_UPDATE_INDIRECT ZEND_FILE_LINE_RELAY_CC); zend_string_release(key); return ret; } @@ -591,7 +591,7 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_str_update_ind(HashTable *ht, const char ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add(HashTable *ht, const char *str, size_t len, zval *pData ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); - zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD ZEND_FILE_LINE_CC); + zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD ZEND_FILE_LINE_RELAY_CC); zend_string_release(key); return ret; } @@ -599,14 +599,13 @@ ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add(HashTable *ht, const char *str, ZEND_API zval* ZEND_FASTCALL _zend_hash_str_add_new(HashTable *ht, const char *str, size_t len, zval *pData ZEND_FILE_LINE_DC) { zend_string *key = zend_string_init(str, len, ht->u.flags & HASH_FLAG_PERSISTENT); - zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW ZEND_FILE_LINE_CC); - zend_string_release(key); + zval *ret = _zend_hash_add_or_update_i(ht, key, pData, HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC); + zend_string_delref(key); return ret; } ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_empty_element(HashTable *ht, zend_ulong h) { - zval dummy; ZVAL_NULL(&dummy); @@ -615,7 +614,6 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_empty_element(HashTable *ht, ze ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_string *key) { - zval dummy; ZVAL_NULL(&dummy); @@ -624,7 +622,6 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_str ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_empty_element(HashTable *ht, const char *str, size_t len) { - zval dummy; ZVAL_NULL(&dummy); @@ -1698,7 +1695,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->pDestructor = source->pDestructor; if (source->nNumUsed == 0) { - target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PERSISTENT)) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS; + target->u.flags = (source->u.flags & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED|HASH_FLAG_PERSISTENT)) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS; target->nTableMask = HT_MIN_MASK; target->nNumUsed = 0; target->nNumOfElements = 0; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index dcac9c355c..e55ee9e61f 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -589,6 +589,15 @@ static zend_always_inline void *zend_hash_index_add_ptr(HashTable *ht, zend_ulon return zv ? Z_PTR_P(zv) : NULL; } +static zend_always_inline void *zend_hash_index_add_new_ptr(HashTable *ht, zend_ulong h, void *pData) +{ + zval tmp, *zv; + + ZVAL_PTR(&tmp, pData); + zv = zend_hash_index_add_new(ht, h, &tmp); + return zv ? Z_PTR_P(zv) : NULL; +} + static zend_always_inline void *zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData) { zval tmp, *zv; @@ -845,8 +854,9 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, #define ZEND_HASH_APPLY_PROTECTION(ht) \ ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) -#define ZEND_HASH_APPLY_SHIFT 8 -#define ZEND_HASH_GET_APPLY_COUNT(ht) ((ht)->u.flags >> ZEND_HASH_APPLY_SHIFT) +#define ZEND_HASH_APPLY_SHIFT 8 +#define ZEND_HASH_APPLY_COUNT_MASK 0xff00 +#define ZEND_HASH_GET_APPLY_COUNT(ht) (((ht)->u.flags & ZEND_HASH_APPLY_COUNT_MASK) >> ZEND_HASH_APPLY_SHIFT) #define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->u.flags += (1 << ZEND_HASH_APPLY_SHIFT)) #define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT)) @@ -877,7 +887,7 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, __fill_ht->nInternalPointer = 0; \ } while (0) -static zend_always_inline void _zend_hash_append(HashTable *ht, zend_string *key, zval *zv) +static zend_always_inline zval *_zend_hash_append(HashTable *ht, zend_string *key, zval *zv) { uint32_t idx = ht->nNumUsed++; uint32_t nIndex; @@ -896,9 +906,10 @@ static zend_always_inline void _zend_hash_append(HashTable *ht, zend_string *key HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); ht->nNumUsed = idx + 1; ht->nNumOfElements++; + return &p->val; } -static zend_always_inline void _zend_hash_append_ptr(HashTable *ht, zend_string *key, void *ptr) +static zend_always_inline zval *_zend_hash_append_ptr(HashTable *ht, zend_string *key, void *ptr) { uint32_t idx = ht->nNumUsed++; uint32_t nIndex; @@ -917,6 +928,7 @@ static zend_always_inline void _zend_hash_append_ptr(HashTable *ht, zend_string HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); ht->nNumUsed = idx + 1; ht->nNumOfElements++; + return &p->val; } static zend_always_inline void _zend_hash_append_ind(HashTable *ht, zend_string *key, zval *ptr) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index f32c55aaef..608af74353 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1451,7 +1451,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce) /* {{{ */ * then check whether the property is already there */ flags = property_info->flags; - if ((flags & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) { + if (flags & ZEND_ACC_PUBLIC) { prop_name = zend_string_copy(property_info->name); } else { const char *pname; diff --git a/Zend/zend_ini_scanner.c b/Zend/zend_ini_scanner.c index 990a3733cd..6eb1ff716e 100644 --- a/Zend/zend_ini_scanner.c +++ b/Zend/zend_ini_scanner.c @@ -1937,7 +1937,7 @@ end_raw_value_chars: } /* Eat leading and trailing double quotes */ - if (yytext[0] == '"' && yytext[yyleng - 1] == '"') { + if (yyleng > 1 && yytext[0] == '"' && yytext[yyleng - 1] == '"') { SCNG(yy_text)++; yyleng = yyleng - 2; } else if (sc) { diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l index c82d79383f..8cc7266ebb 100644 --- a/Zend/zend_ini_scanner.l +++ b/Zend/zend_ini_scanner.l @@ -524,7 +524,7 @@ end_raw_value_chars: } /* Eat leading and trailing double quotes */ - if (yytext[0] == '"' && yytext[yyleng - 1] == '"') { + if (yyleng > 1 && yytext[0] == '"' && yytext[yyleng - 1] == '"') { SCNG(yy_text)++; yyleng = yyleng - 2; } else if (sc) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2541c9f571..9612324eac 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -21,20 +21,13 @@ /* $Id$ */ -/* - * LALR shift/reduce conflicts and how they are resolved: - * - * - 2 shift/reduce conflicts due to the dangling elseif/else ambiguity. Solved by shift. - * - */ - - #include "zend_compile.h" #include "zend.h" #include "zend_list.h" #include "zend_globals.h" #include "zend_API.h" #include "zend_constants.h" +#include "zend_language_scanner.h" #define YYSIZE_T size_t #define yytnamerr zend_yytnamerr @@ -51,7 +44,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %} %pure_parser -%expect 2 +%expect 0 %code requires { } @@ -87,6 +80,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %right T_POW %right '[' %nonassoc T_NEW T_CLONE +%left T_NOELSE %left T_ELSEIF %left T_ELSE %left T_ENDIF @@ -243,8 +237,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type <ast> absolute_trait_method_reference trait_method_reference property echo_expr %type <ast> new_expr anonymous_class class_name class_name_reference simple_variable %type <ast> internal_functions_in_yacc -%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name -%type <ast> variable_class_name dereferencable_scalar class_name_scalar constant dereferencable +%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name property_name +%type <ast> variable_class_name dereferencable_scalar constant dereferencable %type <ast> callable_expr callable_variable static_member new_variable %type <ast> assignment_list_element array_pair encaps_var encaps_var_offset isset_variables %type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt @@ -252,13 +246,14 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type <ast> echo_expr_list unset_variables catch_list parameter_list class_statement_list %type <ast> implements_list case_list if_stmt_without_else %type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list -%type <ast> class_const_list name_list trait_adaptations method_body non_empty_for_exprs +%type <ast> class_const_list class_const_decl name_list trait_adaptations method_body non_empty_for_exprs %type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars %type <ast> lexical_var_list encaps_list array_pair_list non_empty_array_pair_list %type <ast> assignment_list isset_variable type return_type +%type <ast> identifier %type <num> returns_ref function is_reference is_variadic variable_modifiers -%type <num> method_modifiers trait_modifiers non_empty_member_modifiers member_modifier +%type <num> method_modifiers non_empty_member_modifiers member_modifier %type <num> class_modifiers class_modifier use_type %type <str> backup_doc_comment @@ -269,6 +264,29 @@ start: top_statement_list { CG(ast) = $1; } ; +reserved_non_modifiers: + T_INCLUDE | T_INCLUDE_ONCE | T_EVAL | T_REQUIRE | T_REQUIRE_ONCE | T_LOGICAL_OR | T_LOGICAL_XOR | T_LOGICAL_AND + | T_INSTANCEOF | T_NEW | T_CLONE | T_EXIT | T_IF | T_ELSEIF | T_ELSE | T_ENDIF | T_ECHO | T_DO | T_WHILE | T_ENDWHILE + | T_FOR | T_ENDFOR | T_FOREACH | T_ENDFOREACH | T_DECLARE | T_ENDDECLARE | T_AS | T_TRY | T_CATCH | T_FINALLY + | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO + | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_BREAK + | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS +; + +semi_reserved: + reserved_non_modifiers + | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC +; + +identifier: + T_STRING { $$ = $1; } + | semi_reserved { + zval zv; + zend_lex_tstring(&zv); + $$ = zend_ast_create_zval(&zv); + } +; + top_statement_list: top_statement_list top_statement { $$ = zend_ast_list_add($1, $2); } | /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_STMT_LIST); } @@ -561,7 +579,7 @@ if_stmt_without_else: ; if_stmt: - if_stmt_without_else { $$ = $1; } + if_stmt_without_else %prec T_NOELSE { $$ = $1; } | if_stmt_without_else T_ELSE statement { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_IF_ELEM, NULL, $3)); } ; @@ -673,7 +691,7 @@ class_statement: { $$ = $2; RESET_DOC_COMMENT(); } | T_USE name_list trait_adaptations { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } - | method_modifiers function returns_ref T_STRING '(' parameter_list ')' + | method_modifiers function returns_ref identifier '(' parameter_list ')' return_type backup_doc_comment method_body { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1, $2, $9, zend_ast_get_str($4), $6, NULL, $10, $8); } @@ -708,28 +726,27 @@ trait_precedence: ; trait_alias: - trait_method_reference T_AS trait_modifiers T_STRING + trait_method_reference T_AS T_STRING + { $$ = zend_ast_create_ex(ZEND_AST_TRAIT_ALIAS, 0, $1, $3); } + | trait_method_reference T_AS reserved_non_modifiers + { zval zv; zend_lex_tstring(&zv); $$ = zend_ast_create_ex(ZEND_AST_TRAIT_ALIAS, 0, $1, zend_ast_create_zval(&zv)); } + | trait_method_reference T_AS member_modifier identifier { $$ = zend_ast_create_ex(ZEND_AST_TRAIT_ALIAS, $3, $1, $4); } - | trait_method_reference T_AS member_modifier + | trait_method_reference T_AS member_modifier %prec '+' { $$ = zend_ast_create_ex(ZEND_AST_TRAIT_ALIAS, $3, $1, NULL); } ; trait_method_reference: - T_STRING + identifier { $$ = zend_ast_create(ZEND_AST_METHOD_REFERENCE, NULL, $1); } | absolute_trait_method_reference { $$ = $1; } ; absolute_trait_method_reference: - name T_PAAMAYIM_NEKUDOTAYIM T_STRING + name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = zend_ast_create(ZEND_AST_METHOD_REFERENCE, $1, $3); } ; -trait_modifiers: - /* empty */ { $$ = 0; } - | member_modifier { $$ = $1; } -; - method_body: ';' /* abstract method */ { $$ = NULL; } | '{' inner_statement_list '}' { $$ = $2; } @@ -773,8 +790,12 @@ property: ; class_const_list: - class_const_list ',' const_decl { $$ = zend_ast_list_add($1, $3); } - | const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); } + class_const_list ',' class_const_decl { $$ = zend_ast_list_add($1, $3); } + | class_const_decl { $$ = zend_ast_create_list(1, ZEND_AST_CLASS_CONST_DECL, $1); } +; + +class_const_decl: + identifier '=' expr { $$ = zend_ast_create(ZEND_AST_CONST_ELEM, $1, $3); } ; const_decl: @@ -1028,15 +1049,14 @@ scalar: | '"' encaps_list '"' { $$ = $2; } | T_START_HEREDOC encaps_list T_END_HEREDOC { $$ = $2; } | dereferencable_scalar { $$ = $1; } - | class_name_scalar { $$ = $1; } | constant { $$ = $1; } ; constant: name { $$ = zend_ast_create(ZEND_AST_CONST, $1); } - | class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING + | class_name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); } - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM identifier { $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); } ; @@ -1080,7 +1100,7 @@ callable_variable: { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); } | dereferencable '{' expr '}' { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); } - | dereferencable T_OBJECT_OPERATOR member_name argument_list + | dereferencable T_OBJECT_OPERATOR property_name argument_list { $$ = zend_ast_create(ZEND_AST_METHOD_CALL, $1, $3, $4); } | function_call { $$ = $1; } ; @@ -1090,7 +1110,7 @@ variable: { $$ = $1; } | static_member { $$ = $1; } - | dereferencable T_OBJECT_OPERATOR member_name + | dereferencable T_OBJECT_OPERATOR property_name { $$ = zend_ast_create(ZEND_AST_PROP, $1, $3); } ; @@ -1114,7 +1134,7 @@ new_variable: { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); } | new_variable '{' expr '}' { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); } - | new_variable T_OBJECT_OPERATOR member_name + | new_variable T_OBJECT_OPERATOR property_name { $$ = zend_ast_create(ZEND_AST_PROP, $1, $3); } | class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { $$ = zend_ast_create(ZEND_AST_STATIC_PROP, $1, $3); } @@ -1123,7 +1143,13 @@ new_variable: ; member_name: - T_STRING { $$ = $1; } + identifier { $$ = $1; } + | '{' expr '}' { $$ = $2; } + | simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); } +; + +property_name: + T_STRING { $$ = $1; } | '{' expr '}' { $$ = $2; } | simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); } ; @@ -1226,11 +1252,6 @@ isset_variable: expr { $$ = zend_ast_create(ZEND_AST_ISSET, $1); } ; -class_name_scalar: - class_name T_PAAMAYIM_NEKUDOTAYIM T_CLASS - { $$ = zend_ast_create(ZEND_AST_RESOLVE_CLASS_NAME, $1); } -; - %% /* Copy to YYRES the contents of YYSTR after stripping away unnecessary diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c index e3b2de1a79..6813cf4db2 100644 --- a/Zend/zend_language_scanner.c +++ b/Zend/zend_language_scanner.c @@ -195,6 +195,7 @@ void shutdown_scanner(void) zend_stack_destroy(&SCNG(state_stack)); zend_ptr_stack_clean(&SCNG(heredoc_label_stack), (void (*)(void *)) &heredoc_label_dtor, 1); zend_ptr_stack_destroy(&SCNG(heredoc_label_stack)); + SCNG(on_event) = NULL; } ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state) @@ -225,6 +226,8 @@ ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state) lex_state->output_filter = SCNG(output_filter); lex_state->script_encoding = SCNG(script_encoding); + lex_state->on_event = SCNG(on_event); + lex_state->ast = CG(ast); lex_state->ast_arena = CG(ast_arena); } @@ -262,6 +265,8 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state) SCNG(output_filter) = lex_state->output_filter; SCNG(script_encoding) = lex_state->script_encoding; + SCNG(on_event) = lex_state->on_event; + CG(ast) = lex_state->ast; CG(ast_arena) = lex_state->ast_arena; @@ -278,6 +283,13 @@ ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) } } +ZEND_API void zend_lex_tstring(zval *zv) +{ + if (SCNG(on_event)) SCNG(on_event)(ON_FEEDBACK, T_STRING, 0); + + ZVAL_STRINGL(zv, (char*)SCNG(yy_text), SCNG(yy_leng)); +} + #define BOM_UTF32_BE "\x00\x00\xfe\xff" #define BOM_UTF32_LE "\xff\xfe\x00\x00" #define BOM_UTF16_BE "\xfe\xff" @@ -1085,14 +1097,25 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot return SUCCESS; } +static zend_always_inline int emit_token(int token, int token_line) +{ + if(SCNG(on_event)) SCNG(on_event)(ON_TOKEN, token, token_line); + + return token; +} + +#define RETURN_TOKEN(token) return emit_token(token, start_line); int lex_scan(zval *zendlval) { + +int start_line = CG(zend_lineno); + restart: SCNG(yy_text) = YYCURSOR; -#line 1096 "Zend/zend_language_scanner.c" +#line 1119 "Zend/zend_language_scanner.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -1146,10 +1169,10 @@ yyc_INITIAL: yy3: YYDEBUG(3, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1784 "Zend/zend_language_scanner.l" +#line 1807 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } inline_char_handler: @@ -1189,9 +1212,9 @@ inline_char_handler: ZVAL_STRINGL(zendlval, yytext, yyleng); } HANDLE_NEWLINES(yytext, yyleng); - return T_INLINE_HTML; + RETURN_TOKEN(T_INLINE_HTML); } -#line 1195 "Zend/zend_language_scanner.c" +#line 1218 "Zend/zend_language_scanner.c" yy4: YYDEBUG(4, *YYCURSOR); yych = *++YYCURSOR; @@ -1209,27 +1232,27 @@ yy5: yy6: YYDEBUG(6, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1775 "Zend/zend_language_scanner.l" +#line 1798 "Zend/zend_language_scanner.l" { if (CG(short_tags)) { BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG; + RETURN_TOKEN(T_OPEN_TAG); } else { goto inline_char_handler; } } -#line 1222 "Zend/zend_language_scanner.c" +#line 1245 "Zend/zend_language_scanner.c" yy7: YYDEBUG(7, *YYCURSOR); ++YYCURSOR; YYDEBUG(8, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1762 "Zend/zend_language_scanner.l" +#line 1785 "Zend/zend_language_scanner.l" { BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG_WITH_ECHO; + RETURN_TOKEN(T_OPEN_TAG_WITH_ECHO); } -#line 1233 "Zend/zend_language_scanner.c" +#line 1256 "Zend/zend_language_scanner.c" yy9: YYDEBUG(9, *YYCURSOR); yych = *++YYCURSOR; @@ -1260,13 +1283,13 @@ yy13: yy14: YYDEBUG(14, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1768 "Zend/zend_language_scanner.l" +#line 1791 "Zend/zend_language_scanner.l" { HANDLE_NEWLINE(yytext[yyleng-1]); BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG; + RETURN_TOKEN(T_OPEN_TAG); } -#line 1270 "Zend/zend_language_scanner.c" +#line 1293 "Zend/zend_language_scanner.c" yy15: YYDEBUG(15, *YYCURSOR); ++YYCURSOR; @@ -1336,10 +1359,10 @@ yyc_ST_BACKQUOTE: yy19: YYDEBUG(19, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2198 "Zend/zend_language_scanner.l" +#line 2221 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } if (yytext[0] == '\\' && YYCURSOR < YYLIMIT) { YYCURSOR++; @@ -1375,11 +1398,11 @@ yy19: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng, '`') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } -#line 1383 "Zend/zend_language_scanner.c" +#line 1406 "Zend/zend_language_scanner.c" yy20: YYDEBUG(20, *YYCURSOR); yych = *++YYCURSOR; @@ -1390,12 +1413,12 @@ yy21: ++YYCURSOR; YYDEBUG(22, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2140 "Zend/zend_language_scanner.l" +#line 2163 "Zend/zend_language_scanner.l" { BEGIN(ST_IN_SCRIPTING); - return '`'; + RETURN_TOKEN('`'); } -#line 1399 "Zend/zend_language_scanner.c" +#line 1422 "Zend/zend_language_scanner.c" yy23: YYDEBUG(23, *YYCURSOR); yych = *++YYCURSOR; @@ -1405,14 +1428,14 @@ yy24: ++YYCURSOR; YYDEBUG(25, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2127 "Zend/zend_language_scanner.l" +#line 2150 "Zend/zend_language_scanner.l" { Z_LVAL_P(zendlval) = (zend_long) '{'; yy_push_state(ST_IN_SCRIPTING); yyless(1); - return T_CURLY_OPEN; + RETURN_TOKEN(T_CURLY_OPEN); } -#line 1416 "Zend/zend_language_scanner.c" +#line 1439 "Zend/zend_language_scanner.c" yy26: YYDEBUG(26, *YYCURSOR); yyaccept = 0; @@ -1428,23 +1451,23 @@ yy26: yy28: YYDEBUG(28, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1849 "Zend/zend_language_scanner.l" +#line 1872 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1437 "Zend/zend_language_scanner.c" +#line 1460 "Zend/zend_language_scanner.c" yy29: YYDEBUG(29, *YYCURSOR); ++YYCURSOR; YYDEBUG(30, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1570 "Zend/zend_language_scanner.l" +#line 1593 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME); - return T_DOLLAR_OPEN_CURLY_BRACES; + RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES); } -#line 1448 "Zend/zend_language_scanner.c" +#line 1471 "Zend/zend_language_scanner.c" yy31: YYDEBUG(31, *YYCURSOR); yych = *++YYCURSOR; @@ -1458,14 +1481,14 @@ yy33: ++YYCURSOR; YYDEBUG(34, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1842 "Zend/zend_language_scanner.l" +#line 1865 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1469 "Zend/zend_language_scanner.c" +#line 1492 "Zend/zend_language_scanner.c" yy35: YYDEBUG(35, *YYCURSOR); yych = *++YYCURSOR; @@ -1483,14 +1506,14 @@ yy36: ++YYCURSOR; YYDEBUG(37, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1833 "Zend/zend_language_scanner.l" +#line 1856 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1494 "Zend/zend_language_scanner.c" +#line 1517 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_DOUBLE_QUOTES: @@ -1558,7 +1581,7 @@ yy40: yy41: YYDEBUG(41, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2146 "Zend/zend_language_scanner.l" +#line 2169 "Zend/zend_language_scanner.l" { if (GET_DOUBLE_QUOTES_SCANNED_LENGTH()) { YYCURSOR += GET_DOUBLE_QUOTES_SCANNED_LENGTH() - 1; @@ -1568,7 +1591,7 @@ yy41: } if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } if (yytext[0] == '\\' && YYCURSOR < YYLIMIT) { YYCURSOR++; @@ -1605,11 +1628,11 @@ double_quotes_scan_done: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng, '"') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } -#line 1613 "Zend/zend_language_scanner.c" +#line 1636 "Zend/zend_language_scanner.c" yy42: YYDEBUG(42, *YYCURSOR); yych = *++YYCURSOR; @@ -1620,12 +1643,12 @@ yy43: ++YYCURSOR; YYDEBUG(44, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2135 "Zend/zend_language_scanner.l" +#line 2158 "Zend/zend_language_scanner.l" { BEGIN(ST_IN_SCRIPTING); - return '"'; + RETURN_TOKEN('"'); } -#line 1629 "Zend/zend_language_scanner.c" +#line 1652 "Zend/zend_language_scanner.c" yy45: YYDEBUG(45, *YYCURSOR); yych = *++YYCURSOR; @@ -1635,14 +1658,14 @@ yy46: ++YYCURSOR; YYDEBUG(47, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2127 "Zend/zend_language_scanner.l" +#line 2150 "Zend/zend_language_scanner.l" { Z_LVAL_P(zendlval) = (zend_long) '{'; yy_push_state(ST_IN_SCRIPTING); yyless(1); - return T_CURLY_OPEN; + RETURN_TOKEN(T_CURLY_OPEN); } -#line 1646 "Zend/zend_language_scanner.c" +#line 1669 "Zend/zend_language_scanner.c" yy48: YYDEBUG(48, *YYCURSOR); yyaccept = 0; @@ -1658,23 +1681,23 @@ yy48: yy50: YYDEBUG(50, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1849 "Zend/zend_language_scanner.l" +#line 1872 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1667 "Zend/zend_language_scanner.c" +#line 1690 "Zend/zend_language_scanner.c" yy51: YYDEBUG(51, *YYCURSOR); ++YYCURSOR; YYDEBUG(52, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1570 "Zend/zend_language_scanner.l" +#line 1593 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME); - return T_DOLLAR_OPEN_CURLY_BRACES; + RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES); } -#line 1678 "Zend/zend_language_scanner.c" +#line 1701 "Zend/zend_language_scanner.c" yy53: YYDEBUG(53, *YYCURSOR); yych = *++YYCURSOR; @@ -1688,14 +1711,14 @@ yy55: ++YYCURSOR; YYDEBUG(56, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1842 "Zend/zend_language_scanner.l" +#line 1865 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1699 "Zend/zend_language_scanner.c" +#line 1722 "Zend/zend_language_scanner.c" yy57: YYDEBUG(57, *YYCURSOR); yych = *++YYCURSOR; @@ -1713,14 +1736,14 @@ yy58: ++YYCURSOR; YYDEBUG(59, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1833 "Zend/zend_language_scanner.l" +#line 1856 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1724 "Zend/zend_language_scanner.c" +#line 1747 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_END_HEREDOC: @@ -1731,7 +1754,7 @@ yyc_ST_END_HEREDOC: ++YYCURSOR; YYDEBUG(63, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2113 "Zend/zend_language_scanner.l" +#line 2136 "Zend/zend_language_scanner.l" { zend_heredoc_label *heredoc_label = zend_ptr_stack_pop(&SCNG(heredoc_label_stack)); @@ -1742,9 +1765,9 @@ yyc_ST_END_HEREDOC: efree(heredoc_label); BEGIN(ST_IN_SCRIPTING); - return T_END_HEREDOC; + RETURN_TOKEN(T_END_HEREDOC); } -#line 1748 "Zend/zend_language_scanner.c" +#line 1771 "Zend/zend_language_scanner.c" /* *********************************** */ yyc_ST_HEREDOC: { @@ -1806,14 +1829,14 @@ yy66: yy67: YYDEBUG(67, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2242 "Zend/zend_language_scanner.l" +#line 2265 "Zend/zend_language_scanner.l" { int newline = 0; zend_heredoc_label *heredoc_label = zend_ptr_stack_top(&SCNG(heredoc_label_stack)); if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } YYCURSOR--; @@ -1877,11 +1900,11 @@ heredoc_scan_done: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng - newline, 0) == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } -#line 1885 "Zend/zend_language_scanner.c" +#line 1908 "Zend/zend_language_scanner.c" yy68: YYDEBUG(68, *YYCURSOR); yych = *++YYCURSOR; @@ -1896,14 +1919,14 @@ yy70: ++YYCURSOR; YYDEBUG(71, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2127 "Zend/zend_language_scanner.l" +#line 2150 "Zend/zend_language_scanner.l" { Z_LVAL_P(zendlval) = (zend_long) '{'; yy_push_state(ST_IN_SCRIPTING); yyless(1); - return T_CURLY_OPEN; + RETURN_TOKEN(T_CURLY_OPEN); } -#line 1907 "Zend/zend_language_scanner.c" +#line 1930 "Zend/zend_language_scanner.c" yy72: YYDEBUG(72, *YYCURSOR); yyaccept = 0; @@ -1919,23 +1942,23 @@ yy72: yy74: YYDEBUG(74, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1849 "Zend/zend_language_scanner.l" +#line 1872 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1928 "Zend/zend_language_scanner.c" +#line 1951 "Zend/zend_language_scanner.c" yy75: YYDEBUG(75, *YYCURSOR); ++YYCURSOR; YYDEBUG(76, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1570 "Zend/zend_language_scanner.l" +#line 1593 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME); - return T_DOLLAR_OPEN_CURLY_BRACES; + RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES); } -#line 1939 "Zend/zend_language_scanner.c" +#line 1962 "Zend/zend_language_scanner.c" yy77: YYDEBUG(77, *YYCURSOR); yych = *++YYCURSOR; @@ -1949,14 +1972,14 @@ yy79: ++YYCURSOR; YYDEBUG(80, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1842 "Zend/zend_language_scanner.l" +#line 1865 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1960 "Zend/zend_language_scanner.c" +#line 1983 "Zend/zend_language_scanner.c" yy81: YYDEBUG(81, *YYCURSOR); yych = *++YYCURSOR; @@ -1974,14 +1997,14 @@ yy82: ++YYCURSOR; YYDEBUG(83, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1833 "Zend/zend_language_scanner.l" +#line 1856 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 1985 "Zend/zend_language_scanner.c" +#line 2008 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_IN_SCRIPTING: @@ -2164,12 +2187,12 @@ yy86: yy87: YYDEBUG(87, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1872 "Zend/zend_language_scanner.l" +#line 1895 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, yytext, yyleng); - return T_STRING; + RETURN_TOKEN(T_STRING); } -#line 2173 "Zend/zend_language_scanner.c" +#line 2196 "Zend/zend_language_scanner.c" yy88: YYDEBUG(88, *YYCURSOR); yych = *++YYCURSOR; @@ -2401,11 +2424,11 @@ yy101: yy102: YYDEBUG(102, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1559 "Zend/zend_language_scanner.l" +#line 1582 "Zend/zend_language_scanner.l" { - return yytext[0]; + RETURN_TOKEN(yytext[0]); } -#line 2409 "Zend/zend_language_scanner.c" +#line 2432 "Zend/zend_language_scanner.c" yy103: YYDEBUG(103, *YYCURSOR); ++YYCURSOR; @@ -2414,12 +2437,12 @@ yy103: yy104: YYDEBUG(104, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1274 "Zend/zend_language_scanner.l" +#line 1297 "Zend/zend_language_scanner.l" { HANDLE_NEWLINES(yytext, yyleng); - return T_WHITESPACE; + RETURN_TOKEN(T_WHITESPACE); } -#line 2423 "Zend/zend_language_scanner.c" +#line 2446 "Zend/zend_language_scanner.c" yy105: YYDEBUG(105, *YYCURSOR); yych = *++YYCURSOR; @@ -2430,11 +2453,11 @@ yy106: ++YYCURSOR; YYDEBUG(107, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1299 "Zend/zend_language_scanner.l" +#line 1322 "Zend/zend_language_scanner.l" { - return T_NS_SEPARATOR; + RETURN_TOKEN(T_NS_SEPARATOR); } -#line 2438 "Zend/zend_language_scanner.c" +#line 2461 "Zend/zend_language_scanner.c" yy108: YYDEBUG(108, *YYCURSOR); yyaccept = 1; @@ -2663,26 +2686,26 @@ yy131: ++YYCURSOR; YYDEBUG(132, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1564 "Zend/zend_language_scanner.l" +#line 1587 "Zend/zend_language_scanner.l" { yy_push_state(ST_IN_SCRIPTING); - return '{'; + RETURN_TOKEN('{'); } -#line 2672 "Zend/zend_language_scanner.c" +#line 2695 "Zend/zend_language_scanner.c" yy133: YYDEBUG(133, *YYCURSOR); ++YYCURSOR; YYDEBUG(134, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1576 "Zend/zend_language_scanner.l" +#line 1599 "Zend/zend_language_scanner.l" { RESET_DOC_COMMENT(); if (!zend_stack_is_empty(&SCNG(state_stack))) { yy_pop_state(); } - return '}'; + RETURN_TOKEN('}'); } -#line 2686 "Zend/zend_language_scanner.c" +#line 2709 "Zend/zend_language_scanner.c" yy135: YYDEBUG(135, *YYCURSOR); yyaccept = 2; @@ -2710,7 +2733,7 @@ yy135: yy136: YYDEBUG(136, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1629 "Zend/zend_language_scanner.l" +#line 1652 "Zend/zend_language_scanner.l" { char *end; if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */ @@ -2721,7 +2744,7 @@ yy136: */ if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } } else { errno = 0; @@ -2738,21 +2761,21 @@ yy136: if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } ZEND_ASSERT(!errno); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } } ZEND_ASSERT(!errno); - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } -#line 2756 "Zend/zend_language_scanner.c" +#line 2779 "Zend/zend_language_scanner.c" yy137: YYDEBUG(137, *YYCURSOR); yyaccept = 2; @@ -2780,7 +2803,7 @@ yy139: yy140: YYDEBUG(140, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1878 "Zend/zend_language_scanner.l" +#line 1901 "Zend/zend_language_scanner.l" { while (YYCURSOR < YYLIMIT) { switch (*YYCURSOR++) { @@ -2807,16 +2830,16 @@ yy140: yyleng = YYCURSOR - SCNG(yy_text); - return T_COMMENT; + RETURN_TOKEN(T_COMMENT); } -#line 2813 "Zend/zend_language_scanner.c" +#line 2836 "Zend/zend_language_scanner.c" yy141: YYDEBUG(141, *YYCURSOR); ++YYCURSOR; yy142: YYDEBUG(142, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1946 "Zend/zend_language_scanner.l" +#line 1969 "Zend/zend_language_scanner.l" { register char *s, *t; char *end; @@ -2839,7 +2862,7 @@ yy142: * for ' (unrecognized by parser), instead of old flex fallback to "Unexpected character..." * rule, which continued in ST_IN_SCRIPTING state after the quote */ ZVAL_NULL(zendlval); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } } @@ -2882,16 +2905,16 @@ yy142: SCNG(output_filter)((unsigned char **)&str, &sz, (unsigned char *)s, (size_t)Z_STRLEN_P(zendlval)); ZVAL_STRINGL(zendlval, str, sz); } - return T_CONSTANT_ENCAPSED_STRING; + RETURN_TOKEN(T_CONSTANT_ENCAPSED_STRING); } -#line 2888 "Zend/zend_language_scanner.c" +#line 2911 "Zend/zend_language_scanner.c" yy143: YYDEBUG(143, *YYCURSOR); ++YYCURSOR; yy144: YYDEBUG(144, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2015 "Zend/zend_language_scanner.l" +#line 2038 "Zend/zend_language_scanner.l" { int bprefix = (yytext[0] != '"') ? 1 : 0; @@ -2900,9 +2923,9 @@ yy144: case '"': yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext+bprefix+1, yyleng-bprefix-2, '"') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_CONSTANT_ENCAPSED_STRING; + RETURN_TOKEN(T_CONSTANT_ENCAPSED_STRING); case '$': if (IS_LABEL_START(*YYCURSOR) || *YYCURSOR == '{') { break; @@ -2932,35 +2955,35 @@ yy144: YYCURSOR = SCNG(yy_text) + yyleng; BEGIN(ST_DOUBLE_QUOTES); - return '"'; + RETURN_TOKEN('"'); } -#line 2938 "Zend/zend_language_scanner.c" +#line 2961 "Zend/zend_language_scanner.c" yy145: YYDEBUG(145, *YYCURSOR); ++YYCURSOR; YYDEBUG(146, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2107 "Zend/zend_language_scanner.l" +#line 2130 "Zend/zend_language_scanner.l" { BEGIN(ST_BACKQUOTE); - return '`'; + RETURN_TOKEN('`'); } -#line 2949 "Zend/zend_language_scanner.c" +#line 2972 "Zend/zend_language_scanner.c" yy147: YYDEBUG(147, *YYCURSOR); ++YYCURSOR; YYDEBUG(148, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2375 "Zend/zend_language_scanner.l" +#line 2398 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); goto restart; } -#line 2964 "Zend/zend_language_scanner.c" +#line 2987 "Zend/zend_language_scanner.c" yy149: YYDEBUG(149, *YYCURSOR); ++YYCURSOR; @@ -2987,16 +3010,16 @@ yy151: yy153: YYDEBUG(153, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1720 "Zend/zend_language_scanner.l" +#line 1743 "Zend/zend_language_scanner.l" { const char *end; ZVAL_DOUBLE(zendlval, zend_strtod(yytext, &end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == yytext + yyleng); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } -#line 3000 "Zend/zend_language_scanner.c" +#line 3023 "Zend/zend_language_scanner.c" yy154: YYDEBUG(154, *YYCURSOR); yyaccept = 2; @@ -3092,7 +3115,7 @@ yy163: } YYDEBUG(165, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1601 "Zend/zend_language_scanner.l" +#line 1624 "Zend/zend_language_scanner.l" { char *bin = yytext + 2; /* Skip "0b" */ int len = yyleng - 2; @@ -3112,15 +3135,15 @@ yy163: ZVAL_LONG(zendlval, ZEND_STRTOL(bin, &end, 2)); ZEND_ASSERT(!errno && end == yytext + yyleng); } - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } else { ZVAL_DOUBLE(zendlval, zend_bin_strtod(bin, (const char **)&end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == yytext + yyleng); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } } -#line 3124 "Zend/zend_language_scanner.c" +#line 3147 "Zend/zend_language_scanner.c" yy166: YYDEBUG(166, *YYCURSOR); ++YYCURSOR; @@ -3132,7 +3155,7 @@ yy166: } YYDEBUG(168, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1671 "Zend/zend_language_scanner.l" +#line 1694 "Zend/zend_language_scanner.l" { char *hex = yytext + 2; /* Skip "0x" */ int len = yyleng - 2; @@ -3152,15 +3175,15 @@ yy166: ZVAL_LONG(zendlval, ZEND_STRTOL(hex, &end, 16)); ZEND_ASSERT(!errno && end == hex + len); } - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } else { ZVAL_DOUBLE(zendlval, zend_hex_strtod(hex, (const char **)&end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == hex + len); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } } -#line 3164 "Zend/zend_language_scanner.c" +#line 3187 "Zend/zend_language_scanner.c" yy169: YYDEBUG(169, *YYCURSOR); ++YYCURSOR; @@ -3185,12 +3208,12 @@ yy169: yy171: YYDEBUG(171, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1849 "Zend/zend_language_scanner.l" +#line 1872 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 3194 "Zend/zend_language_scanner.c" +#line 3217 "Zend/zend_language_scanner.c" yy172: YYDEBUG(172, *YYCURSOR); yych = *++YYCURSOR; @@ -3204,11 +3227,11 @@ yy173: } YYDEBUG(174, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1547 "Zend/zend_language_scanner.l" +#line 1570 "Zend/zend_language_scanner.l" { - return T_LOGICAL_XOR; + RETURN_TOKEN(T_LOGICAL_XOR); } -#line 3212 "Zend/zend_language_scanner.c" +#line 3235 "Zend/zend_language_scanner.c" yy175: YYDEBUG(175, *YYCURSOR); ++YYCURSOR; @@ -3217,71 +3240,71 @@ yy175: } YYDEBUG(176, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1539 "Zend/zend_language_scanner.l" +#line 1562 "Zend/zend_language_scanner.l" { - return T_LOGICAL_OR; + RETURN_TOKEN(T_LOGICAL_OR); } -#line 3225 "Zend/zend_language_scanner.c" +#line 3248 "Zend/zend_language_scanner.c" yy177: YYDEBUG(177, *YYCURSOR); ++YYCURSOR; YYDEBUG(178, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1527 "Zend/zend_language_scanner.l" +#line 1550 "Zend/zend_language_scanner.l" { - return T_XOR_EQUAL; + RETURN_TOKEN(T_XOR_EQUAL); } -#line 3235 "Zend/zend_language_scanner.c" +#line 3258 "Zend/zend_language_scanner.c" yy179: YYDEBUG(179, *YYCURSOR); ++YYCURSOR; YYDEBUG(180, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1531 "Zend/zend_language_scanner.l" +#line 1554 "Zend/zend_language_scanner.l" { - return T_BOOLEAN_OR; + RETURN_TOKEN(T_BOOLEAN_OR); } -#line 3245 "Zend/zend_language_scanner.c" +#line 3268 "Zend/zend_language_scanner.c" yy181: YYDEBUG(181, *YYCURSOR); ++YYCURSOR; YYDEBUG(182, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1523 "Zend/zend_language_scanner.l" +#line 1546 "Zend/zend_language_scanner.l" { - return T_OR_EQUAL; + RETURN_TOKEN(T_OR_EQUAL); } -#line 3255 "Zend/zend_language_scanner.c" +#line 3278 "Zend/zend_language_scanner.c" yy183: YYDEBUG(183, *YYCURSOR); ++YYCURSOR; YYDEBUG(184, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1535 "Zend/zend_language_scanner.l" +#line 1558 "Zend/zend_language_scanner.l" { - return T_BOOLEAN_AND; + RETURN_TOKEN(T_BOOLEAN_AND); } -#line 3265 "Zend/zend_language_scanner.c" +#line 3288 "Zend/zend_language_scanner.c" yy185: YYDEBUG(185, *YYCURSOR); ++YYCURSOR; YYDEBUG(186, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1519 "Zend/zend_language_scanner.l" +#line 1542 "Zend/zend_language_scanner.l" { - return T_AND_EQUAL; + RETURN_TOKEN(T_AND_EQUAL); } -#line 3275 "Zend/zend_language_scanner.c" +#line 3298 "Zend/zend_language_scanner.c" yy187: YYDEBUG(187, *YYCURSOR); ++YYCURSOR; YYDEBUG(188, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1507 "Zend/zend_language_scanner.l" +#line 1530 "Zend/zend_language_scanner.l" { - return T_MOD_EQUAL; + RETURN_TOKEN(T_MOD_EQUAL); } -#line 3285 "Zend/zend_language_scanner.c" +#line 3308 "Zend/zend_language_scanner.c" yy189: YYDEBUG(189, *YYCURSOR); yyaccept = 4; @@ -3290,7 +3313,7 @@ yy189: yy190: YYDEBUG(190, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1907 "Zend/zend_language_scanner.l" +#line 1930 "Zend/zend_language_scanner.l" { int doc_com; @@ -3318,12 +3341,12 @@ yy190: if (doc_com) { CG(doc_comment) = zend_string_init(yytext, yyleng, 0); - return T_DOC_COMMENT; + RETURN_TOKEN(T_DOC_COMMENT); } - return T_COMMENT; + RETURN_TOKEN(T_COMMENT); } -#line 3327 "Zend/zend_language_scanner.c" +#line 3350 "Zend/zend_language_scanner.c" yy191: YYDEBUG(191, *YYCURSOR); yych = *++YYCURSOR; @@ -3333,11 +3356,11 @@ yy192: ++YYCURSOR; YYDEBUG(193, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1499 "Zend/zend_language_scanner.l" +#line 1522 "Zend/zend_language_scanner.l" { - return T_DIV_EQUAL; + RETURN_TOKEN(T_DIV_EQUAL); } -#line 3341 "Zend/zend_language_scanner.c" +#line 3364 "Zend/zend_language_scanner.c" yy194: YYDEBUG(194, *YYCURSOR); yych = *++YYCURSOR; @@ -3361,62 +3384,62 @@ yy197: if ((yych = *YYCURSOR) == '=') goto yy201; YYDEBUG(198, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1491 "Zend/zend_language_scanner.l" +#line 1514 "Zend/zend_language_scanner.l" { - return T_POW; + RETURN_TOKEN(T_POW); } -#line 3369 "Zend/zend_language_scanner.c" +#line 3392 "Zend/zend_language_scanner.c" yy199: YYDEBUG(199, *YYCURSOR); ++YYCURSOR; YYDEBUG(200, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1487 "Zend/zend_language_scanner.l" +#line 1510 "Zend/zend_language_scanner.l" { - return T_MUL_EQUAL; + RETURN_TOKEN(T_MUL_EQUAL); } -#line 3379 "Zend/zend_language_scanner.c" +#line 3402 "Zend/zend_language_scanner.c" yy201: YYDEBUG(201, *YYCURSOR); ++YYCURSOR; YYDEBUG(202, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1495 "Zend/zend_language_scanner.l" +#line 1518 "Zend/zend_language_scanner.l" { - return T_POW_EQUAL; + RETURN_TOKEN(T_POW_EQUAL); } -#line 3389 "Zend/zend_language_scanner.c" +#line 3412 "Zend/zend_language_scanner.c" yy203: YYDEBUG(203, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) == '=') goto yy207; YYDEBUG(204, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1555 "Zend/zend_language_scanner.l" +#line 1578 "Zend/zend_language_scanner.l" { - return T_SR; + RETURN_TOKEN(T_SR); } -#line 3400 "Zend/zend_language_scanner.c" +#line 3423 "Zend/zend_language_scanner.c" yy205: YYDEBUG(205, *YYCURSOR); ++YYCURSOR; YYDEBUG(206, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1475 "Zend/zend_language_scanner.l" +#line 1498 "Zend/zend_language_scanner.l" { - return T_IS_GREATER_OR_EQUAL; + RETURN_TOKEN(T_IS_GREATER_OR_EQUAL); } -#line 3410 "Zend/zend_language_scanner.c" +#line 3433 "Zend/zend_language_scanner.c" yy207: YYDEBUG(207, *YYCURSOR); ++YYCURSOR; YYDEBUG(208, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1515 "Zend/zend_language_scanner.l" +#line 1538 "Zend/zend_language_scanner.l" { - return T_SR_EQUAL; + RETURN_TOKEN(T_SR_EQUAL); } -#line 3420 "Zend/zend_language_scanner.c" +#line 3443 "Zend/zend_language_scanner.c" yy209: YYDEBUG(209, *YYCURSOR); yyaccept = 5; @@ -3427,53 +3450,53 @@ yy209: yy210: YYDEBUG(210, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1551 "Zend/zend_language_scanner.l" +#line 1574 "Zend/zend_language_scanner.l" { - return T_SL; + RETURN_TOKEN(T_SL); } -#line 3435 "Zend/zend_language_scanner.c" +#line 3458 "Zend/zend_language_scanner.c" yy211: YYDEBUG(211, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) == '>') goto yy215; YYDEBUG(212, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1471 "Zend/zend_language_scanner.l" +#line 1494 "Zend/zend_language_scanner.l" { - return T_IS_SMALLER_OR_EQUAL; + RETURN_TOKEN(T_IS_SMALLER_OR_EQUAL); } -#line 3446 "Zend/zend_language_scanner.c" +#line 3469 "Zend/zend_language_scanner.c" yy213: YYDEBUG(213, *YYCURSOR); ++YYCURSOR; yy214: YYDEBUG(214, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1463 "Zend/zend_language_scanner.l" +#line 1486 "Zend/zend_language_scanner.l" { - return T_IS_NOT_EQUAL; + RETURN_TOKEN(T_IS_NOT_EQUAL); } -#line 3457 "Zend/zend_language_scanner.c" +#line 3480 "Zend/zend_language_scanner.c" yy215: YYDEBUG(215, *YYCURSOR); ++YYCURSOR; YYDEBUG(216, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1467 "Zend/zend_language_scanner.l" +#line 1490 "Zend/zend_language_scanner.l" { - return T_SPACESHIP; + RETURN_TOKEN(T_SPACESHIP); } -#line 3467 "Zend/zend_language_scanner.c" +#line 3490 "Zend/zend_language_scanner.c" yy217: YYDEBUG(217, *YYCURSOR); ++YYCURSOR; YYDEBUG(218, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1511 "Zend/zend_language_scanner.l" +#line 1534 "Zend/zend_language_scanner.l" { - return T_SL_EQUAL; + RETURN_TOKEN(T_SL_EQUAL); } -#line 3477 "Zend/zend_language_scanner.c" +#line 3500 "Zend/zend_language_scanner.c" yy219: YYDEBUG(219, *YYCURSOR); ++YYCURSOR; @@ -3578,7 +3601,7 @@ yy228: yy229: YYDEBUG(229, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2059 "Zend/zend_language_scanner.l" +#line 2082 "Zend/zend_language_scanner.l" { char *s; int bprefix = (yytext[0] != '<') ? 1 : 0; @@ -3623,9 +3646,9 @@ yy229: zend_ptr_stack_push(&SCNG(heredoc_label_stack), (void *) heredoc_label); - return T_START_HEREDOC; + RETURN_TOKEN(T_START_HEREDOC); } -#line 3629 "Zend/zend_language_scanner.c" +#line 3652 "Zend/zend_language_scanner.c" yy230: YYDEBUG(230, *YYCURSOR); yych = *++YYCURSOR; @@ -3665,31 +3688,31 @@ yy233: ++YYCURSOR; YYDEBUG(235, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1455 "Zend/zend_language_scanner.l" +#line 1478 "Zend/zend_language_scanner.l" { - return T_IS_NOT_IDENTICAL; + RETURN_TOKEN(T_IS_NOT_IDENTICAL); } -#line 3673 "Zend/zend_language_scanner.c" +#line 3696 "Zend/zend_language_scanner.c" yy236: YYDEBUG(236, *YYCURSOR); ++YYCURSOR; YYDEBUG(237, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1479 "Zend/zend_language_scanner.l" +#line 1502 "Zend/zend_language_scanner.l" { - return T_PLUS_EQUAL; + RETURN_TOKEN(T_PLUS_EQUAL); } -#line 3683 "Zend/zend_language_scanner.c" +#line 3706 "Zend/zend_language_scanner.c" yy238: YYDEBUG(238, *YYCURSOR); ++YYCURSOR; YYDEBUG(239, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1443 "Zend/zend_language_scanner.l" +#line 1466 "Zend/zend_language_scanner.l" { - return T_INC; + RETURN_TOKEN(T_INC); } -#line 3693 "Zend/zend_language_scanner.c" +#line 3716 "Zend/zend_language_scanner.c" yy240: YYDEBUG(240, *YYCURSOR); yych = *++YYCURSOR; @@ -3708,42 +3731,42 @@ yy242: } YYDEBUG(243, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1431 "Zend/zend_language_scanner.l" +#line 1454 "Zend/zend_language_scanner.l" { - return T_LIST; + RETURN_TOKEN(T_LIST); } -#line 3716 "Zend/zend_language_scanner.c" +#line 3739 "Zend/zend_language_scanner.c" yy244: YYDEBUG(244, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) == '=') goto yy248; YYDEBUG(245, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1459 "Zend/zend_language_scanner.l" +#line 1482 "Zend/zend_language_scanner.l" { - return T_IS_EQUAL; + RETURN_TOKEN(T_IS_EQUAL); } -#line 3727 "Zend/zend_language_scanner.c" +#line 3750 "Zend/zend_language_scanner.c" yy246: YYDEBUG(246, *YYCURSOR); ++YYCURSOR; YYDEBUG(247, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1427 "Zend/zend_language_scanner.l" +#line 1450 "Zend/zend_language_scanner.l" { - return T_DOUBLE_ARROW; + RETURN_TOKEN(T_DOUBLE_ARROW); } -#line 3737 "Zend/zend_language_scanner.c" +#line 3760 "Zend/zend_language_scanner.c" yy248: YYDEBUG(248, *YYCURSOR); ++YYCURSOR; YYDEBUG(249, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1451 "Zend/zend_language_scanner.l" +#line 1474 "Zend/zend_language_scanner.l" { - return T_IS_IDENTICAL; + RETURN_TOKEN(T_IS_IDENTICAL); } -#line 3747 "Zend/zend_language_scanner.c" +#line 3770 "Zend/zend_language_scanner.c" yy250: YYDEBUG(250, *YYCURSOR); yych = *++YYCURSOR; @@ -3873,11 +3896,11 @@ yy266: } YYDEBUG(269, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1757 "Zend/zend_language_scanner.l" +#line 1780 "Zend/zend_language_scanner.l" { - return T_NS_C; + RETURN_TOKEN(T_NS_C); } -#line 3881 "Zend/zend_language_scanner.c" +#line 3904 "Zend/zend_language_scanner.c" yy270: YYDEBUG(270, *YYCURSOR); yych = *++YYCURSOR; @@ -3897,11 +3920,11 @@ yy271: } YYDEBUG(274, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1753 "Zend/zend_language_scanner.l" +#line 1776 "Zend/zend_language_scanner.l" { - return T_DIR; + RETURN_TOKEN(T_DIR); } -#line 3905 "Zend/zend_language_scanner.c" +#line 3928 "Zend/zend_language_scanner.c" yy275: YYDEBUG(275, *YYCURSOR); yych = *++YYCURSOR; @@ -3926,11 +3949,11 @@ yy277: } YYDEBUG(280, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1745 "Zend/zend_language_scanner.l" +#line 1768 "Zend/zend_language_scanner.l" { - return T_LINE; + RETURN_TOKEN(T_LINE); } -#line 3934 "Zend/zend_language_scanner.c" +#line 3957 "Zend/zend_language_scanner.c" yy281: YYDEBUG(281, *YYCURSOR); yych = *++YYCURSOR; @@ -3965,11 +3988,11 @@ yy285: } YYDEBUG(288, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1741 "Zend/zend_language_scanner.l" +#line 1764 "Zend/zend_language_scanner.l" { - return T_METHOD_C; + RETURN_TOKEN(T_METHOD_C); } -#line 3973 "Zend/zend_language_scanner.c" +#line 3996 "Zend/zend_language_scanner.c" yy289: YYDEBUG(289, *YYCURSOR); yych = *++YYCURSOR; @@ -4020,11 +4043,11 @@ yy296: } YYDEBUG(299, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1737 "Zend/zend_language_scanner.l" +#line 1760 "Zend/zend_language_scanner.l" { - return T_FUNC_C; + RETURN_TOKEN(T_FUNC_C); } -#line 4028 "Zend/zend_language_scanner.c" +#line 4051 "Zend/zend_language_scanner.c" yy300: YYDEBUG(300, *YYCURSOR); yych = *++YYCURSOR; @@ -4044,11 +4067,11 @@ yy301: } YYDEBUG(304, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1749 "Zend/zend_language_scanner.l" +#line 1772 "Zend/zend_language_scanner.l" { - return T_FILE; + RETURN_TOKEN(T_FILE); } -#line 4052 "Zend/zend_language_scanner.c" +#line 4075 "Zend/zend_language_scanner.c" yy305: YYDEBUG(305, *YYCURSOR); yych = *++YYCURSOR; @@ -4078,11 +4101,11 @@ yy308: } YYDEBUG(311, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1733 "Zend/zend_language_scanner.l" +#line 1756 "Zend/zend_language_scanner.l" { - return T_TRAIT_C; + RETURN_TOKEN(T_TRAIT_C); } -#line 4086 "Zend/zend_language_scanner.c" +#line 4109 "Zend/zend_language_scanner.c" yy312: YYDEBUG(312, *YYCURSOR); yych = *++YYCURSOR; @@ -4112,11 +4135,11 @@ yy315: } YYDEBUG(318, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1729 "Zend/zend_language_scanner.l" +#line 1752 "Zend/zend_language_scanner.l" { - return T_CLASS_C; + RETURN_TOKEN(T_CLASS_C); } -#line 4120 "Zend/zend_language_scanner.c" +#line 4143 "Zend/zend_language_scanner.c" yy319: YYDEBUG(319, *YYCURSOR); yych = *++YYCURSOR; @@ -4178,11 +4201,11 @@ yy330: } YYDEBUG(331, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1395 "Zend/zend_language_scanner.l" +#line 1418 "Zend/zend_language_scanner.l" { - return T_HALT_COMPILER; + RETURN_TOKEN(T_HALT_COMPILER); } -#line 4186 "Zend/zend_language_scanner.c" +#line 4209 "Zend/zend_language_scanner.c" yy332: YYDEBUG(332, *YYCURSOR); yych = *++YYCURSOR; @@ -4202,11 +4225,11 @@ yy334: } YYDEBUG(335, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1375 "Zend/zend_language_scanner.l" +#line 1398 "Zend/zend_language_scanner.l" { - return T_USE; + RETURN_TOKEN(T_USE); } -#line 4210 "Zend/zend_language_scanner.c" +#line 4233 "Zend/zend_language_scanner.c" yy336: YYDEBUG(336, *YYCURSOR); yych = *++YYCURSOR; @@ -4225,11 +4248,11 @@ yy338: } YYDEBUG(339, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1423 "Zend/zend_language_scanner.l" +#line 1446 "Zend/zend_language_scanner.l" { - return T_UNSET; + RETURN_TOKEN(T_UNSET); } -#line 4233 "Zend/zend_language_scanner.c" +#line 4256 "Zend/zend_language_scanner.c" yy340: YYDEBUG(340, *YYCURSOR); ++YYCURSOR; @@ -4401,11 +4424,11 @@ yy355: ++YYCURSOR; YYDEBUG(357, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1323 "Zend/zend_language_scanner.l" +#line 1346 "Zend/zend_language_scanner.l" { - return T_INT_CAST; + RETURN_TOKEN(T_INT_CAST); } -#line 4409 "Zend/zend_language_scanner.c" +#line 4432 "Zend/zend_language_scanner.c" yy358: YYDEBUG(358, *YYCURSOR); yych = *++YYCURSOR; @@ -4449,11 +4472,11 @@ yy363: ++YYCURSOR; YYDEBUG(366, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1327 "Zend/zend_language_scanner.l" +#line 1350 "Zend/zend_language_scanner.l" { - return T_DOUBLE_CAST; + RETURN_TOKEN(T_DOUBLE_CAST); } -#line 4457 "Zend/zend_language_scanner.c" +#line 4480 "Zend/zend_language_scanner.c" yy367: YYDEBUG(367, *YYCURSOR); yych = *++YYCURSOR; @@ -4523,11 +4546,11 @@ yy377: ++YYCURSOR; YYDEBUG(380, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1331 "Zend/zend_language_scanner.l" +#line 1354 "Zend/zend_language_scanner.l" { - return T_STRING_CAST; + RETURN_TOKEN(T_STRING_CAST); } -#line 4531 "Zend/zend_language_scanner.c" +#line 4554 "Zend/zend_language_scanner.c" yy381: YYDEBUG(381, *YYCURSOR); yych = *++YYCURSOR; @@ -4560,11 +4583,11 @@ yy384: ++YYCURSOR; YYDEBUG(387, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1335 "Zend/zend_language_scanner.l" +#line 1358 "Zend/zend_language_scanner.l" { - return T_ARRAY_CAST; + RETURN_TOKEN(T_ARRAY_CAST); } -#line 4568 "Zend/zend_language_scanner.c" +#line 4591 "Zend/zend_language_scanner.c" yy388: YYDEBUG(388, *YYCURSOR); yych = *++YYCURSOR; @@ -4602,11 +4625,11 @@ yy392: ++YYCURSOR; YYDEBUG(395, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1339 "Zend/zend_language_scanner.l" +#line 1362 "Zend/zend_language_scanner.l" { - return T_OBJECT_CAST; + RETURN_TOKEN(T_OBJECT_CAST); } -#line 4610 "Zend/zend_language_scanner.c" +#line 4633 "Zend/zend_language_scanner.c" yy396: YYDEBUG(396, *YYCURSOR); yych = *++YYCURSOR; @@ -4647,11 +4670,11 @@ yy401: ++YYCURSOR; YYDEBUG(403, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1343 "Zend/zend_language_scanner.l" +#line 1366 "Zend/zend_language_scanner.l" { - return T_BOOL_CAST; + RETURN_TOKEN(T_BOOL_CAST); } -#line 4655 "Zend/zend_language_scanner.c" +#line 4678 "Zend/zend_language_scanner.c" yy404: YYDEBUG(404, *YYCURSOR); yych = *++YYCURSOR; @@ -4711,11 +4734,11 @@ yy412: ++YYCURSOR; YYDEBUG(415, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1347 "Zend/zend_language_scanner.l" +#line 1370 "Zend/zend_language_scanner.l" { - return T_UNSET_CAST; + RETURN_TOKEN(T_UNSET_CAST); } -#line 4719 "Zend/zend_language_scanner.c" +#line 4742 "Zend/zend_language_scanner.c" yy416: YYDEBUG(416, *YYCURSOR); yych = *++YYCURSOR; @@ -4729,11 +4752,11 @@ yy417: } YYDEBUG(418, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1319 "Zend/zend_language_scanner.l" +#line 1342 "Zend/zend_language_scanner.l" { - return T_VAR; + RETURN_TOKEN(T_VAR); } -#line 4737 "Zend/zend_language_scanner.c" +#line 4760 "Zend/zend_language_scanner.c" yy419: YYDEBUG(419, *YYCURSOR); yych = *++YYCURSOR; @@ -4753,11 +4776,11 @@ yy421: } YYDEBUG(422, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1311 "Zend/zend_language_scanner.l" +#line 1334 "Zend/zend_language_scanner.l" { - return T_NEW; + RETURN_TOKEN(T_NEW); } -#line 4761 "Zend/zend_language_scanner.c" +#line 4784 "Zend/zend_language_scanner.c" yy423: YYDEBUG(423, *YYCURSOR); yych = *++YYCURSOR; @@ -4796,11 +4819,11 @@ yy429: } YYDEBUG(430, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1371 "Zend/zend_language_scanner.l" +#line 1394 "Zend/zend_language_scanner.l" { - return T_NAMESPACE; + RETURN_TOKEN(T_NAMESPACE); } -#line 4804 "Zend/zend_language_scanner.c" +#line 4827 "Zend/zend_language_scanner.c" yy431: YYDEBUG(431, *YYCURSOR); ++YYCURSOR; @@ -4809,22 +4832,22 @@ yy431: yy432: YYDEBUG(432, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1940 "Zend/zend_language_scanner.l" +#line 1963 "Zend/zend_language_scanner.l" { BEGIN(INITIAL); - return T_CLOSE_TAG; /* implicit ';' at php-end tag */ + RETURN_TOKEN(T_CLOSE_TAG); /* implicit ';' at php-end tag */ } -#line 4818 "Zend/zend_language_scanner.c" +#line 4841 "Zend/zend_language_scanner.c" yy433: YYDEBUG(433, *YYCURSOR); ++YYCURSOR; YYDEBUG(434, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1307 "Zend/zend_language_scanner.l" +#line 1330 "Zend/zend_language_scanner.l" { - return T_COALESCE; + RETURN_TOKEN(T_COALESCE); } -#line 4828 "Zend/zend_language_scanner.c" +#line 4851 "Zend/zend_language_scanner.c" yy435: YYDEBUG(435, *YYCURSOR); yych = *++YYCURSOR; @@ -4855,11 +4878,11 @@ yy439: ++YYCURSOR; YYDEBUG(440, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1503 "Zend/zend_language_scanner.l" +#line 1526 "Zend/zend_language_scanner.l" { - return T_CONCAT_EQUAL; + RETURN_TOKEN(T_CONCAT_EQUAL); } -#line 4863 "Zend/zend_language_scanner.c" +#line 4886 "Zend/zend_language_scanner.c" yy441: YYDEBUG(441, *YYCURSOR); yych = *++YYCURSOR; @@ -4868,21 +4891,21 @@ yy441: ++YYCURSOR; YYDEBUG(443, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1303 "Zend/zend_language_scanner.l" +#line 1326 "Zend/zend_language_scanner.l" { - return T_ELLIPSIS; + RETURN_TOKEN(T_ELLIPSIS); } -#line 4876 "Zend/zend_language_scanner.c" +#line 4899 "Zend/zend_language_scanner.c" yy444: YYDEBUG(444, *YYCURSOR); ++YYCURSOR; YYDEBUG(445, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1295 "Zend/zend_language_scanner.l" +#line 1318 "Zend/zend_language_scanner.l" { - return T_PAAMAYIM_NEKUDOTAYIM; + RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM); } -#line 4886 "Zend/zend_language_scanner.c" +#line 4909 "Zend/zend_language_scanner.c" yy446: YYDEBUG(446, *YYCURSOR); ++YYCURSOR; @@ -4904,32 +4927,32 @@ yy448: ++YYCURSOR; YYDEBUG(449, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1483 "Zend/zend_language_scanner.l" +#line 1506 "Zend/zend_language_scanner.l" { - return T_MINUS_EQUAL; + RETURN_TOKEN(T_MINUS_EQUAL); } -#line 4912 "Zend/zend_language_scanner.c" +#line 4935 "Zend/zend_language_scanner.c" yy450: YYDEBUG(450, *YYCURSOR); ++YYCURSOR; YYDEBUG(451, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1447 "Zend/zend_language_scanner.l" +#line 1470 "Zend/zend_language_scanner.l" { - return T_DEC; + RETURN_TOKEN(T_DEC); } -#line 4922 "Zend/zend_language_scanner.c" +#line 4945 "Zend/zend_language_scanner.c" yy452: YYDEBUG(452, *YYCURSOR); ++YYCURSOR; YYDEBUG(453, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1269 "Zend/zend_language_scanner.l" +#line 1292 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_PROPERTY); - return T_OBJECT_OPERATOR; + RETURN_TOKEN(T_OBJECT_OPERATOR); } -#line 4933 "Zend/zend_language_scanner.c" +#line 4956 "Zend/zend_language_scanner.c" yy454: YYDEBUG(454, *YYCURSOR); yych = *++YYCURSOR; @@ -4974,11 +4997,11 @@ yy459: } YYDEBUG(460, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1419 "Zend/zend_language_scanner.l" +#line 1442 "Zend/zend_language_scanner.l" { - return T_PUBLIC; + RETURN_TOKEN(T_PUBLIC); } -#line 4982 "Zend/zend_language_scanner.c" +#line 5005 "Zend/zend_language_scanner.c" yy461: YYDEBUG(461, *YYCURSOR); yych = *++YYCURSOR; @@ -5033,11 +5056,11 @@ yy468: } YYDEBUG(469, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1415 "Zend/zend_language_scanner.l" +#line 1438 "Zend/zend_language_scanner.l" { - return T_PROTECTED; + RETURN_TOKEN(T_PROTECTED); } -#line 5041 "Zend/zend_language_scanner.c" +#line 5064 "Zend/zend_language_scanner.c" yy470: YYDEBUG(470, *YYCURSOR); yych = *++YYCURSOR; @@ -5067,11 +5090,11 @@ yy474: } YYDEBUG(475, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1411 "Zend/zend_language_scanner.l" +#line 1434 "Zend/zend_language_scanner.l" { - return T_PRIVATE; + RETURN_TOKEN(T_PRIVATE); } -#line 5075 "Zend/zend_language_scanner.c" +#line 5098 "Zend/zend_language_scanner.c" yy476: YYDEBUG(476, *YYCURSOR); ++YYCURSOR; @@ -5080,11 +5103,11 @@ yy476: } YYDEBUG(477, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1245 "Zend/zend_language_scanner.l" +#line 1268 "Zend/zend_language_scanner.l" { - return T_PRINT; + RETURN_TOKEN(T_PRINT); } -#line 5088 "Zend/zend_language_scanner.c" +#line 5111 "Zend/zend_language_scanner.c" yy478: YYDEBUG(478, *YYCURSOR); yych = *++YYCURSOR; @@ -5109,11 +5132,11 @@ yy481: } YYDEBUG(482, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1237 "Zend/zend_language_scanner.l" +#line 1260 "Zend/zend_language_scanner.l" { - return T_GOTO; + RETURN_TOKEN(T_GOTO); } -#line 5117 "Zend/zend_language_scanner.c" +#line 5140 "Zend/zend_language_scanner.c" yy483: YYDEBUG(483, *YYCURSOR); yych = *++YYCURSOR; @@ -5137,11 +5160,11 @@ yy486: } YYDEBUG(487, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1383 "Zend/zend_language_scanner.l" +#line 1406 "Zend/zend_language_scanner.l" { - return T_GLOBAL; + RETURN_TOKEN(T_GLOBAL); } -#line 5145 "Zend/zend_language_scanner.c" +#line 5168 "Zend/zend_language_scanner.c" yy488: YYDEBUG(488, *YYCURSOR); yych = *++YYCURSOR; @@ -5178,11 +5201,11 @@ yy494: } YYDEBUG(495, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1229 "Zend/zend_language_scanner.l" +#line 1252 "Zend/zend_language_scanner.l" { - return T_BREAK; + RETURN_TOKEN(T_BREAK); } -#line 5186 "Zend/zend_language_scanner.c" +#line 5209 "Zend/zend_language_scanner.c" yy496: YYDEBUG(496, *YYCURSOR); yych = *++YYCURSOR; @@ -5222,11 +5245,11 @@ yy502: } YYDEBUG(503, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1213 "Zend/zend_language_scanner.l" +#line 1236 "Zend/zend_language_scanner.l" { - return T_SWITCH; + RETURN_TOKEN(T_SWITCH); } -#line 5230 "Zend/zend_language_scanner.c" +#line 5253 "Zend/zend_language_scanner.c" yy504: YYDEBUG(504, *YYCURSOR); yych = *++YYCURSOR; @@ -5250,11 +5273,11 @@ yy507: } YYDEBUG(508, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1399 "Zend/zend_language_scanner.l" +#line 1422 "Zend/zend_language_scanner.l" { - return T_STATIC; + RETURN_TOKEN(T_STATIC); } -#line 5258 "Zend/zend_language_scanner.c" +#line 5281 "Zend/zend_language_scanner.c" yy509: YYDEBUG(509, *YYCURSOR); yych = *++YYCURSOR; @@ -5281,11 +5304,11 @@ yy512: } YYDEBUG(513, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1209 "Zend/zend_language_scanner.l" +#line 1232 "Zend/zend_language_scanner.l" { - return T_AS; + RETURN_TOKEN(T_AS); } -#line 5289 "Zend/zend_language_scanner.c" +#line 5312 "Zend/zend_language_scanner.c" yy514: YYDEBUG(514, *YYCURSOR); yych = *++YYCURSOR; @@ -5304,11 +5327,11 @@ yy516: } YYDEBUG(517, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1435 "Zend/zend_language_scanner.l" +#line 1458 "Zend/zend_language_scanner.l" { - return T_ARRAY; + RETURN_TOKEN(T_ARRAY); } -#line 5312 "Zend/zend_language_scanner.c" +#line 5335 "Zend/zend_language_scanner.c" yy518: YYDEBUG(518, *YYCURSOR); ++YYCURSOR; @@ -5317,11 +5340,11 @@ yy518: } YYDEBUG(519, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1543 "Zend/zend_language_scanner.l" +#line 1566 "Zend/zend_language_scanner.l" { - return T_LOGICAL_AND; + RETURN_TOKEN(T_LOGICAL_AND); } -#line 5325 "Zend/zend_language_scanner.c" +#line 5348 "Zend/zend_language_scanner.c" yy520: YYDEBUG(520, *YYCURSOR); yych = *++YYCURSOR; @@ -5355,11 +5378,11 @@ yy525: } YYDEBUG(526, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1403 "Zend/zend_language_scanner.l" +#line 1426 "Zend/zend_language_scanner.l" { - return T_ABSTRACT; + RETURN_TOKEN(T_ABSTRACT); } -#line 5363 "Zend/zend_language_scanner.c" +#line 5386 "Zend/zend_language_scanner.c" yy527: YYDEBUG(527, *YYCURSOR); yych = *++YYCURSOR; @@ -5383,11 +5406,11 @@ yy530: } YYDEBUG(531, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1169 "Zend/zend_language_scanner.l" +#line 1192 "Zend/zend_language_scanner.l" { - return T_WHILE; + RETURN_TOKEN(T_WHILE); } -#line 5391 "Zend/zend_language_scanner.c" +#line 5414 "Zend/zend_language_scanner.c" yy532: YYDEBUG(532, *YYCURSOR); ++YYCURSOR; @@ -5396,11 +5419,11 @@ yy532: } YYDEBUG(533, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1153 "Zend/zend_language_scanner.l" +#line 1176 "Zend/zend_language_scanner.l" { - return T_IF; + RETURN_TOKEN(T_IF); } -#line 5404 "Zend/zend_language_scanner.c" +#line 5427 "Zend/zend_language_scanner.c" yy534: YYDEBUG(534, *YYCURSOR); yych = *++YYCURSOR; @@ -5452,11 +5475,11 @@ yy539: } YYDEBUG(540, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1387 "Zend/zend_language_scanner.l" +#line 1410 "Zend/zend_language_scanner.l" { - return T_ISSET; + RETURN_TOKEN(T_ISSET); } -#line 5460 "Zend/zend_language_scanner.c" +#line 5483 "Zend/zend_language_scanner.c" yy541: YYDEBUG(541, *YYCURSOR); yych = *++YYCURSOR; @@ -5510,11 +5533,11 @@ yy547: yy548: YYDEBUG(548, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1355 "Zend/zend_language_scanner.l" +#line 1378 "Zend/zend_language_scanner.l" { - return T_INCLUDE; + RETURN_TOKEN(T_INCLUDE); } -#line 5518 "Zend/zend_language_scanner.c" +#line 5541 "Zend/zend_language_scanner.c" yy549: YYDEBUG(549, *YYCURSOR); yych = *++YYCURSOR; @@ -5543,11 +5566,11 @@ yy553: } YYDEBUG(554, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1359 "Zend/zend_language_scanner.l" +#line 1382 "Zend/zend_language_scanner.l" { - return T_INCLUDE_ONCE; + RETURN_TOKEN(T_INCLUDE_ONCE); } -#line 5551 "Zend/zend_language_scanner.c" +#line 5574 "Zend/zend_language_scanner.c" yy555: YYDEBUG(555, *YYCURSOR); yych = *++YYCURSOR; @@ -5581,11 +5604,11 @@ yy560: } YYDEBUG(561, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1253 "Zend/zend_language_scanner.l" +#line 1276 "Zend/zend_language_scanner.l" { - return T_INTERFACE; + RETURN_TOKEN(T_INTERFACE); } -#line 5589 "Zend/zend_language_scanner.c" +#line 5612 "Zend/zend_language_scanner.c" yy562: YYDEBUG(562, *YYCURSOR); yych = *++YYCURSOR; @@ -5635,11 +5658,11 @@ yy568: } YYDEBUG(569, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1379 "Zend/zend_language_scanner.l" +#line 1402 "Zend/zend_language_scanner.l" { - return T_INSTEADOF; + RETURN_TOKEN(T_INSTEADOF); } -#line 5643 "Zend/zend_language_scanner.c" +#line 5666 "Zend/zend_language_scanner.c" yy570: YYDEBUG(570, *YYCURSOR); yych = *++YYCURSOR; @@ -5668,11 +5691,11 @@ yy574: } YYDEBUG(575, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1205 "Zend/zend_language_scanner.l" +#line 1228 "Zend/zend_language_scanner.l" { - return T_INSTANCEOF; + RETURN_TOKEN(T_INSTANCEOF); } -#line 5676 "Zend/zend_language_scanner.c" +#line 5699 "Zend/zend_language_scanner.c" yy576: YYDEBUG(576, *YYCURSOR); yych = *++YYCURSOR; @@ -5716,11 +5739,11 @@ yy583: } YYDEBUG(584, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1265 "Zend/zend_language_scanner.l" +#line 1288 "Zend/zend_language_scanner.l" { - return T_IMPLEMENTS; + RETURN_TOKEN(T_IMPLEMENTS); } -#line 5724 "Zend/zend_language_scanner.c" +#line 5747 "Zend/zend_language_scanner.c" yy585: YYDEBUG(585, *YYCURSOR); yych = *++YYCURSOR; @@ -5748,11 +5771,11 @@ yy586: } YYDEBUG(588, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1137 "Zend/zend_language_scanner.l" +#line 1160 "Zend/zend_language_scanner.l" { - return T_TRY; + RETURN_TOKEN(T_TRY); } -#line 5756 "Zend/zend_language_scanner.c" +#line 5779 "Zend/zend_language_scanner.c" yy589: YYDEBUG(589, *YYCURSOR); yych = *++YYCURSOR; @@ -5771,11 +5794,11 @@ yy591: } YYDEBUG(592, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1257 "Zend/zend_language_scanner.l" +#line 1280 "Zend/zend_language_scanner.l" { - return T_TRAIT; + RETURN_TOKEN(T_TRAIT); } -#line 5779 "Zend/zend_language_scanner.c" +#line 5802 "Zend/zend_language_scanner.c" yy593: YYDEBUG(593, *YYCURSOR); yych = *++YYCURSOR; @@ -5794,11 +5817,11 @@ yy595: } YYDEBUG(596, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1149 "Zend/zend_language_scanner.l" +#line 1172 "Zend/zend_language_scanner.l" { - return T_THROW; + RETURN_TOKEN(T_THROW); } -#line 5802 "Zend/zend_language_scanner.c" +#line 5825 "Zend/zend_language_scanner.c" yy597: YYDEBUG(597, *YYCURSOR); yych = *++YYCURSOR; @@ -5831,11 +5854,11 @@ yy600: yy601: YYDEBUG(601, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1133 "Zend/zend_language_scanner.l" +#line 1156 "Zend/zend_language_scanner.l" { - return T_YIELD; + RETURN_TOKEN(T_YIELD); } -#line 5839 "Zend/zend_language_scanner.c" +#line 5862 "Zend/zend_language_scanner.c" yy602: YYDEBUG(602, *YYCURSOR); ++YYCURSOR; @@ -5877,11 +5900,11 @@ yy607: ++YYCURSOR; YYDEBUG(608, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1129 "Zend/zend_language_scanner.l" +#line 1152 "Zend/zend_language_scanner.l" { - return T_YIELD_FROM; + RETURN_TOKEN(T_YIELD_FROM); } -#line 5885 "Zend/zend_language_scanner.c" +#line 5908 "Zend/zend_language_scanner.c" yy609: YYDEBUG(609, *YYCURSOR); yych = *++YYCURSOR; @@ -5942,11 +5965,11 @@ yy615: yy616: YYDEBUG(616, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1363 "Zend/zend_language_scanner.l" +#line 1386 "Zend/zend_language_scanner.l" { - return T_REQUIRE; + RETURN_TOKEN(T_REQUIRE); } -#line 5950 "Zend/zend_language_scanner.c" +#line 5973 "Zend/zend_language_scanner.c" yy617: YYDEBUG(617, *YYCURSOR); yych = *++YYCURSOR; @@ -5975,11 +5998,11 @@ yy621: } YYDEBUG(622, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1367 "Zend/zend_language_scanner.l" +#line 1390 "Zend/zend_language_scanner.l" { - return T_REQUIRE_ONCE; + RETURN_TOKEN(T_REQUIRE_ONCE); } -#line 5983 "Zend/zend_language_scanner.c" +#line 6006 "Zend/zend_language_scanner.c" yy623: YYDEBUG(623, *YYCURSOR); yych = *++YYCURSOR; @@ -5998,11 +6021,11 @@ yy625: } YYDEBUG(626, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1125 "Zend/zend_language_scanner.l" +#line 1148 "Zend/zend_language_scanner.l" { - return T_RETURN; + RETURN_TOKEN(T_RETURN); } -#line 6006 "Zend/zend_language_scanner.c" +#line 6029 "Zend/zend_language_scanner.c" yy627: YYDEBUG(627, *YYCURSOR); yych = *++YYCURSOR; @@ -6092,11 +6115,11 @@ yy636: } YYDEBUG(637, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1233 "Zend/zend_language_scanner.l" +#line 1256 "Zend/zend_language_scanner.l" { - return T_CONTINUE; + RETURN_TOKEN(T_CONTINUE); } -#line 6100 "Zend/zend_language_scanner.c" +#line 6123 "Zend/zend_language_scanner.c" yy638: YYDEBUG(638, *YYCURSOR); ++YYCURSOR; @@ -6105,11 +6128,11 @@ yy638: } YYDEBUG(639, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1121 "Zend/zend_language_scanner.l" +#line 1144 "Zend/zend_language_scanner.l" { - return T_CONST; + RETURN_TOKEN(T_CONST); } -#line 6113 "Zend/zend_language_scanner.c" +#line 6136 "Zend/zend_language_scanner.c" yy640: YYDEBUG(640, *YYCURSOR); yych = *++YYCURSOR; @@ -6134,11 +6157,11 @@ yy643: } YYDEBUG(644, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1315 "Zend/zend_language_scanner.l" +#line 1338 "Zend/zend_language_scanner.l" { - return T_CLONE; + RETURN_TOKEN(T_CLONE); } -#line 6142 "Zend/zend_language_scanner.c" +#line 6165 "Zend/zend_language_scanner.c" yy645: YYDEBUG(645, *YYCURSOR); yych = *++YYCURSOR; @@ -6152,11 +6175,11 @@ yy646: } YYDEBUG(647, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1249 "Zend/zend_language_scanner.l" +#line 1272 "Zend/zend_language_scanner.l" { - return T_CLASS; + RETURN_TOKEN(T_CLASS); } -#line 6160 "Zend/zend_language_scanner.c" +#line 6183 "Zend/zend_language_scanner.c" yy648: YYDEBUG(648, *YYCURSOR); yych = *++YYCURSOR; @@ -6202,11 +6225,11 @@ yy655: } YYDEBUG(656, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1439 "Zend/zend_language_scanner.l" +#line 1462 "Zend/zend_language_scanner.l" { - return T_CALLABLE; + RETURN_TOKEN(T_CALLABLE); } -#line 6210 "Zend/zend_language_scanner.c" +#line 6233 "Zend/zend_language_scanner.c" yy657: YYDEBUG(657, *YYCURSOR); ++YYCURSOR; @@ -6215,11 +6238,11 @@ yy657: } YYDEBUG(658, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1221 "Zend/zend_language_scanner.l" +#line 1244 "Zend/zend_language_scanner.l" { - return T_CASE; + RETURN_TOKEN(T_CASE); } -#line 6223 "Zend/zend_language_scanner.c" +#line 6246 "Zend/zend_language_scanner.c" yy659: YYDEBUG(659, *YYCURSOR); yych = *++YYCURSOR; @@ -6233,11 +6256,11 @@ yy660: } YYDEBUG(661, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1141 "Zend/zend_language_scanner.l" +#line 1164 "Zend/zend_language_scanner.l" { - return T_CATCH; + RETURN_TOKEN(T_CATCH); } -#line 6241 "Zend/zend_language_scanner.c" +#line 6264 "Zend/zend_language_scanner.c" yy662: YYDEBUG(662, *YYCURSOR); yych = *++YYCURSOR; @@ -6288,11 +6311,11 @@ yy670: } YYDEBUG(671, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1117 "Zend/zend_language_scanner.l" +#line 1140 "Zend/zend_language_scanner.l" { - return T_FUNCTION; + RETURN_TOKEN(T_FUNCTION); } -#line 6296 "Zend/zend_language_scanner.c" +#line 6319 "Zend/zend_language_scanner.c" yy672: YYDEBUG(672, *YYCURSOR); ++YYCURSOR; @@ -6316,11 +6339,11 @@ yy672: yy673: YYDEBUG(673, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1181 "Zend/zend_language_scanner.l" +#line 1204 "Zend/zend_language_scanner.l" { - return T_FOR; + RETURN_TOKEN(T_FOR); } -#line 6324 "Zend/zend_language_scanner.c" +#line 6347 "Zend/zend_language_scanner.c" yy674: YYDEBUG(674, *YYCURSOR); yych = *++YYCURSOR; @@ -6344,11 +6367,11 @@ yy677: } YYDEBUG(678, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1189 "Zend/zend_language_scanner.l" +#line 1212 "Zend/zend_language_scanner.l" { - return T_FOREACH; + RETURN_TOKEN(T_FOREACH); } -#line 6352 "Zend/zend_language_scanner.c" +#line 6375 "Zend/zend_language_scanner.c" yy679: YYDEBUG(679, *YYCURSOR); yych = *++YYCURSOR; @@ -6382,11 +6405,11 @@ yy681: yy682: YYDEBUG(682, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1407 "Zend/zend_language_scanner.l" +#line 1430 "Zend/zend_language_scanner.l" { - return T_FINAL; + RETURN_TOKEN(T_FINAL); } -#line 6390 "Zend/zend_language_scanner.c" +#line 6413 "Zend/zend_language_scanner.c" yy683: YYDEBUG(683, *YYCURSOR); yych = *++YYCURSOR; @@ -6400,11 +6423,11 @@ yy684: } YYDEBUG(685, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1145 "Zend/zend_language_scanner.l" +#line 1168 "Zend/zend_language_scanner.l" { - return T_FINALLY; + RETURN_TOKEN(T_FINALLY); } -#line 6408 "Zend/zend_language_scanner.c" +#line 6431 "Zend/zend_language_scanner.c" yy686: YYDEBUG(686, *YYCURSOR); yych = *++YYCURSOR; @@ -6435,11 +6458,11 @@ yy688: } YYDEBUG(689, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1177 "Zend/zend_language_scanner.l" +#line 1200 "Zend/zend_language_scanner.l" { - return T_DO; + RETURN_TOKEN(T_DO); } -#line 6443 "Zend/zend_language_scanner.c" +#line 6466 "Zend/zend_language_scanner.c" yy690: YYDEBUG(690, *YYCURSOR); ++YYCURSOR; @@ -6448,11 +6471,11 @@ yy690: } YYDEBUG(691, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1113 "Zend/zend_language_scanner.l" +#line 1136 "Zend/zend_language_scanner.l" { - return T_EXIT; + RETURN_TOKEN(T_EXIT); } -#line 6456 "Zend/zend_language_scanner.c" +#line 6479 "Zend/zend_language_scanner.c" yy692: YYDEBUG(692, *YYCURSOR); yych = *++YYCURSOR; @@ -6487,11 +6510,11 @@ yy697: } YYDEBUG(698, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1225 "Zend/zend_language_scanner.l" +#line 1248 "Zend/zend_language_scanner.l" { - return T_DEFAULT; + RETURN_TOKEN(T_DEFAULT); } -#line 6495 "Zend/zend_language_scanner.c" +#line 6518 "Zend/zend_language_scanner.c" yy699: YYDEBUG(699, *YYCURSOR); yych = *++YYCURSOR; @@ -6515,11 +6538,11 @@ yy702: } YYDEBUG(703, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1197 "Zend/zend_language_scanner.l" +#line 1220 "Zend/zend_language_scanner.l" { - return T_DECLARE; + RETURN_TOKEN(T_DECLARE); } -#line 6523 "Zend/zend_language_scanner.c" +#line 6546 "Zend/zend_language_scanner.c" yy704: YYDEBUG(704, *YYCURSOR); yych = *++YYCURSOR; @@ -6599,11 +6622,11 @@ yy715: } YYDEBUG(716, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1261 "Zend/zend_language_scanner.l" +#line 1284 "Zend/zend_language_scanner.l" { - return T_EXTENDS; + RETURN_TOKEN(T_EXTENDS); } -#line 6607 "Zend/zend_language_scanner.c" +#line 6630 "Zend/zend_language_scanner.c" yy717: YYDEBUG(717, *YYCURSOR); ++YYCURSOR; @@ -6612,11 +6635,11 @@ yy717: } YYDEBUG(718, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1109 "Zend/zend_language_scanner.l" +#line 1132 "Zend/zend_language_scanner.l" { - return T_EXIT; + RETURN_TOKEN(T_EXIT); } -#line 6620 "Zend/zend_language_scanner.c" +#line 6643 "Zend/zend_language_scanner.c" yy719: YYDEBUG(719, *YYCURSOR); yych = *++YYCURSOR; @@ -6630,11 +6653,11 @@ yy720: } YYDEBUG(721, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1351 "Zend/zend_language_scanner.l" +#line 1374 "Zend/zend_language_scanner.l" { - return T_EVAL; + RETURN_TOKEN(T_EVAL); } -#line 6638 "Zend/zend_language_scanner.c" +#line 6661 "Zend/zend_language_scanner.c" yy722: YYDEBUG(722, *YYCURSOR); yych = *++YYCURSOR; @@ -6704,11 +6727,11 @@ yy731: } YYDEBUG(732, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1173 "Zend/zend_language_scanner.l" +#line 1196 "Zend/zend_language_scanner.l" { - return T_ENDWHILE; + RETURN_TOKEN(T_ENDWHILE); } -#line 6712 "Zend/zend_language_scanner.c" +#line 6735 "Zend/zend_language_scanner.c" yy733: YYDEBUG(733, *YYCURSOR); yych = *++YYCURSOR; @@ -6737,11 +6760,11 @@ yy737: } YYDEBUG(738, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1217 "Zend/zend_language_scanner.l" +#line 1240 "Zend/zend_language_scanner.l" { - return T_ENDSWITCH; + RETURN_TOKEN(T_ENDSWITCH); } -#line 6745 "Zend/zend_language_scanner.c" +#line 6768 "Zend/zend_language_scanner.c" yy739: YYDEBUG(739, *YYCURSOR); ++YYCURSOR; @@ -6750,11 +6773,11 @@ yy739: } YYDEBUG(740, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1161 "Zend/zend_language_scanner.l" +#line 1184 "Zend/zend_language_scanner.l" { - return T_ENDIF; + RETURN_TOKEN(T_ENDIF); } -#line 6758 "Zend/zend_language_scanner.c" +#line 6781 "Zend/zend_language_scanner.c" yy741: YYDEBUG(741, *YYCURSOR); yych = *++YYCURSOR; @@ -6783,11 +6806,11 @@ yy742: yy743: YYDEBUG(743, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1185 "Zend/zend_language_scanner.l" +#line 1208 "Zend/zend_language_scanner.l" { - return T_ENDFOR; + RETURN_TOKEN(T_ENDFOR); } -#line 6791 "Zend/zend_language_scanner.c" +#line 6814 "Zend/zend_language_scanner.c" yy744: YYDEBUG(744, *YYCURSOR); yych = *++YYCURSOR; @@ -6811,11 +6834,11 @@ yy747: } YYDEBUG(748, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1193 "Zend/zend_language_scanner.l" +#line 1216 "Zend/zend_language_scanner.l" { - return T_ENDFOREACH; + RETURN_TOKEN(T_ENDFOREACH); } -#line 6819 "Zend/zend_language_scanner.c" +#line 6842 "Zend/zend_language_scanner.c" yy749: YYDEBUG(749, *YYCURSOR); yych = *++YYCURSOR; @@ -6849,11 +6872,11 @@ yy754: } YYDEBUG(755, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1201 "Zend/zend_language_scanner.l" +#line 1224 "Zend/zend_language_scanner.l" { - return T_ENDDECLARE; + RETURN_TOKEN(T_ENDDECLARE); } -#line 6857 "Zend/zend_language_scanner.c" +#line 6880 "Zend/zend_language_scanner.c" yy756: YYDEBUG(756, *YYCURSOR); yych = *++YYCURSOR; @@ -6872,11 +6895,11 @@ yy758: } YYDEBUG(759, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1391 "Zend/zend_language_scanner.l" +#line 1414 "Zend/zend_language_scanner.l" { - return T_EMPTY; + RETURN_TOKEN(T_EMPTY); } -#line 6880 "Zend/zend_language_scanner.c" +#line 6903 "Zend/zend_language_scanner.c" yy760: YYDEBUG(760, *YYCURSOR); yych = *++YYCURSOR; @@ -6905,11 +6928,11 @@ yy761: yy762: YYDEBUG(762, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1165 "Zend/zend_language_scanner.l" +#line 1188 "Zend/zend_language_scanner.l" { - return T_ELSE; + RETURN_TOKEN(T_ELSE); } -#line 6913 "Zend/zend_language_scanner.c" +#line 6936 "Zend/zend_language_scanner.c" yy763: YYDEBUG(763, *YYCURSOR); yych = *++YYCURSOR; @@ -6923,11 +6946,11 @@ yy764: } YYDEBUG(765, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1157 "Zend/zend_language_scanner.l" +#line 1180 "Zend/zend_language_scanner.l" { - return T_ELSEIF; + RETURN_TOKEN(T_ELSEIF); } -#line 6931 "Zend/zend_language_scanner.c" +#line 6954 "Zend/zend_language_scanner.c" yy766: YYDEBUG(766, *YYCURSOR); yych = *++YYCURSOR; @@ -6941,11 +6964,11 @@ yy767: } YYDEBUG(768, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1241 "Zend/zend_language_scanner.l" +#line 1264 "Zend/zend_language_scanner.l" { - return T_ECHO; + RETURN_TOKEN(T_ECHO); } -#line 6949 "Zend/zend_language_scanner.c" +#line 6972 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_LOOKING_FOR_PROPERTY: @@ -7018,12 +7041,12 @@ yy771: yy772: YYDEBUG(772, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1274 "Zend/zend_language_scanner.l" +#line 1297 "Zend/zend_language_scanner.l" { HANDLE_NEWLINES(yytext, yyleng); - return T_WHITESPACE; + RETURN_TOKEN(T_WHITESPACE); } -#line 7027 "Zend/zend_language_scanner.c" +#line 7050 "Zend/zend_language_scanner.c" yy773: YYDEBUG(773, *YYCURSOR); ++YYCURSOR; @@ -7031,13 +7054,13 @@ yy773: yy774: YYDEBUG(774, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1289 "Zend/zend_language_scanner.l" +#line 1312 "Zend/zend_language_scanner.l" { yyless(0); yy_pop_state(); goto restart; } -#line 7041 "Zend/zend_language_scanner.c" +#line 7064 "Zend/zend_language_scanner.c" yy775: YYDEBUG(775, *YYCURSOR); ++YYCURSOR; @@ -7046,13 +7069,13 @@ yy775: yy776: YYDEBUG(776, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1283 "Zend/zend_language_scanner.l" +#line 1306 "Zend/zend_language_scanner.l" { yy_pop_state(); zend_copy_value(zendlval, yytext, yyleng); - return T_STRING; + RETURN_TOKEN(T_STRING); } -#line 7056 "Zend/zend_language_scanner.c" +#line 7079 "Zend/zend_language_scanner.c" yy777: YYDEBUG(777, *YYCURSOR); yych = *++YYCURSOR; @@ -7073,11 +7096,11 @@ yy780: ++YYCURSOR; YYDEBUG(781, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1279 "Zend/zend_language_scanner.l" +#line 1302 "Zend/zend_language_scanner.l" { - return T_OBJECT_OPERATOR; + RETURN_TOKEN(T_OBJECT_OPERATOR); } -#line 7081 "Zend/zend_language_scanner.c" +#line 7104 "Zend/zend_language_scanner.c" yy782: YYDEBUG(782, *YYCURSOR); ++YYCURSOR; @@ -7162,14 +7185,14 @@ yy786: yy787: YYDEBUG(787, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1594 "Zend/zend_language_scanner.l" +#line 1617 "Zend/zend_language_scanner.l" { yyless(0); yy_pop_state(); yy_push_state(ST_IN_SCRIPTING); goto restart; } -#line 7173 "Zend/zend_language_scanner.c" +#line 7196 "Zend/zend_language_scanner.c" yy788: YYDEBUG(788, *YYCURSOR); yych = *++YYCURSOR; @@ -7194,15 +7217,15 @@ yy792: ++YYCURSOR; YYDEBUG(793, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1585 "Zend/zend_language_scanner.l" +#line 1608 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); zend_copy_value(zendlval, yytext, yyleng); yy_pop_state(); yy_push_state(ST_IN_SCRIPTING); - return T_STRING_VARNAME; + RETURN_TOKEN(T_STRING_VARNAME); } -#line 7206 "Zend/zend_language_scanner.c" +#line 7229 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_NOWDOC: @@ -7213,14 +7236,14 @@ yyc_ST_NOWDOC: ++YYCURSOR; YYDEBUG(797, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2318 "Zend/zend_language_scanner.l" +#line 2341 "Zend/zend_language_scanner.l" { int newline = 0; zend_heredoc_label *heredoc_label = zend_ptr_stack_top(&SCNG(heredoc_label_stack)); if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } YYCURSOR--; @@ -7267,9 +7290,9 @@ nowdoc_scan_done: zend_copy_value(zendlval, yytext, yyleng - newline); HANDLE_NEWLINES(yytext, yyleng - newline); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } -#line 7273 "Zend/zend_language_scanner.c" +#line 7296 "Zend/zend_language_scanner.c" /* *********************************** */ yyc_ST_VAR_OFFSET: { @@ -7376,7 +7399,7 @@ yy800: yy801: YYDEBUG(801, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1699 "Zend/zend_language_scanner.l" +#line 1722 "Zend/zend_language_scanner.l" { /* Offset could be treated as a long */ if (yyleng < MAX_LENGTH_OF_LONG - 1 || (yyleng == MAX_LENGTH_OF_LONG - 1 && strcmp(yytext, long_min_digits) < 0)) { char *end; @@ -7390,9 +7413,9 @@ yy801: string: ZVAL_STRINGL(zendlval, yytext, yyleng); } - return T_NUM_STRING; + RETURN_TOKEN(T_NUM_STRING); } -#line 7396 "Zend/zend_language_scanner.c" +#line 7419 "Zend/zend_language_scanner.c" yy802: YYDEBUG(802, *YYCURSOR); yych = *++YYCURSOR; @@ -7412,23 +7435,23 @@ yy803: yy804: YYDEBUG(804, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1859 "Zend/zend_language_scanner.l" +#line 1882 "Zend/zend_language_scanner.l" { /* Only '[' can be valid, but returning other tokens will allow a more explicit parse error */ - return yytext[0]; + RETURN_TOKEN(yytext[0]); } -#line 7421 "Zend/zend_language_scanner.c" +#line 7444 "Zend/zend_language_scanner.c" yy805: YYDEBUG(805, *YYCURSOR); ++YYCURSOR; YYDEBUG(806, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1854 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { yy_pop_state(); - return ']'; + RETURN_TOKEN(']'); } -#line 7432 "Zend/zend_language_scanner.c" +#line 7455 "Zend/zend_language_scanner.c" yy807: YYDEBUG(807, *YYCURSOR); yych = *++YYCURSOR; @@ -7438,15 +7461,15 @@ yy808: ++YYCURSOR; YYDEBUG(809, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1864 "Zend/zend_language_scanner.l" +#line 1887 "Zend/zend_language_scanner.l" { /* Invalid rule to return a more explicit parse error with proper line number */ yyless(0); yy_pop_state(); ZVAL_NULL(zendlval); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } -#line 7450 "Zend/zend_language_scanner.c" +#line 7473 "Zend/zend_language_scanner.c" yy810: YYDEBUG(810, *YYCURSOR); ++YYCURSOR; @@ -7455,27 +7478,27 @@ yy810: yy811: YYDEBUG(811, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1872 "Zend/zend_language_scanner.l" +#line 1895 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, yytext, yyleng); - return T_STRING; + RETURN_TOKEN(T_STRING); } -#line 7464 "Zend/zend_language_scanner.c" +#line 7487 "Zend/zend_language_scanner.c" yy812: YYDEBUG(812, *YYCURSOR); ++YYCURSOR; YYDEBUG(813, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2375 "Zend/zend_language_scanner.l" +#line 2398 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); goto restart; } -#line 7479 "Zend/zend_language_scanner.c" +#line 7502 "Zend/zend_language_scanner.c" yy814: YYDEBUG(814, *YYCURSOR); ++YYCURSOR; @@ -7511,12 +7534,12 @@ yy816: yy818: YYDEBUG(818, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1849 "Zend/zend_language_scanner.l" +#line 1872 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } -#line 7520 "Zend/zend_language_scanner.c" +#line 7543 "Zend/zend_language_scanner.c" yy819: YYDEBUG(819, *YYCURSOR); ++YYCURSOR; @@ -7556,12 +7579,12 @@ yy824: yy826: YYDEBUG(826, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1715 "Zend/zend_language_scanner.l" +#line 1738 "Zend/zend_language_scanner.l" { /* Offset must be treated as a string */ ZVAL_STRINGL(zendlval, yytext, yyleng); - return T_NUM_STRING; + RETURN_TOKEN(T_NUM_STRING); } -#line 7565 "Zend/zend_language_scanner.c" +#line 7588 "Zend/zend_language_scanner.c" yy827: YYDEBUG(827, *YYCURSOR); ++YYCURSOR; @@ -7584,6 +7607,6 @@ yy829: goto yy826; } } -#line 2384 "Zend/zend_language_scanner.l" +#line 2407 "Zend/zend_language_scanner.l" } diff --git a/Zend/zend_language_scanner.h b/Zend/zend_language_scanner.h index c82b3069c5..3b75ff8cc4 100644 --- a/Zend/zend_language_scanner.h +++ b/Zend/zend_language_scanner.h @@ -50,6 +50,9 @@ typedef struct _zend_lex_state { zend_encoding_filter output_filter; const zend_encoding *script_encoding; + /* hooks */ + void (* on_event)(zend_php_scanner_event event, int token, int line); + zend_ast *ast; zend_arena *ast_arena; } zend_lex_state; @@ -66,6 +69,7 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state); ZEND_API int zend_prepare_string_for_scanning(zval *str, char *filename); ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, const zend_encoding *old_encoding); ZEND_API int zend_multibyte_set_filter(const zend_encoding *onetime_encoding); +ZEND_API void zend_lex_tstring(zval *zv); END_EXTERN_C() diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 1204287ad9..01c489cbe4 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -193,6 +193,7 @@ void shutdown_scanner(void) zend_stack_destroy(&SCNG(state_stack)); zend_ptr_stack_clean(&SCNG(heredoc_label_stack), (void (*)(void *)) &heredoc_label_dtor, 1); zend_ptr_stack_destroy(&SCNG(heredoc_label_stack)); + SCNG(on_event) = NULL; } ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state) @@ -223,6 +224,8 @@ ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state) lex_state->output_filter = SCNG(output_filter); lex_state->script_encoding = SCNG(script_encoding); + lex_state->on_event = SCNG(on_event); + lex_state->ast = CG(ast); lex_state->ast_arena = CG(ast_arena); } @@ -260,6 +263,8 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state) SCNG(output_filter) = lex_state->output_filter; SCNG(script_encoding) = lex_state->script_encoding; + SCNG(on_event) = lex_state->on_event; + CG(ast) = lex_state->ast; CG(ast_arena) = lex_state->ast_arena; @@ -276,6 +281,13 @@ ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) } } +ZEND_API void zend_lex_tstring(zval *zv) +{ + if (SCNG(on_event)) SCNG(on_event)(ON_FEEDBACK, T_STRING, 0); + + ZVAL_STRINGL(zv, (char*)SCNG(yy_text), SCNG(yy_leng)); +} + #define BOM_UTF32_BE "\x00\x00\xfe\xff" #define BOM_UTF32_LE "\xff\xfe\x00\x00" #define BOM_UTF16_BE "\xfe\xff" @@ -1083,9 +1095,20 @@ static int zend_scan_escape_string(zval *zendlval, char *str, int len, char quot return SUCCESS; } +static zend_always_inline int emit_token(int token, int token_line) +{ + if(SCNG(on_event)) SCNG(on_event)(ON_TOKEN, token, token_line); + + return token; +} + +#define RETURN_TOKEN(token) return emit_token(token, start_line); int lex_scan(zval *zendlval) { + +int start_line = CG(zend_lineno); + restart: SCNG(yy_text) = YYCURSOR; @@ -1107,183 +1130,183 @@ NEWLINE ("\r"|"\n"|"\r\n") <!*> := yyleng = YYCURSOR - SCNG(yy_text); <ST_IN_SCRIPTING>"exit" { - return T_EXIT; + RETURN_TOKEN(T_EXIT); } <ST_IN_SCRIPTING>"die" { - return T_EXIT; + RETURN_TOKEN(T_EXIT); } <ST_IN_SCRIPTING>"function" { - return T_FUNCTION; + RETURN_TOKEN(T_FUNCTION); } <ST_IN_SCRIPTING>"const" { - return T_CONST; + RETURN_TOKEN(T_CONST); } <ST_IN_SCRIPTING>"return" { - return T_RETURN; + RETURN_TOKEN(T_RETURN); } <ST_IN_SCRIPTING>"yield"{WHITESPACE}"from" { - return T_YIELD_FROM; + RETURN_TOKEN(T_YIELD_FROM); } <ST_IN_SCRIPTING>"yield" { - return T_YIELD; + RETURN_TOKEN(T_YIELD); } <ST_IN_SCRIPTING>"try" { - return T_TRY; + RETURN_TOKEN(T_TRY); } <ST_IN_SCRIPTING>"catch" { - return T_CATCH; + RETURN_TOKEN(T_CATCH); } <ST_IN_SCRIPTING>"finally" { - return T_FINALLY; + RETURN_TOKEN(T_FINALLY); } <ST_IN_SCRIPTING>"throw" { - return T_THROW; + RETURN_TOKEN(T_THROW); } <ST_IN_SCRIPTING>"if" { - return T_IF; + RETURN_TOKEN(T_IF); } <ST_IN_SCRIPTING>"elseif" { - return T_ELSEIF; + RETURN_TOKEN(T_ELSEIF); } <ST_IN_SCRIPTING>"endif" { - return T_ENDIF; + RETURN_TOKEN(T_ENDIF); } <ST_IN_SCRIPTING>"else" { - return T_ELSE; + RETURN_TOKEN(T_ELSE); } <ST_IN_SCRIPTING>"while" { - return T_WHILE; + RETURN_TOKEN(T_WHILE); } <ST_IN_SCRIPTING>"endwhile" { - return T_ENDWHILE; + RETURN_TOKEN(T_ENDWHILE); } <ST_IN_SCRIPTING>"do" { - return T_DO; + RETURN_TOKEN(T_DO); } <ST_IN_SCRIPTING>"for" { - return T_FOR; + RETURN_TOKEN(T_FOR); } <ST_IN_SCRIPTING>"endfor" { - return T_ENDFOR; + RETURN_TOKEN(T_ENDFOR); } <ST_IN_SCRIPTING>"foreach" { - return T_FOREACH; + RETURN_TOKEN(T_FOREACH); } <ST_IN_SCRIPTING>"endforeach" { - return T_ENDFOREACH; + RETURN_TOKEN(T_ENDFOREACH); } <ST_IN_SCRIPTING>"declare" { - return T_DECLARE; + RETURN_TOKEN(T_DECLARE); } <ST_IN_SCRIPTING>"enddeclare" { - return T_ENDDECLARE; + RETURN_TOKEN(T_ENDDECLARE); } <ST_IN_SCRIPTING>"instanceof" { - return T_INSTANCEOF; + RETURN_TOKEN(T_INSTANCEOF); } <ST_IN_SCRIPTING>"as" { - return T_AS; + RETURN_TOKEN(T_AS); } <ST_IN_SCRIPTING>"switch" { - return T_SWITCH; + RETURN_TOKEN(T_SWITCH); } <ST_IN_SCRIPTING>"endswitch" { - return T_ENDSWITCH; + RETURN_TOKEN(T_ENDSWITCH); } <ST_IN_SCRIPTING>"case" { - return T_CASE; + RETURN_TOKEN(T_CASE); } <ST_IN_SCRIPTING>"default" { - return T_DEFAULT; + RETURN_TOKEN(T_DEFAULT); } <ST_IN_SCRIPTING>"break" { - return T_BREAK; + RETURN_TOKEN(T_BREAK); } <ST_IN_SCRIPTING>"continue" { - return T_CONTINUE; + RETURN_TOKEN(T_CONTINUE); } <ST_IN_SCRIPTING>"goto" { - return T_GOTO; + RETURN_TOKEN(T_GOTO); } <ST_IN_SCRIPTING>"echo" { - return T_ECHO; + RETURN_TOKEN(T_ECHO); } <ST_IN_SCRIPTING>"print" { - return T_PRINT; + RETURN_TOKEN(T_PRINT); } <ST_IN_SCRIPTING>"class" { - return T_CLASS; + RETURN_TOKEN(T_CLASS); } <ST_IN_SCRIPTING>"interface" { - return T_INTERFACE; + RETURN_TOKEN(T_INTERFACE); } <ST_IN_SCRIPTING>"trait" { - return T_TRAIT; + RETURN_TOKEN(T_TRAIT); } <ST_IN_SCRIPTING>"extends" { - return T_EXTENDS; + RETURN_TOKEN(T_EXTENDS); } <ST_IN_SCRIPTING>"implements" { - return T_IMPLEMENTS; + RETURN_TOKEN(T_IMPLEMENTS); } <ST_IN_SCRIPTING>"->" { yy_push_state(ST_LOOKING_FOR_PROPERTY); - return T_OBJECT_OPERATOR; + RETURN_TOKEN(T_OBJECT_OPERATOR); } <ST_IN_SCRIPTING,ST_LOOKING_FOR_PROPERTY>{WHITESPACE}+ { HANDLE_NEWLINES(yytext, yyleng); - return T_WHITESPACE; + RETURN_TOKEN(T_WHITESPACE); } <ST_LOOKING_FOR_PROPERTY>"->" { - return T_OBJECT_OPERATOR; + RETURN_TOKEN(T_OBJECT_OPERATOR); } <ST_LOOKING_FOR_PROPERTY>{LABEL} { yy_pop_state(); zend_copy_value(zendlval, yytext, yyleng); - return T_STRING; + RETURN_TOKEN(T_STRING); } <ST_LOOKING_FOR_PROPERTY>{ANY_CHAR} { @@ -1293,283 +1316,283 @@ NEWLINE ("\r"|"\n"|"\r\n") } <ST_IN_SCRIPTING>"::" { - return T_PAAMAYIM_NEKUDOTAYIM; + RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM); } <ST_IN_SCRIPTING>"\\" { - return T_NS_SEPARATOR; + RETURN_TOKEN(T_NS_SEPARATOR); } <ST_IN_SCRIPTING>"..." { - return T_ELLIPSIS; + RETURN_TOKEN(T_ELLIPSIS); } <ST_IN_SCRIPTING>"??" { - return T_COALESCE; + RETURN_TOKEN(T_COALESCE); } <ST_IN_SCRIPTING>"new" { - return T_NEW; + RETURN_TOKEN(T_NEW); } <ST_IN_SCRIPTING>"clone" { - return T_CLONE; + RETURN_TOKEN(T_CLONE); } <ST_IN_SCRIPTING>"var" { - return T_VAR; + RETURN_TOKEN(T_VAR); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("int"|"integer"){TABS_AND_SPACES}")" { - return T_INT_CAST; + RETURN_TOKEN(T_INT_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("real"|"double"|"float"){TABS_AND_SPACES}")" { - return T_DOUBLE_CAST; + RETURN_TOKEN(T_DOUBLE_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("string"|"binary"){TABS_AND_SPACES}")" { - return T_STRING_CAST; + RETURN_TOKEN(T_STRING_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}"array"{TABS_AND_SPACES}")" { - return T_ARRAY_CAST; + RETURN_TOKEN(T_ARRAY_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}"object"{TABS_AND_SPACES}")" { - return T_OBJECT_CAST; + RETURN_TOKEN(T_OBJECT_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("bool"|"boolean"){TABS_AND_SPACES}")" { - return T_BOOL_CAST; + RETURN_TOKEN(T_BOOL_CAST); } <ST_IN_SCRIPTING>"("{TABS_AND_SPACES}("unset"){TABS_AND_SPACES}")" { - return T_UNSET_CAST; + RETURN_TOKEN(T_UNSET_CAST); } <ST_IN_SCRIPTING>"eval" { - return T_EVAL; + RETURN_TOKEN(T_EVAL); } <ST_IN_SCRIPTING>"include" { - return T_INCLUDE; + RETURN_TOKEN(T_INCLUDE); } <ST_IN_SCRIPTING>"include_once" { - return T_INCLUDE_ONCE; + RETURN_TOKEN(T_INCLUDE_ONCE); } <ST_IN_SCRIPTING>"require" { - return T_REQUIRE; + RETURN_TOKEN(T_REQUIRE); } <ST_IN_SCRIPTING>"require_once" { - return T_REQUIRE_ONCE; + RETURN_TOKEN(T_REQUIRE_ONCE); } <ST_IN_SCRIPTING>"namespace" { - return T_NAMESPACE; + RETURN_TOKEN(T_NAMESPACE); } <ST_IN_SCRIPTING>"use" { - return T_USE; + RETURN_TOKEN(T_USE); } <ST_IN_SCRIPTING>"insteadof" { - return T_INSTEADOF; + RETURN_TOKEN(T_INSTEADOF); } <ST_IN_SCRIPTING>"global" { - return T_GLOBAL; + RETURN_TOKEN(T_GLOBAL); } <ST_IN_SCRIPTING>"isset" { - return T_ISSET; + RETURN_TOKEN(T_ISSET); } <ST_IN_SCRIPTING>"empty" { - return T_EMPTY; + RETURN_TOKEN(T_EMPTY); } <ST_IN_SCRIPTING>"__halt_compiler" { - return T_HALT_COMPILER; + RETURN_TOKEN(T_HALT_COMPILER); } <ST_IN_SCRIPTING>"static" { - return T_STATIC; + RETURN_TOKEN(T_STATIC); } <ST_IN_SCRIPTING>"abstract" { - return T_ABSTRACT; + RETURN_TOKEN(T_ABSTRACT); } <ST_IN_SCRIPTING>"final" { - return T_FINAL; + RETURN_TOKEN(T_FINAL); } <ST_IN_SCRIPTING>"private" { - return T_PRIVATE; + RETURN_TOKEN(T_PRIVATE); } <ST_IN_SCRIPTING>"protected" { - return T_PROTECTED; + RETURN_TOKEN(T_PROTECTED); } <ST_IN_SCRIPTING>"public" { - return T_PUBLIC; + RETURN_TOKEN(T_PUBLIC); } <ST_IN_SCRIPTING>"unset" { - return T_UNSET; + RETURN_TOKEN(T_UNSET); } <ST_IN_SCRIPTING>"=>" { - return T_DOUBLE_ARROW; + RETURN_TOKEN(T_DOUBLE_ARROW); } <ST_IN_SCRIPTING>"list" { - return T_LIST; + RETURN_TOKEN(T_LIST); } <ST_IN_SCRIPTING>"array" { - return T_ARRAY; + RETURN_TOKEN(T_ARRAY); } <ST_IN_SCRIPTING>"callable" { - return T_CALLABLE; + RETURN_TOKEN(T_CALLABLE); } <ST_IN_SCRIPTING>"++" { - return T_INC; + RETURN_TOKEN(T_INC); } <ST_IN_SCRIPTING>"--" { - return T_DEC; + RETURN_TOKEN(T_DEC); } <ST_IN_SCRIPTING>"===" { - return T_IS_IDENTICAL; + RETURN_TOKEN(T_IS_IDENTICAL); } <ST_IN_SCRIPTING>"!==" { - return T_IS_NOT_IDENTICAL; + RETURN_TOKEN(T_IS_NOT_IDENTICAL); } <ST_IN_SCRIPTING>"==" { - return T_IS_EQUAL; + RETURN_TOKEN(T_IS_EQUAL); } <ST_IN_SCRIPTING>"!="|"<>" { - return T_IS_NOT_EQUAL; + RETURN_TOKEN(T_IS_NOT_EQUAL); } <ST_IN_SCRIPTING>"<=>" { - return T_SPACESHIP; + RETURN_TOKEN(T_SPACESHIP); } <ST_IN_SCRIPTING>"<=" { - return T_IS_SMALLER_OR_EQUAL; + RETURN_TOKEN(T_IS_SMALLER_OR_EQUAL); } <ST_IN_SCRIPTING>">=" { - return T_IS_GREATER_OR_EQUAL; + RETURN_TOKEN(T_IS_GREATER_OR_EQUAL); } <ST_IN_SCRIPTING>"+=" { - return T_PLUS_EQUAL; + RETURN_TOKEN(T_PLUS_EQUAL); } <ST_IN_SCRIPTING>"-=" { - return T_MINUS_EQUAL; + RETURN_TOKEN(T_MINUS_EQUAL); } <ST_IN_SCRIPTING>"*=" { - return T_MUL_EQUAL; + RETURN_TOKEN(T_MUL_EQUAL); } <ST_IN_SCRIPTING>"*\*" { - return T_POW; + RETURN_TOKEN(T_POW); } <ST_IN_SCRIPTING>"*\*=" { - return T_POW_EQUAL; + RETURN_TOKEN(T_POW_EQUAL); } <ST_IN_SCRIPTING>"/=" { - return T_DIV_EQUAL; + RETURN_TOKEN(T_DIV_EQUAL); } <ST_IN_SCRIPTING>".=" { - return T_CONCAT_EQUAL; + RETURN_TOKEN(T_CONCAT_EQUAL); } <ST_IN_SCRIPTING>"%=" { - return T_MOD_EQUAL; + RETURN_TOKEN(T_MOD_EQUAL); } <ST_IN_SCRIPTING>"<<=" { - return T_SL_EQUAL; + RETURN_TOKEN(T_SL_EQUAL); } <ST_IN_SCRIPTING>">>=" { - return T_SR_EQUAL; + RETURN_TOKEN(T_SR_EQUAL); } <ST_IN_SCRIPTING>"&=" { - return T_AND_EQUAL; + RETURN_TOKEN(T_AND_EQUAL); } <ST_IN_SCRIPTING>"|=" { - return T_OR_EQUAL; + RETURN_TOKEN(T_OR_EQUAL); } <ST_IN_SCRIPTING>"^=" { - return T_XOR_EQUAL; + RETURN_TOKEN(T_XOR_EQUAL); } <ST_IN_SCRIPTING>"||" { - return T_BOOLEAN_OR; + RETURN_TOKEN(T_BOOLEAN_OR); } <ST_IN_SCRIPTING>"&&" { - return T_BOOLEAN_AND; + RETURN_TOKEN(T_BOOLEAN_AND); } <ST_IN_SCRIPTING>"OR" { - return T_LOGICAL_OR; + RETURN_TOKEN(T_LOGICAL_OR); } <ST_IN_SCRIPTING>"AND" { - return T_LOGICAL_AND; + RETURN_TOKEN(T_LOGICAL_AND); } <ST_IN_SCRIPTING>"XOR" { - return T_LOGICAL_XOR; + RETURN_TOKEN(T_LOGICAL_XOR); } <ST_IN_SCRIPTING>"<<" { - return T_SL; + RETURN_TOKEN(T_SL); } <ST_IN_SCRIPTING>">>" { - return T_SR; + RETURN_TOKEN(T_SR); } <ST_IN_SCRIPTING>{TOKENS} { - return yytext[0]; + RETURN_TOKEN(yytext[0]); } <ST_IN_SCRIPTING>"{" { yy_push_state(ST_IN_SCRIPTING); - return '{'; + RETURN_TOKEN('{'); } <ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>"${" { yy_push_state(ST_LOOKING_FOR_VARNAME); - return T_DOLLAR_OPEN_CURLY_BRACES; + RETURN_TOKEN(T_DOLLAR_OPEN_CURLY_BRACES); } @@ -1578,7 +1601,7 @@ NEWLINE ("\r"|"\n"|"\r\n") if (!zend_stack_is_empty(&SCNG(state_stack))) { yy_pop_state(); } - return '}'; + RETURN_TOKEN('}'); } @@ -1587,7 +1610,7 @@ NEWLINE ("\r"|"\n"|"\r\n") zend_copy_value(zendlval, yytext, yyleng); yy_pop_state(); yy_push_state(ST_IN_SCRIPTING); - return T_STRING_VARNAME; + RETURN_TOKEN(T_STRING_VARNAME); } @@ -1617,12 +1640,12 @@ NEWLINE ("\r"|"\n"|"\r\n") ZVAL_LONG(zendlval, ZEND_STRTOL(bin, &end, 2)); ZEND_ASSERT(!errno && end == yytext + yyleng); } - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } else { ZVAL_DOUBLE(zendlval, zend_bin_strtod(bin, (const char **)&end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == yytext + yyleng); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } } @@ -1636,7 +1659,7 @@ NEWLINE ("\r"|"\n"|"\r\n") */ if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } } else { errno = 0; @@ -1653,19 +1676,19 @@ NEWLINE ("\r"|"\n"|"\r\n") if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } ZEND_ASSERT(!errno); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } /* Also not an assert for the same reason */ if (end != yytext + yyleng) { zend_throw_exception(zend_get_parse_error(), "Invalid numeric literal", E_PARSE); - return T_ERROR; + RETURN_TOKEN(T_ERROR); } } ZEND_ASSERT(!errno); - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } <ST_IN_SCRIPTING>{HNUM} { @@ -1687,12 +1710,12 @@ NEWLINE ("\r"|"\n"|"\r\n") ZVAL_LONG(zendlval, ZEND_STRTOL(hex, &end, 16)); ZEND_ASSERT(!errno && end == hex + len); } - return T_LNUMBER; + RETURN_TOKEN(T_LNUMBER); } else { ZVAL_DOUBLE(zendlval, zend_hex_strtod(hex, (const char **)&end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == hex + len); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } } @@ -1709,12 +1732,12 @@ NEWLINE ("\r"|"\n"|"\r\n") string: ZVAL_STRINGL(zendlval, yytext, yyleng); } - return T_NUM_STRING; + RETURN_TOKEN(T_NUM_STRING); } <ST_VAR_OFFSET>{LNUM}|{HNUM}|{BNUM} { /* Offset must be treated as a string */ ZVAL_STRINGL(zendlval, yytext, yyleng); - return T_NUM_STRING; + RETURN_TOKEN(T_NUM_STRING); } <ST_IN_SCRIPTING>{DNUM}|{EXPONENT_DNUM} { @@ -1723,59 +1746,59 @@ string: ZVAL_DOUBLE(zendlval, zend_strtod(yytext, &end)); /* errno isn't checked since we allow HUGE_VAL/INF overflow */ ZEND_ASSERT(end == yytext + yyleng); - return T_DNUMBER; + RETURN_TOKEN(T_DNUMBER); } <ST_IN_SCRIPTING>"__CLASS__" { - return T_CLASS_C; + RETURN_TOKEN(T_CLASS_C); } <ST_IN_SCRIPTING>"__TRAIT__" { - return T_TRAIT_C; + RETURN_TOKEN(T_TRAIT_C); } <ST_IN_SCRIPTING>"__FUNCTION__" { - return T_FUNC_C; + RETURN_TOKEN(T_FUNC_C); } <ST_IN_SCRIPTING>"__METHOD__" { - return T_METHOD_C; + RETURN_TOKEN(T_METHOD_C); } <ST_IN_SCRIPTING>"__LINE__" { - return T_LINE; + RETURN_TOKEN(T_LINE); } <ST_IN_SCRIPTING>"__FILE__" { - return T_FILE; + RETURN_TOKEN(T_FILE); } <ST_IN_SCRIPTING>"__DIR__" { - return T_DIR; + RETURN_TOKEN(T_DIR); } <ST_IN_SCRIPTING>"__NAMESPACE__" { - return T_NS_C; + RETURN_TOKEN(T_NS_C); } <INITIAL>"<?=" { BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG_WITH_ECHO; + RETURN_TOKEN(T_OPEN_TAG_WITH_ECHO); } <INITIAL>"<?php"([ \t]|{NEWLINE}) { HANDLE_NEWLINE(yytext[yyleng-1]); BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG; + RETURN_TOKEN(T_OPEN_TAG); } <INITIAL>"<?" { if (CG(short_tags)) { BEGIN(ST_IN_SCRIPTING); - return T_OPEN_TAG; + RETURN_TOKEN(T_OPEN_TAG); } else { goto inline_char_handler; } @@ -1783,7 +1806,7 @@ string: <INITIAL>{ANY_CHAR} { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } inline_char_handler: @@ -1823,7 +1846,7 @@ inline_char_handler: ZVAL_STRINGL(zendlval, yytext, yyleng); } HANDLE_NEWLINES(yytext, yyleng); - return T_INLINE_HTML; + RETURN_TOKEN(T_INLINE_HTML); } @@ -1834,7 +1857,7 @@ inline_char_handler: yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } /* A [ always designates a variable offset, regardless of what follows @@ -1843,22 +1866,22 @@ inline_char_handler: yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } <ST_IN_SCRIPTING,ST_DOUBLE_QUOTES,ST_HEREDOC,ST_BACKQUOTE,ST_VAR_OFFSET>"$"{LABEL} { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); - return T_VARIABLE; + RETURN_TOKEN(T_VARIABLE); } <ST_VAR_OFFSET>"]" { yy_pop_state(); - return ']'; + RETURN_TOKEN(']'); } <ST_VAR_OFFSET>{TOKENS}|[{}"`] { /* Only '[' can be valid, but returning other tokens will allow a more explicit parse error */ - return yytext[0]; + RETURN_TOKEN(yytext[0]); } <ST_VAR_OFFSET>[ \n\r\t\\'#] { @@ -1866,12 +1889,12 @@ inline_char_handler: yyless(0); yy_pop_state(); ZVAL_NULL(zendlval); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } <ST_IN_SCRIPTING,ST_VAR_OFFSET>{LABEL} { zend_copy_value(zendlval, yytext, yyleng); - return T_STRING; + RETURN_TOKEN(T_STRING); } @@ -1901,7 +1924,7 @@ inline_char_handler: yyleng = YYCURSOR - SCNG(yy_text); - return T_COMMENT; + RETURN_TOKEN(T_COMMENT); } <ST_IN_SCRIPTING>"/*"|"/**"{WHITESPACE} { @@ -1931,15 +1954,15 @@ inline_char_handler: if (doc_com) { CG(doc_comment) = zend_string_init(yytext, yyleng, 0); - return T_DOC_COMMENT; + RETURN_TOKEN(T_DOC_COMMENT); } - return T_COMMENT; + RETURN_TOKEN(T_COMMENT); } <ST_IN_SCRIPTING>"?>"{NEWLINE}? { BEGIN(INITIAL); - return T_CLOSE_TAG; /* implicit ';' at php-end tag */ + RETURN_TOKEN(T_CLOSE_TAG); /* implicit ';' at php-end tag */ } @@ -1965,7 +1988,7 @@ inline_char_handler: * for ' (unrecognized by parser), instead of old flex fallback to "Unexpected character..." * rule, which continued in ST_IN_SCRIPTING state after the quote */ ZVAL_NULL(zendlval); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } } @@ -2008,7 +2031,7 @@ inline_char_handler: SCNG(output_filter)((unsigned char **)&str, &sz, (unsigned char *)s, (size_t)Z_STRLEN_P(zendlval)); ZVAL_STRINGL(zendlval, str, sz); } - return T_CONSTANT_ENCAPSED_STRING; + RETURN_TOKEN(T_CONSTANT_ENCAPSED_STRING); } @@ -2020,9 +2043,9 @@ inline_char_handler: case '"': yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext+bprefix+1, yyleng-bprefix-2, '"') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_CONSTANT_ENCAPSED_STRING; + RETURN_TOKEN(T_CONSTANT_ENCAPSED_STRING); case '$': if (IS_LABEL_START(*YYCURSOR) || *YYCURSOR == '{') { break; @@ -2052,7 +2075,7 @@ inline_char_handler: YYCURSOR = SCNG(yy_text) + yyleng; BEGIN(ST_DOUBLE_QUOTES); - return '"'; + RETURN_TOKEN('"'); } @@ -2100,13 +2123,13 @@ inline_char_handler: zend_ptr_stack_push(&SCNG(heredoc_label_stack), (void *) heredoc_label); - return T_START_HEREDOC; + RETURN_TOKEN(T_START_HEREDOC); } <ST_IN_SCRIPTING>[`] { BEGIN(ST_BACKQUOTE); - return '`'; + RETURN_TOKEN('`'); } @@ -2120,7 +2143,7 @@ inline_char_handler: efree(heredoc_label); BEGIN(ST_IN_SCRIPTING); - return T_END_HEREDOC; + RETURN_TOKEN(T_END_HEREDOC); } @@ -2128,18 +2151,18 @@ inline_char_handler: Z_LVAL_P(zendlval) = (zend_long) '{'; yy_push_state(ST_IN_SCRIPTING); yyless(1); - return T_CURLY_OPEN; + RETURN_TOKEN(T_CURLY_OPEN); } <ST_DOUBLE_QUOTES>["] { BEGIN(ST_IN_SCRIPTING); - return '"'; + RETURN_TOKEN('"'); } <ST_BACKQUOTE>[`] { BEGIN(ST_IN_SCRIPTING); - return '`'; + RETURN_TOKEN('`'); } @@ -2152,7 +2175,7 @@ inline_char_handler: } if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } if (yytext[0] == '\\' && YYCURSOR < YYLIMIT) { YYCURSOR++; @@ -2189,15 +2212,15 @@ double_quotes_scan_done: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng, '"') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } <ST_BACKQUOTE>{ANY_CHAR} { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } if (yytext[0] == '\\' && YYCURSOR < YYLIMIT) { YYCURSOR++; @@ -2233,9 +2256,9 @@ double_quotes_scan_done: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng, '`') == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } @@ -2245,7 +2268,7 @@ double_quotes_scan_done: zend_heredoc_label *heredoc_label = zend_ptr_stack_top(&SCNG(heredoc_label_stack)); if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } YYCURSOR--; @@ -2309,9 +2332,9 @@ heredoc_scan_done: yyleng = YYCURSOR - SCNG(yy_text); if (zend_scan_escape_string(zendlval, yytext, yyleng - newline, 0) == FAILURE) { - return T_ERROR; + RETURN_TOKEN(T_ERROR); } - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } @@ -2321,7 +2344,7 @@ heredoc_scan_done: zend_heredoc_label *heredoc_label = zend_ptr_stack_top(&SCNG(heredoc_label_stack)); if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } YYCURSOR--; @@ -2368,13 +2391,13 @@ nowdoc_scan_done: zend_copy_value(zendlval, yytext, yyleng - newline); HANDLE_NEWLINES(yytext, yyleng - newline); - return T_ENCAPSED_AND_WHITESPACE; + RETURN_TOKEN(T_ENCAPSED_AND_WHITESPACE); } <ST_IN_SCRIPTING,ST_VAR_OFFSET>{ANY_CHAR} { if (YYCURSOR > YYLIMIT) { - return 0; + RETURN_TOKEN(END); } zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); diff --git a/Zend/zend_long.h b/Zend/zend_long.h index 1acd2cfb08..4c81105438 100644 --- a/Zend/zend_long.h +++ b/Zend/zend_long.h @@ -25,7 +25,7 @@ #include "main/php_stdint.h" /* This is the heart of the whole int64 enablement in zval. */ -#if defined(__X86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64) +#if defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64) # define ZEND_ENABLE_ZVAL_LONG64 1 #endif diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 363ea139d6..74ae382df4 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -265,15 +265,14 @@ static void zend_std_call_issetter(zval *object, zval *member, zval *retval) /* static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce) /* {{{ */ { - switch (property_info->flags & ZEND_ACC_PPP_MASK) { - case ZEND_ACC_PUBLIC: - return 1; - case ZEND_ACC_PROTECTED: - return zend_check_protected(property_info->ce, EG(scope)); - case ZEND_ACC_PRIVATE: - return (ce == EG(scope) || property_info->ce == EG(scope)); + if (property_info->flags & ZEND_ACC_PUBLIC) { + return 1; + } else if (property_info->flags & ZEND_ACC_PRIVATE) { + return (ce == EG(scope) || property_info->ce == EG(scope)); + } else { + ZEND_ASSERT(property_info->flags & ZEND_ACC_PROTECTED); + return zend_check_protected(property_info->ce, EG(scope)); } - return 0; } /* }}} */ @@ -673,7 +672,7 @@ write_std_property: if (Z_REFCOUNTED_P(value)) { if (Z_ISREF_P(value)) { /* if we assign referenced variable, we should separate it */ - ZVAL_DUP(&tmp, Z_REFVAL_P(value)); + ZVAL_COPY(&tmp, Z_REFVAL_P(value)); value = &tmp; } else { Z_ADDREF_P(value); @@ -1256,8 +1255,10 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p goto undeclared_property; } - if (UNEXPECTED(zend_update_class_constants(ce)) != SUCCESS) { - return NULL; + if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) { + if (UNEXPECTED(zend_update_class_constants(ce)) != SUCCESS) { + return NULL; + } } ret = CE_STATIC_MEMBERS(ce) + property_info->offset; diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 2826c322a9..9007661f03 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -164,7 +164,8 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o dst++; } while (src != end); } - if (old_object->properties) { + if (old_object->properties && + EXPECTED(zend_hash_num_elements(old_object->properties))) { zval *prop, new_prop; zend_ulong num_key; zend_string *key; @@ -172,6 +173,9 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o if (!new_object->properties) { ALLOC_HASHTABLE(new_object->properties); zend_hash_init(new_object->properties, zend_hash_num_elements(old_object->properties), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_real_init(new_object->properties, 0); + } else { + zend_hash_extend(new_object->properties, new_object->properties->nNumUsed + zend_hash_num_elements(old_object->properties), 0); } ZEND_HASH_FOREACH_KEY_VAL(old_object->properties, num_key, key, prop) { @@ -181,8 +185,8 @@ ZEND_API void zend_objects_clone_members(zend_object *new_object, zend_object *o ZVAL_COPY_VALUE(&new_prop, prop); zval_add_ref(&new_prop); } - if (key) { - zend_hash_add_new(new_object->properties, key, &new_prop); + if (EXPECTED(key)) { + _zend_hash_append(new_object->properties, key, &new_prop); } else { zend_hash_index_add_new(new_object->properties, num_key, &new_prop); } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index f24fc9f193..abb91b1ee3 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -180,6 +180,7 @@ ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce) #else ce->static_members_table = NULL; #endif + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; while (p != end) { i_zval_ptr_dtor(p ZEND_FILE_LINE_CC); p++; @@ -409,6 +410,10 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) } if (arg_info[i].class_name) { zend_string_release(arg_info[i].class_name); + + if (arg_info[i].lower_class_name) { + zend_string_release(arg_info[i].lower_class_name); + } } } efree(arg_info); @@ -666,6 +671,20 @@ static void zend_resolve_finally_ret(zend_op_array *op_array, uint32_t op_num) } } +static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) { + int nest_levels = opline->op2.num; + int array_offset = opline->op1.num; + zend_brk_cont_element *jmp_to; + do { + jmp_to = &op_array->brk_cont_array[array_offset]; + if (nest_levels > 1) { + array_offset = jmp_to->parent; + } + } while (--nest_levels > 0); + + return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont; +} + static void zend_resolve_finally_calls(zend_op_array *op_array) { uint32_t i, j; @@ -681,22 +700,8 @@ static void zend_resolve_finally_calls(zend_op_array *op_array) break; case ZEND_BRK: case ZEND_CONT: - { - int nest_levels, array_offset; - zend_brk_cont_element *jmp_to; - - nest_levels = Z_LVAL(op_array->literals[opline->op2.constant]); - if ((array_offset = opline->op1.opline_num) != -1) { - do { - jmp_to = &op_array->brk_cont_array[array_offset]; - if (nest_levels > 1) { - array_offset = jmp_to->parent; - } - } while (--nest_levels > 0); - zend_resolve_finally_call(op_array, i, opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont); - break; - } - } + zend_resolve_finally_call(op_array, i, zend_get_brk_cont_target(op_array, opline)); + break; case ZEND_GOTO: if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) { uint32_t num = opline->op2.constant; @@ -774,6 +779,16 @@ ZEND_API int pass_two(zend_op_array *op_array) case ZEND_DECLARE_INHERITED_CLASS_DELAYED: opline->extended_value = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->extended_value); break; + case ZEND_BRK: + case ZEND_CONT: + { + uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline); + opline->opcode = ZEND_JMP; + opline->op1.opline_num = jmp_target; + opline->op2.num = 0; + ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1); + } + break; case ZEND_GOTO: if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) { zend_resolve_goto_label(op_array, opline, 1); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index e384031ddb..a6172179f5 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -209,7 +209,7 @@ try_again: (op) = &(holder); \ break; \ case IS_OBJECT: \ - ZVAL_DUP(&(holder), op); \ + ZVAL_COPY(&(holder), op); \ convert_to_long_base(&(holder), 10); \ if (Z_TYPE(holder) == IS_LONG) { \ (op) = &(holder); \ @@ -312,7 +312,7 @@ ZEND_API void ZEND_FASTCALL convert_to_long_base(zval *op, int base) /* {{{ */ break; case IS_ARRAY: tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0); - zval_dtor(op); + zval_ptr_dtor(op); ZVAL_LONG(op, tmp); break; case IS_OBJECT: @@ -369,7 +369,7 @@ ZEND_API void ZEND_FASTCALL convert_to_double(zval *op) /* {{{ */ break; case IS_ARRAY: tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0); - zval_dtor(op); + zval_ptr_dtor(op); ZVAL_DOUBLE(op, tmp); break; case IS_OBJECT: @@ -408,7 +408,7 @@ ZEND_API void ZEND_FASTCALL convert_to_null(zval *op) /* {{{ */ } } - zval_dtor(op); + zval_ptr_dtor(op); ZVAL_NULL(op); } /* }}} */ @@ -452,7 +452,7 @@ ZEND_API void ZEND_FASTCALL convert_to_boolean(zval *op) /* {{{ */ break; case IS_ARRAY: tmp = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0); - zval_dtor(op); + zval_ptr_dtor(op); ZVAL_BOOL(op, tmp); break; case IS_OBJECT: @@ -516,7 +516,7 @@ ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op ZEND_FILE_LINE_DC) /* {{ } case IS_ARRAY: zend_error(E_NOTICE, "Array to string conversion"); - zval_dtor(op); + zval_ptr_dtor(op); ZVAL_NEW_STR(op, zend_string_init("Array", sizeof("Array")-1, 0)); break; case IS_OBJECT: { @@ -603,14 +603,10 @@ ZEND_API void ZEND_FASTCALL convert_to_object(zval *op) /* {{{ */ switch (Z_TYPE_P(op)) { case IS_ARRAY: { - HashTable *properties = emalloc(sizeof(HashTable)); - zend_array *arr = Z_ARR_P(op); - - memcpy(properties, Z_ARRVAL_P(op), sizeof(HashTable)); - object_and_properties_init(op, zend_standard_class_def, properties); - if (--GC_REFCOUNT(arr) == 0) { - efree_size(arr, sizeof(zend_array)); - } + zval tmp; + ZVAL_COPY_VALUE(&tmp, op); + SEPARATE_ARRAY(&tmp); + object_and_properties_init(op, zend_standard_class_def, Z_ARR(tmp)); break; } case IS_OBJECT: @@ -1249,11 +1245,20 @@ try_again: case IS_STRING: { size_t i; - ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(op1), 0)); - for (i = 0; i < Z_STRLEN_P(op1); i++) { - Z_STRVAL_P(result)[i] = ~Z_STRVAL_P(op1)[i]; + if (Z_STRLEN_P(op1) == 1) { + zend_uchar not = (zend_uchar) ~*Z_STRVAL_P(op1); + if (CG(one_char_string)[not]) { + ZVAL_INTERNED_STR(result, CG(one_char_string)[not]); + } else { + ZVAL_NEW_STR(result, zend_string_init((char *) ¬, 1, 0)); + } + } else { + ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(op1), 0)); + for (i = 0; i < Z_STRLEN_P(op1); i++) { + Z_STRVAL_P(result)[i] = ~Z_STRVAL_P(op1)[i]; + } + Z_STRVAL_P(result)[i] = 0; } - Z_STRVAL_P(result)[i] = 0; return SUCCESS; } case IS_REFERENCE: @@ -1280,12 +1285,21 @@ ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op ZVAL_DEREF(op1); ZVAL_DEREF(op2); - if (Z_TYPE_P(op1) == IS_STRING && Z_TYPE_P(op2) == IS_STRING) { + if (Z_TYPE_P(op1) == IS_STRING && EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { zval *longer, *shorter; zend_string *str; size_t i; - if (Z_STRLEN_P(op1) >= Z_STRLEN_P(op2)) { + if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) { + if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) { + zend_uchar or = (zend_uchar) (*Z_STRVAL_P(op1) | *Z_STRVAL_P(op2)); + if (CG(one_char_string)[or]) { + ZVAL_INTERNED_STR(result, CG(one_char_string)[or]); + } else { + ZVAL_NEW_STR(result, zend_string_init((char *) &or, 1, 0)); + } + return SUCCESS; + } longer = op1; shorter = op2; } else { @@ -1343,7 +1357,16 @@ ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *o zend_string *str; size_t i; - if (Z_STRLEN_P(op1) >= Z_STRLEN_P(op2)) { + if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) { + if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) { + zend_uchar and = (zend_uchar) (*Z_STRVAL_P(op1) & *Z_STRVAL_P(op2)); + if (CG(one_char_string)[and]) { + ZVAL_INTERNED_STR(result, CG(one_char_string)[and]); + } else { + ZVAL_NEW_STR(result, zend_string_init((char *) &and, 1, 0)); + } + return SUCCESS; + } longer = op1; shorter = op2; } else { @@ -1364,7 +1387,7 @@ ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *o } if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { - ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_AND, bitwise_or_function); + ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_AND, bitwise_and_function); op1_lval = _zval_get_long_func(op1); } else { op1_lval = Z_LVAL_P(op1); @@ -1401,7 +1424,16 @@ ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *o zend_string *str; size_t i; - if (Z_STRLEN_P(op1) >= Z_STRLEN_P(op2)) { + if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) { + if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) { + zend_uchar xor = (zend_uchar) (*Z_STRVAL_P(op1) ^ *Z_STRVAL_P(op2)); + if (CG(one_char_string)[xor]) { + ZVAL_INTERNED_STR(result, CG(one_char_string)[xor]); + } else { + ZVAL_NEW_STR(result, zend_string_init((char *) &xor, 1, 0)); + } + return SUCCESS; + } longer = op1; shorter = op2; } else { @@ -1422,7 +1454,7 @@ ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *o } if (UNEXPECTED(Z_TYPE_P(op1) != IS_LONG)) { - ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR, bitwise_or_function); + ZEND_TRY_BINARY_OP1_OBJECT_OPERATION(ZEND_BW_XOR, bitwise_xor_function); op1_lval = _zval_get_long_func(op1); } else { op1_lval = Z_LVAL_P(op1); diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index c026f23415..17d1bcffb4 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -364,7 +364,6 @@ ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_D #define convert_to_ex_master(pzv, lower_type, upper_type) \ if (Z_TYPE_P(pzv)!=upper_type) { \ - SEPARATE_ZVAL_IF_NOT_REF(pzv); \ convert_to_##lower_type(pzv); \ } @@ -400,7 +399,6 @@ ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_D #define convert_to_explicit_type_ex(pzv, str_type) \ if (Z_TYPE_P(pzv) != str_type) { \ - SEPARATE_ZVAL_IF_NOT_REF(pzv); \ convert_to_explicit_type(pzv, str_type); \ } @@ -414,7 +412,6 @@ ZEND_API void ZEND_FASTCALL zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_D #define convert_scalar_to_number_ex(pzv) \ if (Z_TYPE_P(pzv)!=IS_LONG && Z_TYPE_P(pzv)!=IS_DOUBLE) { \ - SEPARATE_ZVAL_IF_NOT_REF(pzv); \ convert_scalar_to_number(pzv); \ } diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index eca9c83f89..65455ee10f 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -240,6 +240,7 @@ char *alloca(); #if (defined(__GNUC__) && __GNUC__ >= 3 && !defined(__INTEL_COMPILER) && !defined(DARWIN) && !defined(__hpux) && !defined(_AIX) && !defined(__osf__)) # define HAVE_NORETURN_ALIAS +# define HAVE_ATTRIBUTE_WEAK #endif #if ZEND_DEBUG diff --git a/Zend/zend_types.h b/Zend/zend_types.h index d66da1eade..63565182ce 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -229,13 +229,13 @@ struct _zend_array { HT_HASH_EX((ht)->arData, idx) #define HT_HASH_SIZE(ht) \ - ((-(int32_t)(ht)->nTableMask) * sizeof(uint32_t)) + (((size_t)(uint32_t)-(int32_t)(ht)->nTableMask) * sizeof(uint32_t)) #define HT_DATA_SIZE(ht) \ - ((ht)->nTableSize * sizeof(Bucket)) + ((size_t)(ht)->nTableSize * sizeof(Bucket)) #define HT_SIZE(ht) \ (HT_HASH_SIZE(ht) + HT_DATA_SIZE(ht)) #define HT_USED_SIZE(ht) \ - (HT_HASH_SIZE(ht) + ((ht)->nNumUsed * sizeof(Bucket))) + (HT_HASH_SIZE(ht) + ((size_t)(ht)->nNumUsed * sizeof(Bucket))) #define HT_HASH_RESET(ht) \ memset(&HT_HASH(ht, (ht)->nTableMask), HT_INVALID_IDX, HT_HASH_SIZE(ht)) #define HT_HASH_RESET_PACKED(ht) do { \ @@ -802,7 +802,7 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) { return --GC_REFCOUNT(Z_COUNTED_P(pz)); } -#if SIZEOF_ZEND_LONG == 4 +#if SIZEOF_SIZE_T == 4 # define ZVAL_COPY_VALUE_EX(z, v, gc, t) \ do { \ uint32_t _w2 = v->value.ww.w2; \ @@ -810,14 +810,14 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) { z->value.ww.w2 = _w2; \ Z_TYPE_INFO_P(z) = t; \ } while (0) -#elif SIZEOF_ZEND_LONG == 8 +#elif SIZEOF_SIZE_T == 8 # define ZVAL_COPY_VALUE_EX(z, v, gc, t) \ do { \ Z_COUNTED_P(z) = gc; \ Z_TYPE_INFO_P(z) = t; \ } while (0) #else -# error "Unknbown SIZEOF_ZEND_LONG" +# error "Unknown SIZEOF_SIZE_T" #endif #define ZVAL_COPY_VALUE(z, v) \ @@ -890,11 +890,12 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) { #define SEPARATE_ARRAY(zv) do { \ zval *_zv = (zv); \ - if (Z_REFCOUNT_P(_zv) > 1) { \ + zend_array *_arr = Z_ARR_P(_zv); \ + if (GC_REFCOUNT(_arr) > 1) { \ if (!Z_IMMUTABLE_P(_zv)) { \ - Z_DELREF_P(_zv); \ + GC_REFCOUNT(_arr)--; \ } \ - zval_copy_ctor_func(_zv); \ + ZVAL_ARR(_zv, zend_array_dup(_arr)); \ } \ } while (0) diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index 28cc7f6eed..cc3fff4416 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -54,10 +54,6 @@ # endif #endif -#ifndef S_IFLNK -# define S_IFLNK 0120000 -#endif - #ifdef NETWARE #include <fsio.h> #endif @@ -89,14 +85,6 @@ cwd_state main_cwd_state; /* True global */ #include <direct.h> #endif -#ifndef S_ISDIR -#define S_ISDIR(mode) ((mode) & _S_IFDIR) -#endif - -#ifndef S_ISREG -#define S_ISREG(mode) ((mode) & _S_IFREG) -#endif - #ifdef TSRM_WIN32 #include <tchar.h> #define tsrm_strtok_r(a,b,c) _tcstok((a),(b)) @@ -237,6 +225,10 @@ CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD); gfpnh_func pGetFinalPathNameByHandle; + if (!target_len) { + return -1; + } + kernel32 = LoadLibrary("kernel32.dll"); if (kernel32) { @@ -260,8 +252,14 @@ CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ return -1; } - dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS); - if(dwRet >= MAXPATHLEN || dwRet == 0) { + /* Despite MSDN has documented it won't to, the length returned by + GetFinalPathNameByHandleA includes the length of the + null terminator. This behavior is at least reproducible + with VS2012 and earlier, and seems not to be fixed till + now. Thus, correcting target_len so it's suddenly don't + overflown. */ + dwRet = pGetFinalPathNameByHandle(hFile, target, target_len - 1, VOLUME_NAME_DOS); + if(dwRet >= target_len || dwRet >= MAXPATHLEN || dwRet == 0) { return -1; } @@ -555,6 +553,11 @@ CWD_API char *virtual_getcwd_ex(size_t *length) /* {{{ */ return retval; } #endif + if (!state->cwd) { + *length = 0; + return NULL; + } + *length = state->cwd_length; return estrdup(state->cwd); } @@ -576,6 +579,9 @@ CWD_API char *virtual_getcwd(char *buf, size_t size) /* {{{ */ errno = ERANGE; /* Is this OK? */ return NULL; } + if (!cwd) { + return NULL; + } memcpy(buf, cwd, length+1); efree(cwd); return buf; diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index d7d1067828..019888757f 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -337,4 +337,33 @@ CWD_API realpath_cache_bucket** realpath_cache_get_buckets(void); #endif +/* Global stat declarations */ +#ifndef _S_IFDIR +#define _S_IFDIR S_IFDIR +#endif + +#ifndef _S_IFREG +#define _S_IFREG S_IFREG +#endif + +#ifndef S_IFLNK +# define S_IFLNK 0120000 +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISLNK +#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) +#endif + +#ifndef S_IXROOT +#define S_IXROOT ( S_IXUSR | S_IXGRP | S_IXOTH ) +#endif + #endif /* VIRTUAL_CWD_H */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 5d6b128cb3..0fe820a20f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -164,11 +164,12 @@ ZEND_VM_HANDLER(4, ZEND_DIV, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + fast_div_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -218,11 +219,12 @@ ZEND_VM_HANDLER(6, ZEND_SL, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + shift_left_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -233,11 +235,12 @@ ZEND_VM_HANDLER(7, ZEND_SR, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + shift_right_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -248,12 +251,58 @@ ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); - FREE_OP1(); + op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + + do { + if ((OP1_TYPE == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (OP2_TYPE == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if (OP1_TYPE != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + FREE_OP1(); + break; + } + } + if (OP2_TYPE != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + FREE_OP1(); + break; + } + } + if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + FREE_OP1(); + } while (0); FREE_OP2(); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -263,12 +312,13 @@ ZEND_VM_HANDLER(15, ZEND_IS_IDENTICAL, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R), - GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R); + result = fast_is_identical_function(op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_SMART_BRANCH(result, (OP1_TYPE|OP2_TYPE) & (IS_VAR|IS_TMP_VAR)); @@ -283,12 +333,13 @@ ZEND_VM_HANDLER(16, ZEND_IS_NOT_IDENTICAL, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R), - GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R); + result = fast_is_not_identical_function(op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_SMART_BRANCH(result, (OP1_TYPE|OP2_TYPE) & (IS_VAR|IS_TMP_VAR)); @@ -310,18 +361,18 @@ ZEND_VM_HANDLER(17, ZEND_IS_EQUAL, CONST|TMPVAR|CV, CONST|TMPVAR|CV) do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -353,10 +404,10 @@ ZEND_VM_HANDLER(17, ZEND_IS_EQUAL, CONST|TMPVAR|CV, CONST|TMPVAR|CV) } while (0); SAVE_OPLINE(); - if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -379,18 +430,18 @@ ZEND_VM_HANDLER(18, ZEND_IS_NOT_EQUAL, CONST|TMPVAR|CV, CONST|TMPVAR|CV) do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -422,10 +473,10 @@ ZEND_VM_HANDLER(18, ZEND_IS_NOT_EQUAL, CONST|TMPVAR|CV, CONST|TMPVAR|CV) } while (0); SAVE_OPLINE(); - if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -543,12 +594,12 @@ ZEND_VM_HANDLER(170, ZEND_SPACESHIP, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + compare_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -559,11 +610,12 @@ ZEND_VM_HANDLER(9, ZEND_BW_OR, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -574,11 +626,12 @@ ZEND_VM_HANDLER(10, ZEND_BW_AND, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -589,11 +642,12 @@ ZEND_VM_HANDLER(11, ZEND_BW_XOR, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -604,11 +658,12 @@ ZEND_VM_HANDLER(14, ZEND_BOOL_XOR, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -678,7 +733,6 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR| zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -708,42 +762,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMPVAR| ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -775,7 +794,6 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_dim_helper, VAR|UNUSED|CV, CONST|TMPVAR| zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -846,7 +864,6 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_helper, VAR|CV, CONST|TMPVAR|CV, binary_ if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1091,7 +1108,7 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_BW_XOR, VAR|UNUSED|CV, CONST|TMPVAR|UNUSED|CV) #endif } -ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, incdec_t incdec_op) +ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc) { USE_OPLINE zend_free_op free_op1, free_op2; @@ -1113,7 +1130,6 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1134,51 +1150,27 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -1190,15 +1182,15 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|C ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) { - ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, incdec_op, increment_function); + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, inc, 1); } ZEND_VM_HANDLER(133, ZEND_PRE_DEC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) { - ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, incdec_op, decrement_function); + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_pre_incdec_property_helper, inc, 0); } -ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, incdec_t incdec_op) +ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR|CV, int inc) { USE_OPLINE zend_free_op free_op1, free_op2; @@ -1220,7 +1212,6 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR| if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1239,44 +1230,25 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR| if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -1288,12 +1260,12 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMPVAR| ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) { - ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, incdec_op, increment_function); + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, inc, 1); } ZEND_VM_HANDLER(135, ZEND_POST_DEC_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) { - ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, incdec_op, decrement_function); + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_post_incdec_property_helper, inc, 0); } ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY) @@ -1307,7 +1279,6 @@ ZEND_VM_HANDLER(34, ZEND_PRE_INC, VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1355,7 +1326,6 @@ ZEND_VM_HANDLER(35, ZEND_PRE_DEC, VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1403,7 +1373,6 @@ ZEND_VM_HANDLER(36, ZEND_POST_INC, VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1444,7 +1413,6 @@ ZEND_VM_HANDLER(37, ZEND_POST_DEC, VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1481,7 +1449,7 @@ ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMPVAR|CV, ANY) zval *z; SAVE_OPLINE(); - z = GET_OP1_ZVAL_PTR(BP_VAR_R); + z = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); if (Z_TYPE_P(z) == IS_STRING) { zend_string *str = Z_STR_P(z); @@ -1494,6 +1462,8 @@ ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMPVAR|CV, ANY) if (str->len != 0) { zend_write(str->val, str->len); + } else if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(z) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(z, BP_VAR_R); } zend_string_release(str); } @@ -1513,7 +1483,7 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED|CONST|V HashTable *target_symbol_table; SAVE_OPLINE(); - varname = GET_OP1_ZVAL_PTR(BP_VAR_R); + varname = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP1_TYPE == IS_CONST) { name = Z_STR_P(varname); @@ -1521,6 +1491,9 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMPVAR|CV, UNUSED|CONST|V name = Z_STR_P(varname); zend_string_addref(name); } else { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -1714,7 +1687,6 @@ ZEND_VM_HANDLER(84, ZEND_FETCH_DIM_W, VAR|CV, CONST|TMPVAR|UNUSED|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE); @@ -1738,7 +1710,6 @@ ZEND_VM_HANDLER(87, ZEND_FETCH_DIM_RW, VAR|CV, CONST|TMPVAR|UNUSED|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE); @@ -1785,7 +1756,6 @@ ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUS if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE); @@ -1822,7 +1792,6 @@ ZEND_VM_HANDLER(96, ZEND_FETCH_DIM_UNSET, VAR|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE); @@ -1854,7 +1823,8 @@ ZEND_VM_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMP|VAR|UNUSED|CV, CONST|TMPVAR|CV) offset = GET_OP2_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (OP1_TYPE == IS_CONST || + (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -1927,7 +1897,6 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -1960,7 +1929,6 @@ ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -1992,7 +1960,8 @@ ZEND_VM_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR|CV) offset = GET_OP2_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (OP1_TYPE == IS_CONST || + (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -2074,7 +2043,6 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, CONST|TMP|VAR|UNUSED|CV, CONST|TMPV if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -2110,7 +2078,6 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -2130,7 +2097,7 @@ ZEND_VM_HANDLER(98, ZEND_FETCH_LIST, CONST|TMPVAR|CV, CONST) zval *container; SAVE_OPLINE(); - container = GET_OP1_ZVAL_PTR(BP_VAR_R); + container = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); ZEND_VM_C_LABEL(try_fetch_list): if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { @@ -2142,7 +2109,8 @@ ZEND_VM_C_LABEL(try_fetch_list): } else { ZVAL_COPY(EX_VAR(opline->result.var), value); } - } else if (UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && + } else if (OP1_TYPE != IS_CONST && + UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && EXPECTED(Z_OBJ_HT_P(container)->read_dimension)) { zval *result = EX_VAR(opline->result.var); zval *retval = Z_OBJ_HT_P(container)->read_dimension(container, EX_CONSTANT(opline->op2), BP_VAR_R, result); @@ -2154,10 +2122,13 @@ ZEND_VM_C_LABEL(try_fetch_list): } else { ZVAL_NULL(result); } - } else if (Z_TYPE_P(container) == IS_REFERENCE) { + } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(container) == IS_REFERENCE) { container = Z_REFVAL_P(container); ZEND_VM_C_GOTO(try_fetch_list); } else { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(container, BP_VAR_R); + } ZVAL_NULL(EX_VAR(opline->result.var)); } CHECK_EXCEPTION(); @@ -2185,7 +2156,6 @@ ZEND_VM_HANDLER(136, ZEND_ASSIGN_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, OP1_TYPE, property_name, OP2_TYPE, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -2214,11 +2184,9 @@ ZEND_VM_HANDLER(147, ZEND_ASSIGN_DIM, VAR|CV, CONST|TMPVAR|UNUSED|CV) zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } -ZEND_VM_C_LABEL(try_assign_dim): if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { ZEND_VM_C_LABEL(try_assign_dim_array): if (OP2_TYPE == IS_UNUSED) { @@ -2246,53 +2214,58 @@ ZEND_VM_C_LABEL(try_assign_dim_array): ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zend_free_op free_op2; - zval *property_name = GET_OP2_ZVAL_PTR(BP_VAR_R); + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + ZEND_VM_C_GOTO(try_assign_dim_array); + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_free_op free_op2; + zval *property_name = GET_OP2_ZVAL_PTR(BP_VAR_R); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - FREE_OP2(); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (OP2_TYPE == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - FREE_OP1_VAR_PTR(); - HANDLE_EXCEPTION(); - } else { - zend_long offset; + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + FREE_OP2(); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (OP2_TYPE == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + FREE_OP1_VAR_PTR(); + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - FREE_OP2(); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + dim = GET_OP2_ZVAL_PTR(BP_VAR_R); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + FREE_OP2(); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +ZEND_VM_C_LABEL(assign_dim_convert_to_array): + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + ZEND_VM_C_GOTO(try_assign_dim_array); } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (OP1_TYPE == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + ZEND_VM_C_GOTO(assign_dim_clean); + } + ZEND_VM_C_GOTO(assign_dim_convert_to_array); } else { - zval_ptr_dtor_nogc(object_ptr); -ZEND_VM_C_LABEL(assign_dim_convert_to_array): - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - ZEND_VM_C_GOTO(try_assign_dim_array); - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - ZEND_VM_C_GOTO(try_assign_dim); - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - ZEND_VM_C_GOTO(assign_dim_clean); - } - ZEND_VM_C_GOTO(assign_dim_convert_to_array); - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); ZEND_VM_C_LABEL(assign_dim_clean): - dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - FREE_OP2(); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + dim = GET_OP2_ZVAL_PTR(BP_VAR_R); + FREE_OP2(); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } FREE_OP1_VAR_PTR(); @@ -2343,7 +2316,6 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV) if (OP2_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - FREE_OP2_VAR_PTR(); FREE_UNFETCHED_OP1(); HANDLE_EXCEPTION(); } @@ -2366,7 +2338,6 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(variable_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); FREE_OP2_VAR_PTR(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } if (OP1_TYPE == IS_VAR && @@ -2735,28 +2706,60 @@ ZEND_VM_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV) zend_string *op1_str, *op2_str, *str; SAVE_OPLINE(); - op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); - op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP1_TYPE == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP2_TYPE == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (OP1_TYPE != IS_CONST) { - zend_string_release(op1_str); - } - if (OP2_TYPE != IS_CONST) { - zend_string_release(op2_str); + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (OP1_TYPE != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (OP2_TYPE == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (OP2_TYPE != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (OP1_TYPE == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (OP1_TYPE != IS_CONST) { + zend_string_release(op1_str); + } + if (OP2_TYPE != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -2776,11 +2779,22 @@ ZEND_VM_HANDLER(54, ZEND_ROPE_INIT, UNUSED, CONST|TMPVAR|CV) var = GET_OP2_ZVAL_PTR(BP_VAR_R); rope[0] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = GET_OP2_ZVAL_PTR(BP_VAR_R); - rope[0] = zval_get_string(var); - FREE_OP2(); - CHECK_EXCEPTION(); + var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (OP2_TYPE == IS_CV) { + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + rope[0] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[0] = _zval_get_string_func(var); + FREE_OP2(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -2798,11 +2812,22 @@ ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV) var = GET_OP2_ZVAL_PTR(BP_VAR_R); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = GET_OP2_ZVAL_PTR(BP_VAR_R); - rope[opline->extended_value] = zval_get_string(var); - FREE_OP2(); - CHECK_EXCEPTION(); + var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (OP2_TYPE == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); + FREE_OP2(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -2822,11 +2847,22 @@ ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV) var = GET_OP2_ZVAL_PTR(BP_VAR_R); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = GET_OP2_ZVAL_PTR(BP_VAR_R); - rope[opline->extended_value] = zval_get_string(var); - FREE_OP2(); - CHECK_EXCEPTION(); + var = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (OP2_TYPE == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); + FREE_OP2(); + CHECK_EXCEPTION(); + } } for (i = 0; i <= opline->extended_value; i++) { len += rope[i]->len; @@ -2858,7 +2894,7 @@ ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ANY, CONST|TMPVAR|UNUSED|CV) ZEND_VM_NEXT_OPCODE(); } else { zend_free_op free_op2; - zval *class_name = GET_OP2_ZVAL_PTR(BP_VAR_R); + zval *class_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); ZEND_VM_C_LABEL(try_class_name): if (OP2_TYPE == IS_CONST) { @@ -2876,6 +2912,9 @@ ZEND_VM_C_LABEL(try_class_name): class_name = Z_REFVAL_P(class_name); ZEND_VM_C_GOTO(try_class_name); } else { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(class_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(class_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2902,7 +2941,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR SAVE_OPLINE(); - function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); + function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP2_TYPE != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { @@ -2913,6 +2952,9 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR break; } } + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2923,7 +2965,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR } while (0); } - object = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R); + object = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -2933,13 +2975,16 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR if (OP1_TYPE != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (OP1_TYPE == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2986,7 +3031,8 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (OP1_TYPE & (IS_VAR|IS_TMP_VAR)) { + } else if (OP1_TYPE & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -3044,9 +3090,12 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE } else if (OP2_TYPE != IS_UNUSED) { zend_free_op free_op2; - function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); + function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP2_TYPE != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -3174,7 +3223,7 @@ ZEND_VM_HANDLER(128, ZEND_INIT_DYNAMIC_CALL, ANY, CONST|TMPVAR|CV) uint32_t call_info = ZEND_CALL_NESTED_FUNCTION; SAVE_OPLINE(); - function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); + function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); ZEND_VM_C_LABEL(try_function_name): if (OP2_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) { @@ -3354,6 +3403,9 @@ ZEND_VM_C_LABEL(try_function_name): function_name = Z_REFVAL_P(function_name); ZEND_VM_C_GOTO(try_function_name); } else { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -3908,6 +3960,10 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED) } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + FREE_OP1(); + } #endif } CHECK_EXCEPTION(); @@ -3924,15 +3980,14 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R); - CHECK_EXCEPTION(); - } - - if (!EX(return_value)) { + if (EX(return_value)) { + ZVAL_NULL(EX(return_value)); + } + } else if (!EX(return_value)) { if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_TMP_VAR ) { if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) { SAVE_OPLINE(); zval_dtor_func_for_ptr(Z_COUNTED_P(free_op1)); - CHECK_EXCEPTION(); } } } else { @@ -3998,7 +4053,6 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(retval_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot return string offsets by reference"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -4080,7 +4134,7 @@ ZEND_VM_HANDLER(108, ZEND_THROW, CONST|TMP|VAR|CV, ANY) zend_free_op free_op1; SAVE_OPLINE(); - value = GET_OP1_ZVAL_PTR(BP_VAR_R); + value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); do { if (OP1_TYPE == IS_CONST || UNEXPECTED(Z_TYPE_P(value) != IS_OBJECT)) { @@ -4090,6 +4144,9 @@ ZEND_VM_HANDLER(108, ZEND_THROW, CONST|TMP|VAR|CV, ANY) break; } } + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -4297,7 +4354,6 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY) if (OP1_TYPE == IS_VAR && UNEXPECTED(varptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Only variables can be passed by reference"); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } @@ -4376,71 +4432,67 @@ ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY) int arg_num; SAVE_OPLINE(); - args = GET_OP1_ZVAL_PTR(BP_VAR_R); + args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1; ZEND_VM_C_LABEL(send_again): - switch (Z_TYPE_P(args)) { - case IS_ARRAY: { - HashTable *ht = Z_ARRVAL_P(args); - zval *arg, *top; - zend_string *name; - - zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); - - if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { - uint32_t i; - int separate = 0; - - /* check if any of arguments are going to be passed by reference */ - for (i = 0; i < zend_hash_num_elements(ht); i++) { - if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) { - separate = 1; - break; - } - } - if (separate) { - zval_copy_ctor(args); - ht = Z_ARRVAL_P(args); + if (EXPECTED(Z_TYPE_P(args) == IS_ARRAY)) { + HashTable *ht = Z_ARRVAL_P(args); + zval *arg, *top; + zend_string *name; + + zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); + + if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { + uint32_t i; + int separate = 0; + + /* check if any of arguments are going to be passed by reference */ + for (i = 0; i < zend_hash_num_elements(ht); i++) { + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) { + separate = 1; + break; } } + if (separate) { + zval_copy_ctor(args); + ht = Z_ARRVAL_P(args); + } + } - ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { - if (name) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unpack array with string keys"); - FREE_OP1(); - HANDLE_EXCEPTION(); - } + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { + if (name) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unpack array with string keys"); + FREE_OP1(); + HANDLE_EXCEPTION(); + } - top = ZEND_CALL_ARG(EX(call), arg_num); - if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { - if (!Z_IMMUTABLE_P(args)) { - ZVAL_MAKE_REF(arg); - Z_ADDREF_P(arg); - ZVAL_REF(top, Z_REF_P(arg)); - } else { - ZVAL_DUP(top, arg); - } - } else if (Z_ISREF_P(arg)) { - ZVAL_COPY(top, Z_REFVAL_P(arg)); + top = ZEND_CALL_ARG(EX(call), arg_num); + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + if (!Z_IMMUTABLE_P(args)) { + ZVAL_MAKE_REF(arg); + Z_ADDREF_P(arg); + ZVAL_REF(top, Z_REF_P(arg)); } else { - ZVAL_COPY(top, arg); + ZVAL_DUP(top, arg); } + } else if (Z_ISREF_P(arg)) { + ZVAL_COPY(top, Z_REFVAL_P(arg)); + } else { + ZVAL_COPY(top, arg); + } - ZEND_CALL_NUM_ARGS(EX(call))++; - arg_num++; - } ZEND_HASH_FOREACH_END(); + ZEND_CALL_NUM_ARGS(EX(call))++; + arg_num++; + } ZEND_HASH_FOREACH_END(); - break; - } - case IS_OBJECT: { - zend_class_entry *ce = Z_OBJCE_P(args); - zend_object_iterator *iter; + } else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) { + zend_class_entry *ce = Z_OBJCE_P(args); + zend_object_iterator *iter; - if (!ce || !ce->get_iterator) { - zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); - break; - } + if (!ce || !ce->get_iterator) { + zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); + } else { iter = ce->get_iterator(ce, args, 0); if (UNEXPECTED(!iter)) { @@ -4518,14 +4570,15 @@ ZEND_VM_C_LABEL(send_again): ZEND_VM_C_LABEL(unpack_iter_dtor): zend_iterator_dtor(iter); - break; } - case IS_REFERENCE: - args = Z_REFVAL_P(args); - ZEND_VM_C_GOTO(send_again); - break; - default: - zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); + } else if (EXPECTED(Z_ISREF_P(args))) { + args = Z_REFVAL_P(args); + ZEND_VM_C_GOTO(send_again); + } else { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(args) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(args, BP_VAR_R); + } + zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); } FREE_OP1(); @@ -4817,31 +4870,8 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(50, ZEND_BRK, ANY, CONST) -{ - USE_OPLINE - zend_brk_cont_element *el; - - SAVE_OPLINE(); - el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->op1.opline_num, - &EX(func)->op_array, execute_data); - ZEND_VM_JMP(EX(func)->op_array.opcodes + el->brk); -} - -ZEND_VM_HANDLER(51, ZEND_CONT, ANY, CONST) -{ - USE_OPLINE - zend_brk_cont_element *el; - - SAVE_OPLINE(); - el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->op1.opline_num, - &EX(func)->op_array, execute_data); - ZEND_VM_JMP(EX(func)->op_array.opcodes + el->cont); -} - ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) { - zend_op *brk_opline; USE_OPLINE zend_brk_cont_element *el; @@ -4849,14 +4879,12 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value, &EX(func)->op_array, execute_data); - brk_opline = EX(func)->op_array.opcodes + el->brk; + if (el->start >= 0) { + zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk; - if (brk_opline->opcode == ZEND_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + if (brk_opline->opcode == ZEND_FREE) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } - } else if (brk_opline->opcode == ZEND_FE_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + } else if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); @@ -4878,18 +4906,18 @@ ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV) do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -4920,10 +4948,10 @@ ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV) } while (0); SAVE_OPLINE(); - if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -4999,7 +5027,7 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|CV, ANY) zend_object_clone_obj_t clone_call; SAVE_OPLINE(); - obj = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R); + obj = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R); if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -5014,7 +5042,10 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|CV, ANY) if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { break; } - } + } + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(obj, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -5075,7 +5106,6 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST) SAVE_OPLINE(); if (OP1_TYPE == IS_UNUSED) { zend_constant *c; - zval *retval; if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); @@ -5101,67 +5131,80 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST) } else { CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c); } - retval = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(retval, &c->value); - if (Z_OPT_COPYABLE_P(retval) || Z_OPT_REFCOUNTED_P(retval)) { - if (Z_OPT_COPYABLE_P(retval)) { - zval_copy_ctor_func(retval); - } else { - Z_ADDREF_P(retval); - } +#ifdef ZTS + if (c->flags & CONST_PERSISTENT) { + ZVAL_DUP(EX_VAR(opline->result.var), &c->value); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); +#endif } else { /* class constant */ zend_class_entry *ce; zval *value; - if (OP1_TYPE == IS_CONST) { - if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { - value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - ZEND_VM_C_GOTO(constant_fetch_end); - } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { - ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); - } else { - ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + do { + if (OP1_TYPE == IS_CONST) { + if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { + value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); + ZVAL_DEREF(value); +#ifdef ZTS + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); +#endif + break; + } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); + } else { + ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); + HANDLE_EXCEPTION(); + } + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - if (UNEXPECTED(ce == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); - HANDLE_EXCEPTION(); + } else { + ce = Z_CE_P(EX_VAR(opline->op1.var)); + if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { + ZVAL_DEREF(value); + break; } - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); - } - } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); - if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - ZEND_VM_C_GOTO(constant_fetch_end); } - } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); - if (Z_CONSTANT_P(value)) { - EG(scope) = ce; - zval_update_constant_ex(value, 1, NULL); - EG(scope) = EX(func)->op_array.scope; - } - if (OP1_TYPE == IS_CONST) { - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + ZVAL_DEREF(value); + if (Z_CONSTANT_P(value)) { + EG(scope) = ce; + zval_update_constant_ex(value, 1, NULL); + EG(scope) = EX(func)->op_array.scope; + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + if (OP1_TYPE == IS_CONST) { + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + } else { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + } } else { - CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); } + } while (0); +#ifdef ZTS + if (ce->type == ZEND_INTERNAL_CLASS) { ZVAL_DUP(EX_VAR(opline->result.var), value); } else { - zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + ZVAL_COPY(EX_VAR(opline->result.var), value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), value); +#endif } -ZEND_VM_C_LABEL(constant_fetch_end): - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -5173,11 +5216,10 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE SAVE_OPLINE(); if ((OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W); if (OP1_TYPE == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - FREE_OP1_VAR_PTR(); zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -5187,11 +5229,11 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE } else { expr_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); if (OP1_TYPE == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (OP1_TYPE == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (OP1_TYPE == IS_CV) { @@ -5217,48 +5259,46 @@ ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMPVAR|UNUSE if (OP2_TYPE != IS_UNUSED) { zend_free_op free_op2; - zval *offset = GET_OP2_ZVAL_PTR(BP_VAR_R); + zval *offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); zend_string *str; zend_ulong hval; ZEND_VM_C_LABEL(add_again): - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - ZEND_VM_C_GOTO(num_index); - case IS_LONG: - hval = Z_LVAL_P(offset); -ZEND_VM_C_LABEL(num_index): - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (OP2_TYPE != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - ZEND_VM_C_GOTO(num_index); - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (OP2_TYPE != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + ZEND_VM_C_GOTO(num_index); } + } ZEND_VM_C_LABEL(str_index): - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - ZEND_VM_C_GOTO(str_index); - case IS_FALSE: - hval = 0; - ZEND_VM_C_GOTO(num_index); - case IS_TRUE: - hval = 1; - ZEND_VM_C_GOTO(num_index); - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - ZEND_VM_C_GOTO(add_again); - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +ZEND_VM_C_LABEL(num_index): + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + ZEND_VM_C_GOTO(add_again); + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index); + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + ZEND_VM_C_GOTO(num_index); + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + ZEND_VM_C_GOTO(num_index); + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + ZEND_VM_C_GOTO(num_index); + } else if (OP2_TYPE == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index); + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } FREE_OP2(); } else { @@ -5392,8 +5432,7 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY) } } } else { - ZVAL_COPY_VALUE(result, expr); - zval_opt_copy_ctor(result); + ZVAL_COPY(result, expr); convert_to_object(result); } } @@ -5414,10 +5453,13 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY) zend_bool failure_retval=0; SAVE_OPLINE(); - inc_filename = GET_OP1_ZVAL_PTR(BP_VAR_R); + inc_filename = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); ZVAL_UNDEF(&tmp_inc_filename); if (Z_TYPE_P(inc_filename) != IS_STRING) { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(inc_filename) == IS_UNDEF)) { + inc_filename = GET_OP1_UNDEF_CV(inc_filename, BP_VAR_R); + } ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); inc_filename = &tmp_inc_filename; } @@ -5560,10 +5602,13 @@ ZEND_VM_HANDLER(74, ZEND_UNSET_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR) ZEND_VM_NEXT_OPCODE(); } - varname = GET_OP1_ZVAL_PTR(BP_VAR_R); + varname = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); ZVAL_UNDEF(&tmp); if (OP1_TYPE != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -5616,6 +5661,7 @@ ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|CV, CONST|TMPVAR|CV) zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_UNSET); @@ -5627,72 +5673,81 @@ ZEND_VM_HANDLER(75, ZEND_UNSET_DIM, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } - offset = GET_OP2_ZVAL_PTR(BP_VAR_R); + offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); -ZEND_VM_C_LABEL(unset_dim_again): - if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +ZEND_VM_C_LABEL(unset_dim_array): + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); ZEND_VM_C_LABEL(offset_again): - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -ZEND_VM_C_LABEL(num_index_dim): - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (OP2_TYPE != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { ZEND_VM_C_GOTO(num_index_dim); } } +ZEND_VM_C_LABEL(str_index_dim): if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +ZEND_VM_C_LABEL(num_index_dim): + zend_hash_index_del(ht, hval); + } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + ZEND_VM_C_GOTO(offset_again); + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + ZEND_VM_C_GOTO(num_index_dim); + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index_dim); + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; ZEND_VM_C_GOTO(num_index_dim); - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; ZEND_VM_C_GOTO(num_index_dim); - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); ZEND_VM_C_GOTO(num_index_dim); - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - ZEND_VM_C_GOTO(offset_again); - break; - default: + } else if (OP2_TYPE == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index_dim); + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (OP1_TYPE != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + ZEND_VM_C_GOTO(unset_dim_array); + } } - } else if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (OP1_TYPE != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - ZEND_VM_C_GOTO(unset_dim_again); - } else if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); + FREE_OP2(); FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); @@ -5716,7 +5771,6 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); FREE_UNFETCHED_OP2(); - FREE_OP1_VAR_PTR(); HANDLE_EXCEPTION(); } offset = GET_OP2_ZVAL_PTR(BP_VAR_R); @@ -6520,14 +6574,15 @@ ZEND_VM_HANDLER(115, ZEND_ISSET_ISEMPTY_DIM_OBJ, CONST|TMPVAR|UNUSED|CV, CONST|T HANDLE_EXCEPTION(); } - offset = GET_OP2_ZVAL_PTR(BP_VAR_R); + offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); -ZEND_VM_C_LABEL(isset_dim_obj_again): if (OP1_TYPE != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +ZEND_VM_C_LABEL(isset_dim_obj_array): + ht = Z_ARRVAL_P(container); ZEND_VM_C_LABEL(isset_again): if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -6542,31 +6597,31 @@ ZEND_VM_C_LABEL(str_index_prop): hval = Z_LVAL_P(offset); ZEND_VM_C_LABEL(num_index_prop): value = zend_hash_index_find(ht, hval); + } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + ZEND_VM_C_GOTO(isset_again); + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + ZEND_VM_C_GOTO(num_index_prop); + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index_prop); + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + ZEND_VM_C_GOTO(num_index_prop); + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + ZEND_VM_C_GOTO(num_index_prop); + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + ZEND_VM_C_GOTO(num_index_prop); + } else if (OP2_TYPE == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + ZEND_VM_C_GOTO(str_index_prop); } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - ZEND_VM_C_GOTO(num_index_prop); - case IS_NULL: - str = STR_EMPTY_ALLOC(); - ZEND_VM_C_GOTO(str_index_prop); - case IS_FALSE: - hval = 0; - ZEND_VM_C_GOTO(num_index_prop); - case IS_TRUE: - hval = 1; - ZEND_VM_C_GOTO(num_index_prop); - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - ZEND_VM_C_GOTO(num_index_prop); - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - ZEND_VM_C_GOTO(isset_again); - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + ZEND_VM_C_GOTO(isset_not_found); } if (opline->extended_value & ZEND_ISSET) { @@ -6576,50 +6631,61 @@ ZEND_VM_C_LABEL(num_index_prop): } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (OP1_TYPE == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + ZEND_VM_C_GOTO(isset_dim_obj_exit); + } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + ZEND_VM_C_GOTO(isset_dim_obj_array); + } + } + + if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (OP1_TYPE == IS_UNUSED || + (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + ZEND_VM_C_GOTO(isset_not_found); } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +ZEND_VM_C_LABEL(isset_str_offset): + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + ZEND_VM_C_GOTO(isset_not_found); + } + } else { if (OP2_TYPE & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + ZEND_VM_C_GOTO(isset_str_offset); } + ZEND_VM_C_GOTO(isset_not_found); } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - ZEND_VM_C_GOTO(isset_dim_obj_again); } else { +ZEND_VM_C_LABEL(isset_not_found): result = ((opline->extended_value & ZEND_ISSET) == 0); } +ZEND_VM_C_LABEL(isset_dim_obj_exit): FREE_OP2(); FREE_OP1(); ZEND_VM_SMART_BRANCH(result, 1); @@ -6647,7 +6713,8 @@ ZEND_VM_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED|CV, CONST| offset = GET_OP2_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (OP1_TYPE == IS_CONST || + (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -6662,10 +6729,9 @@ ZEND_VM_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED|CV, CONST| ZEND_VM_C_LABEL(isset_no_object): result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } FREE_OP2(); @@ -7000,7 +7066,7 @@ ZEND_VM_HANDLER(138, ZEND_INSTANCEOF, TMPVAR|CV, CONST|VAR) zend_bool result; SAVE_OPLINE(); - expr = GET_OP1_ZVAL_PTR(BP_VAR_R); + expr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); ZEND_VM_C_LABEL(try_instanceof): if (Z_TYPE_P(expr) == IS_OBJECT) { @@ -7027,6 +7093,9 @@ ZEND_VM_C_LABEL(try_instanceof): expr = Z_REFVAL_P(expr); ZEND_VM_C_GOTO(try_instanceof); } else { + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(expr) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(expr, BP_VAR_R); + } result = 0; } FREE_OP1(); @@ -7265,17 +7334,13 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) zend_op *brk_opline = &EX(func)->op_array.opcodes[EX(func)->op_array.brk_cont_array[i].brk]; if (brk_opline->opcode == ZEND_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } + zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } else if (brk_opline->opcode == ZEND_FE_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval *var = EX_VAR(brk_opline->op1.var); - if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { - zend_hash_iterator_del(Z_FE_ITER_P(var)); - } - zval_ptr_dtor_nogc(var); + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); } + zval_ptr_dtor_nogc(var); } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { @@ -7446,7 +7511,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); FREE_UNFETCHED_OP2(); FREE_UNFETCHED_OP1(); @@ -7463,7 +7528,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE if (OP1_TYPE != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) { @@ -7473,11 +7538,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE value = GET_OP1_ZVAL_PTR(BP_VAR_R); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (OP1_TYPE != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (OP1_TYPE != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W); @@ -7485,7 +7549,6 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE if (OP1_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); FREE_UNFETCHED_OP2(); - FREE_OP1(); HANDLE_EXCEPTION(); } @@ -7508,11 +7571,14 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* Consts, temporary variables and references need copying */ if (OP1_TYPE == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (OP1_TYPE == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); FREE_OP1_IF_VAR(); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -7533,11 +7599,14 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* Consts, temporary variables and references need copying */ if (OP2_TYPE == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (OP2_TYPE == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((OP2_TYPE == IS_VAR || OP2_TYPE == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((OP2_TYPE & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); FREE_OP2_IF_VAR(); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -7742,11 +7811,12 @@ ZEND_VM_HANDLER(166, ZEND_POW, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - GET_OP1_ZVAL_PTR(BP_VAR_R), - GET_OP2_ZVAL_PTR(BP_VAR_R)); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + pow_function(EX_VAR(opline->result.var), op1, op2); FREE_OP1(); FREE_OP2(); CHECK_EXCEPTION(); @@ -7846,13 +7916,16 @@ ZEND_VM_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY) zend_free_op free_op1; SAVE_OPLINE(); - value = GET_OP1_ZVAL_PTR(BP_VAR_R); + value = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); ZEND_VM_C_LABEL(try_strlen): if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { zend_bool strict; + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + value = GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); ZEND_VM_C_GOTO(try_strlen); @@ -7890,7 +7963,7 @@ ZEND_VM_HANDLER(123, ZEND_TYPE_CHECK, CONST|TMP|VAR|CV, ANY) SAVE_OPLINE(); value = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) { - if (UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { + if (OP1_TYPE != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(value); if (UNEXPECTED(ce->name->len != sizeof("__PHP_Incomplete_Class") - 1) || diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index e98f9dbf15..1ab12b7779 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -926,71 +926,67 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ int arg_num; SAVE_OPLINE(); - args = get_zval_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, BP_VAR_R); + args = get_zval_ptr_undef(opline->op1_type, opline->op1, execute_data, &free_op1, BP_VAR_R); arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1; send_again: - switch (Z_TYPE_P(args)) { - case IS_ARRAY: { - HashTable *ht = Z_ARRVAL_P(args); - zval *arg, *top; - zend_string *name; - - zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); - - if (opline->op1_type != IS_CONST && opline->op1_type != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { - uint32_t i; - int separate = 0; - - /* check if any of arguments are going to be passed by reference */ - for (i = 0; i < zend_hash_num_elements(ht); i++) { - if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) { - separate = 1; - break; - } - } - if (separate) { - zval_copy_ctor(args); - ht = Z_ARRVAL_P(args); + if (EXPECTED(Z_TYPE_P(args) == IS_ARRAY)) { + HashTable *ht = Z_ARRVAL_P(args); + zval *arg, *top; + zend_string *name; + + zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht)); + + if (opline->op1_type != IS_CONST && opline->op1_type != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { + uint32_t i; + int separate = 0; + + /* check if any of arguments are going to be passed by reference */ + for (i = 0; i < zend_hash_num_elements(ht); i++) { + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) { + separate = 1; + break; } } + if (separate) { + zval_copy_ctor(args); + ht = Z_ARRVAL_P(args); + } + } - ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { - if (name) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unpack array with string keys"); - FREE_OP(free_op1); - HANDLE_EXCEPTION(); - } + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { + if (name) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unpack array with string keys"); + FREE_OP(free_op1); + HANDLE_EXCEPTION(); + } - top = ZEND_CALL_ARG(EX(call), arg_num); - if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { - if (!Z_IMMUTABLE_P(args)) { - ZVAL_MAKE_REF(arg); - Z_ADDREF_P(arg); - ZVAL_REF(top, Z_REF_P(arg)); - } else { - ZVAL_DUP(top, arg); - } - } else if (Z_ISREF_P(arg)) { - ZVAL_COPY(top, Z_REFVAL_P(arg)); + top = ZEND_CALL_ARG(EX(call), arg_num); + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) { + if (!Z_IMMUTABLE_P(args)) { + ZVAL_MAKE_REF(arg); + Z_ADDREF_P(arg); + ZVAL_REF(top, Z_REF_P(arg)); } else { - ZVAL_COPY(top, arg); + ZVAL_DUP(top, arg); } + } else if (Z_ISREF_P(arg)) { + ZVAL_COPY(top, Z_REFVAL_P(arg)); + } else { + ZVAL_COPY(top, arg); + } - ZEND_CALL_NUM_ARGS(EX(call))++; - arg_num++; - } ZEND_HASH_FOREACH_END(); + ZEND_CALL_NUM_ARGS(EX(call))++; + arg_num++; + } ZEND_HASH_FOREACH_END(); - break; - } - case IS_OBJECT: { - zend_class_entry *ce = Z_OBJCE_P(args); - zend_object_iterator *iter; + } else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) { + zend_class_entry *ce = Z_OBJCE_P(args); + zend_object_iterator *iter; - if (!ce || !ce->get_iterator) { - zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); - break; - } + if (!ce || !ce->get_iterator) { + zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); + } else { iter = ce->get_iterator(ce, args, 0); if (UNEXPECTED(!iter)) { @@ -1068,14 +1064,15 @@ send_again: unpack_iter_dtor: zend_iterator_dtor(iter); - break; } - case IS_REFERENCE: - args = Z_REFVAL_P(args); - goto send_again; - break; - default: - zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); + } else if (EXPECTED(Z_ISREF_P(args))) { + args = Z_REFVAL_P(args); + goto send_again; + } else { + if (opline->op1_type == IS_CV && UNEXPECTED(Z_TYPE_P(args) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(args, BP_VAR_R); + } + zend_error(E_WARNING, "Only arrays and Traversables can be unpacked"); } FREE_OP(free_op1); @@ -1615,17 +1612,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER( zend_op *brk_opline = &EX(func)->op_array.opcodes[EX(func)->op_array.brk_cont_array[i].brk]; if (brk_opline->opcode == ZEND_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } + zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } else if (brk_opline->opcode == ZEND_FE_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { - zval *var = EX_VAR(brk_opline->op1.var); - if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { - zend_hash_iterator_del(Z_FE_ITER_P(var)); - } - zval_ptr_dtor_nogc(var); + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); } + zval_ptr_dtor_nogc(var); } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { @@ -2004,6 +1997,9 @@ try_class_name: class_name = Z_REFVAL_P(class_name); goto try_class_name; } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(class_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(class_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2236,6 +2232,9 @@ try_function_name: function_name = Z_REFVAL_P(function_name); goto try_function_name; } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2352,31 +2351,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BRK_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_brk_cont_element *el; - - SAVE_OPLINE(); - el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->op1.opline_num, - &EX(func)->op_array, execute_data); - ZEND_VM_JMP(EX(func)->op_array.opcodes + el->brk); -} - -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_brk_cont_element *el; - - SAVE_OPLINE(); - el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->op1.opline_num, - &EX(func)->op_array, execute_data); - ZEND_VM_JMP(EX(func)->op_array.opcodes + el->cont); -} - static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - zend_op *brk_opline; USE_OPLINE zend_brk_cont_element *el; @@ -2384,14 +2360,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_O el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value, &EX(func)->op_array, execute_data); - brk_opline = EX(func)->op_array.opcodes + el->brk; + if (el->start >= 0) { + zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk; - if (brk_opline->opcode == ZEND_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + if (brk_opline->opcode == ZEND_FREE) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } - } else if (brk_opline->opcode == ZEND_FE_FREE) { - if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + } else if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); @@ -2461,6 +2435,9 @@ try_class_name: class_name = Z_REFVAL_P(class_name); goto try_class_name; } else { + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(class_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(class_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2486,7 +2463,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CV_HANDLER(ZE ZEND_VM_NEXT_OPCODE(); } else { - zval *class_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *class_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); try_class_name: if (IS_CV == IS_CONST) { @@ -2504,6 +2481,9 @@ try_class_name: class_name = Z_REFVAL_P(class_name); goto try_class_name; } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(class_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(class_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2528,7 +2508,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HAND uint32_t call_info = ZEND_CALL_NESTED_FUNCTION; SAVE_OPLINE(); - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); try_function_name: if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) { @@ -2708,6 +2688,9 @@ try_function_name: function_name = Z_REFVAL_P(function_name); goto try_function_name; } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2756,6 +2739,9 @@ try_class_name: class_name = Z_REFVAL_P(class_name); goto try_class_name; } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(class_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(class_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -2961,6 +2947,9 @@ try_function_name: function_name = Z_REFVAL_P(function_name); goto try_function_name; } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -3035,6 +3024,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ECHO_SPEC_CONST_HANDLER(ZEND_O if (str->len != 0) { zend_write(str->val, str->len); + } else if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(z) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(z, BP_VAR_R); } zend_string_release(str); } @@ -3252,15 +3243,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R); - CHECK_EXCEPTION(); - } - - if (!EX(return_value)) { + if (EX(return_value)) { + ZVAL_NULL(EX(return_value)); + } + } else if (!EX(return_value)) { if (IS_CONST == IS_VAR || IS_CONST == IS_TMP_VAR ) { if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) { SAVE_OPLINE(); zval_dtor_func_for_ptr(Z_COUNTED_P(free_op1)); - CHECK_EXCEPTION(); } } } else { @@ -3326,7 +3316,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDL if (IS_CONST == IS_VAR && UNEXPECTED(retval_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot return string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -3417,6 +3406,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_CONST_HANDLER(ZEND_ break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -3589,7 +3581,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_HANDLER(ZEND_ if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { break; } - } + } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(obj, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -3735,8 +3730,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_O } } } else { - ZVAL_COPY_VALUE(result, expr); - zval_opt_copy_ctor(result); + ZVAL_COPY(result, expr); convert_to_object(result); } } @@ -3760,6 +3754,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN ZVAL_UNDEF(&tmp_inc_filename); if (Z_TYPE_P(inc_filename) != IS_STRING) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(inc_filename) == IS_UNDEF)) { + inc_filename = GET_OP1_UNDEF_CV(inc_filename, BP_VAR_R); + } ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); inc_filename = &tmp_inc_filename; } @@ -4382,6 +4379,9 @@ try_strlen: } else { zend_bool strict; + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + value = GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; @@ -4419,7 +4419,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CONST_HANDLER( SAVE_OPLINE(); value = EX_CONSTANT(opline->op1); if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) { - if (UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { + if (IS_CONST != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(value); if (UNEXPECTED(ce->name->len != sizeof("__PHP_Incomplete_Class") - 1) || @@ -4605,11 +4605,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CONST_CONST_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -4659,11 +4660,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_CONST_HANDLER(ZE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -4674,11 +4676,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_CONST_HANDLER(ZE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -4689,12 +4692,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CONST_HANDLE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + + do { + if ((IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CONST != IS_CONST && IS_CONST != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -4704,12 +4753,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CONST_CONST_ { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -4724,12 +4774,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CO { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_not_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -4751,18 +4802,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_CONST_HAND do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -4794,10 +4845,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_CONST_HAND } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -4820,18 +4871,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_CONST_ do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -4863,10 +4914,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_CONST_ } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -4984,12 +5035,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CONST_CONST_HAN { USE_OPLINE - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + compare_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -5000,11 +5051,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CONST_CONST_HANDLER { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -5015,11 +5067,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CONST_CONST_HANDLE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -5030,11 +5083,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CONST_CONST_HANDLE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -5045,11 +5099,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CONST_CONST_HAND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -5074,6 +5129,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -5289,7 +5347,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -5333,7 +5390,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_CONST_H offset = EX_CONSTANT(opline->op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -5405,7 +5463,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_CONST_ offset = EX_CONSTANT(opline->op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -5486,7 +5545,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -5521,7 +5579,8 @@ try_fetch_list: } else { ZVAL_COPY(EX_VAR(opline->result.var), value); } - } else if (UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && + } else if (IS_CONST != IS_CONST && + UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && EXPECTED(Z_OBJ_HT_P(container)->read_dimension)) { zval *result = EX_VAR(opline->result.var); zval *retval = Z_OBJ_HT_P(container)->read_dimension(container, EX_CONSTANT(opline->op2), BP_VAR_R, result); @@ -5533,10 +5592,13 @@ try_fetch_list: } else { ZVAL_NULL(result); } - } else if (Z_TYPE_P(container) == IS_REFERENCE) { + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_TYPE_P(container) == IS_REFERENCE) { container = Z_REFVAL_P(container); goto try_fetch_list; } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(container, BP_VAR_R); + } ZVAL_NULL(EX_VAR(opline->result.var)); } CHECK_EXCEPTION(); @@ -5552,27 +5614,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CONST_H SAVE_OPLINE(); op1 = EX_CONSTANT(opline->op1); - op2 = EX_CONSTANT(opline->op2); if (IS_CONST == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = EX_CONSTANT(opline->op2); if (IS_CONST == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CONST != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CONST != IS_CONST) { - zend_string_release(op2_str); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); CHECK_EXCEPTION(); @@ -5604,6 +5698,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CO break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -5624,13 +5721,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CO if (IS_CONST != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -5677,7 +5777,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CO call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CONST & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CONST & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -5736,6 +5837,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C function_name = EX_CONSTANT(opline->op2); if (IS_CONST != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -5889,18 +5993,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER( do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -5931,10 +6035,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CONST_HANDLER( } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -5952,7 +6056,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONS SAVE_OPLINE(); if (IS_CONST == IS_UNUSED) { zend_constant *c; - zval *retval; if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); @@ -5978,67 +6081,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONS } else { CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c); } - retval = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(retval, &c->value); - if (Z_OPT_COPYABLE_P(retval) || Z_OPT_REFCOUNTED_P(retval)) { - if (Z_OPT_COPYABLE_P(retval)) { - zval_copy_ctor_func(retval); - } else { - Z_ADDREF_P(retval); - } +#ifdef ZTS + if (c->flags & CONST_PERSISTENT) { + ZVAL_DUP(EX_VAR(opline->result.var), &c->value); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); +#endif } else { /* class constant */ zend_class_entry *ce; zval *value; - if (IS_CONST == IS_CONST) { - if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { - value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; - } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { - ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); - } else { - ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + do { + if (IS_CONST == IS_CONST) { + if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { + value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); + ZVAL_DEREF(value); +#ifdef ZTS + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); +#endif + break; + } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); + } else { + ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); + HANDLE_EXCEPTION(); + } + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - if (UNEXPECTED(ce == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); - HANDLE_EXCEPTION(); + } else { + ce = Z_CE_P(EX_VAR(opline->op1.var)); + if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { + ZVAL_DEREF(value); + break; } - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); - } - } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); - if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; } - } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); - if (Z_CONSTANT_P(value)) { - EG(scope) = ce; - zval_update_constant_ex(value, 1, NULL); - EG(scope) = EX(func)->op_array.scope; - } - if (IS_CONST == IS_CONST) { - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + ZVAL_DEREF(value); + if (Z_CONSTANT_P(value)) { + EG(scope) = ce; + zval_update_constant_ex(value, 1, NULL); + EG(scope) = EX(func)->op_array.scope; + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + if (IS_CONST == IS_CONST) { + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + } else { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + } } else { - CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); } + } while (0); +#ifdef ZTS + if (ce->type == ZEND_INTERNAL_CLASS) { ZVAL_DUP(EX_VAR(opline->result.var), value); } else { - zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + ZVAL_COPY(EX_VAR(opline->result.var), value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), value); +#endif } -constant_fetch_end: - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -6050,11 +6166,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C SAVE_OPLINE(); if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -6064,11 +6179,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C } else { expr_ptr = EX_CONSTANT(opline->op1); if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CONST == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CONST == IS_CV) { @@ -6099,43 +6214,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CONST != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -6210,6 +6323,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_CONST_HAN ZVAL_UNDEF(&tmp); if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -6380,12 +6496,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON offset = EX_CONSTANT(opline->op2); -isset_dim_obj_again: if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -6400,31 +6517,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -6434,50 +6551,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CONST == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CONST == IS_UNUSED || + (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -6504,7 +6633,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO offset = EX_CONSTANT(opline->op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -6519,10 +6649,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -6576,7 +6705,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -6593,7 +6722,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER if (IS_CONST != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { @@ -6603,11 +6732,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER value = EX_CONSTANT(opline->op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CONST != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -6615,7 +6743,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -6637,11 +6764,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -6662,11 +6792,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -6710,11 +6843,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CONST_CONST_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - EX_CONSTANT(opline->op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + pow_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -6725,12 +6859,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CONST_TMP_HA { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -6745,12 +6880,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CONST_TM { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -6768,7 +6904,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -6785,7 +6921,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z if (IS_CONST != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { @@ -6795,11 +6931,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z value = EX_CONSTANT(opline->op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CONST != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -6807,7 +6942,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -6829,11 +6963,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -6854,11 +6991,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -6902,12 +7042,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CONST_VAR_HA { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -6922,12 +7063,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CONST_VA { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -6956,6 +7098,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -7156,6 +7301,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_VAR_HANDL ZVAL_UNDEF(&tmp); if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -7313,7 +7461,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -7330,7 +7478,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z if (IS_CONST != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { @@ -7340,11 +7488,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z value = EX_CONSTANT(opline->op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CONST != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -7352,7 +7499,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -7374,11 +7520,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -7399,11 +7548,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); zval_ptr_dtor_nogc(free_op2); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -7461,6 +7613,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -7646,7 +7801,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -7715,6 +7869,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C function_name = NULL; if (IS_UNUSED != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -7844,6 +8001,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_ } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + + } #endif } CHECK_EXCEPTION(); @@ -7858,11 +8019,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_U SAVE_OPLINE(); if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -7872,11 +8032,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_U } else { expr_ptr = EX_CONSTANT(opline->op1); if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CONST == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CONST == IS_CV) { @@ -7907,43 +8067,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_U zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_UNUSED != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_UNUSED != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_UNUSED == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -8018,6 +8176,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CONST_UNUSED_HA ZVAL_UNDEF(&tmp); if (IS_CONST != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -8198,7 +8359,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -8215,7 +8376,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE if (IS_CONST != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { @@ -8225,11 +8386,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE value = EX_CONSTANT(opline->op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CONST != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -8237,7 +8397,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -8259,11 +8418,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -8284,11 +8446,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -8467,11 +8632,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CONST_CV_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + fast_div_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8521,11 +8687,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_CV_HANDLER(ZEND_ { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_left_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8536,11 +8703,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_CV_HANDLER(ZEND_ { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_right_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8551,12 +8719,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_CV_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + + do { + if ((IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CONST != IS_CONST && IS_CONST != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -8566,12 +8780,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CONST_CV_HAN { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -8586,12 +8801,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CONST_CV { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_not_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CONST|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -8613,18 +8829,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_CV_HANDLER do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -8656,10 +8872,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_CV_HANDLER } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -8682,18 +8898,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_CV_HAN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -8725,10 +8941,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_CV_HAN } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -8846,12 +9062,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CONST_CV_HANDLE { USE_OPLINE - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + compare_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8862,11 +9078,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CONST_CV_HANDLER(ZE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8877,11 +9094,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CONST_CV_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8892,11 +9110,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CONST_CV_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8907,11 +9126,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CONST_CV_HANDLER { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -8967,7 +9187,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -9011,7 +9230,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_CV_HAND offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -9083,7 +9303,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_CV_HAN offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -9164,7 +9385,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -9189,27 +9409,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CV_HAND SAVE_OPLINE(); op1 = EX_CONSTANT(opline->op1); - op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); if (IS_CONST == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CONST != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CV != IS_CONST) { - zend_string_release(op2_str); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); CHECK_EXCEPTION(); @@ -9230,7 +9482,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV SAVE_OPLINE(); - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { @@ -9241,6 +9493,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -9261,13 +9516,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV if (IS_CONST != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -9314,7 +9572,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_CV call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CONST & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CONST & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -9370,9 +9629,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C } else if (IS_CV != IS_UNUSED) { - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -9577,18 +9839,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CV_HANDLER(ZEN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -9619,10 +9881,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_CV_HANDLER(ZEN } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -9641,11 +9903,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C SAVE_OPLINE(); if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -9655,11 +9916,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C } else { expr_ptr = EX_CONSTANT(opline->op1); if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CONST == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CONST == IS_CV) { @@ -9685,48 +9946,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_C if (IS_CV != IS_UNUSED) { - zval *offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); zend_string *str; zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CV != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -9785,14 +10044,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -isset_dim_obj_again: if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -9807,31 +10067,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -9841,50 +10101,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CONST == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CONST == IS_UNUSED || + (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } - } - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -9911,7 +10183,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -9926,10 +10199,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -9946,7 +10218,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -9963,7 +10235,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE if (IS_CONST != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { @@ -9973,11 +10245,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE value = EX_CONSTANT(opline->op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CONST != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -9985,7 +10256,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -10007,11 +10277,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -10032,11 +10305,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -10080,11 +10356,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CONST_CV_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + pow_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -10230,11 +10507,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CONST_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10284,11 +10562,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CONST_TMPVAR_HANDLER(Z { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10299,11 +10578,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CONST_TMPVAR_HANDLER(Z { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10314,12 +10594,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CONST_TMPVAR_HANDL { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + + do { + if ((IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + ((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CONST != IS_CONST && IS_CONST != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + + } while (0); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -10336,18 +10662,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HAN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -10379,10 +10705,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CONST_TMPVAR_HAN } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -10405,18 +10731,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -10448,10 +10774,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CONST_TMPVAR } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -10569,12 +10895,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CONST_TMPVAR_HA { USE_OPLINE zend_free_op free_op2; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + compare_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10585,11 +10911,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CONST_TMPVAR_HANDLE { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10600,11 +10927,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CONST_TMPVAR_HANDL { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10615,11 +10943,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CONST_TMPVAR_HANDL { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10630,11 +10959,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CONST_TMPVAR_HAN { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10690,7 +11020,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -10734,7 +11063,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CONST_TMPVAR_ offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -10807,7 +11137,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CONST_TMPVAR offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -10889,7 +11220,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ if (IS_CONST == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CONST, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -10914,27 +11244,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_ SAVE_OPLINE(); op1 = EX_CONSTANT(opline->op1); - op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if (IS_CONST == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CONST != IS_CONST) { - zend_string_release(op1_str); - } - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op2_str); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -10966,6 +11328,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_TM break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -10986,13 +11351,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_TM if (IS_CONST != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -11039,7 +11407,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CONST_TM call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CONST & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CONST & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -11099,6 +11468,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -11253,18 +11625,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -11295,10 +11667,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CONST_TMPVAR_HANDLER } while (0); SAVE_OPLINE(); - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -11317,11 +11689,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_T SAVE_OPLINE(); if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_CONST == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -11331,11 +11702,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_T } else { expr_ptr = EX_CONSTANT(opline->op1); if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CONST == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CONST == IS_CV) { @@ -11366,43 +11737,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_T zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } zval_ptr_dtor_nogc(free_op2); } else { @@ -11463,12 +11832,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CON offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -isset_dim_obj_again: if (IS_CONST != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -11483,31 +11853,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -11517,50 +11887,61 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CONST == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CONST == IS_UNUSED || + (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if ((IS_TMP_VAR|IS_VAR) & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, 1); @@ -11588,7 +11969,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CONST == IS_CONST || + (IS_CONST != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -11603,10 +11985,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CO isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op2); @@ -11621,11 +12002,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CONST_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - EX_CONSTANT(opline->op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + pow_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -11642,15 +12024,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_O if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R); - CHECK_EXCEPTION(); - } - - if (!EX(return_value)) { + if (EX(return_value)) { + ZVAL_NULL(EX(return_value)); + } + } else if (!EX(return_value)) { if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_TMP_VAR ) { if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) { SAVE_OPLINE(); zval_dtor_func_for_ptr(Z_COUNTED_P(free_op1)); - CHECK_EXCEPTION(); } } } else { @@ -11716,7 +12097,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER if (IS_TMP_VAR == IS_VAR && UNEXPECTED(retval_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot return string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -11807,6 +12187,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_TMP_HANDLER(ZEND_OP break; } } + if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -11966,8 +12349,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC } } } else { - ZVAL_COPY_VALUE(result, expr); - zval_opt_copy_ctor(result); + ZVAL_COPY(result, expr); convert_to_object(result); } } @@ -12467,7 +12849,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_TMP_HANDLER(ZE SAVE_OPLINE(); value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) { - if (UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { + if (IS_TMP_VAR != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(value); if (UNEXPECTED(ce->name->len != sizeof("__PHP_Incomplete_Class") - 1) || @@ -12498,12 +12880,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HA { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -12518,12 +12901,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONS { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -12553,7 +12937,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CO if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -12597,7 +12980,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_CONST_HAN offset = EX_CONSTANT(opline->op2); - if (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_TMP_VAR == IS_CONST || + (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -12678,7 +13062,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CO if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -12707,11 +13090,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_CONST_HANDLE var = EX_CONSTANT(opline->op2); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = EX_CONSTANT(opline->op2); - rope[opline->extended_value] = zval_get_string(var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CONST == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -12731,11 +13125,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CONST_HANDLE var = EX_CONSTANT(opline->op2); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = EX_CONSTANT(opline->op2); - rope[opline->extended_value] = zval_get_string(var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CONST == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } for (i = 0; i <= opline->extended_value; i++) { len += rope[i]->len; @@ -12761,11 +13166,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CON SAVE_OPLINE(); if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -12775,11 +13179,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CON } else { expr_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_TMP_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_TMP_VAR == IS_CV) { @@ -12810,43 +13214,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CON zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CONST != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -12894,7 +13296,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -12911,7 +13313,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { @@ -12921,11 +13323,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_TMP_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_TMP_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -12933,7 +13334,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -12955,11 +13355,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -12980,11 +13383,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -13028,12 +13434,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -13048,12 +13455,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_ { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -13071,7 +13479,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -13088,7 +13496,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { @@ -13098,11 +13506,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_TMP_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_TMP_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -13110,7 +13517,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -13132,11 +13538,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -13157,11 +13566,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -13205,12 +13617,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_VAR_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -13225,12 +13638,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_VAR_ { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -13248,7 +13662,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -13265,7 +13679,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { @@ -13275,11 +13689,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_TMP_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_TMP_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -13287,7 +13700,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -13309,11 +13721,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -13334,11 +13749,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); zval_ptr_dtor_nogc(free_op2); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -13397,7 +13815,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UN if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -13465,6 +13882,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor_nogc(free_op1); + } #endif } CHECK_EXCEPTION(); @@ -13479,11 +13900,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNU SAVE_OPLINE(); if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -13493,11 +13913,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNU } else { expr_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_TMP_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_TMP_VAR == IS_CV) { @@ -13528,43 +13948,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNU zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_UNUSED != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_UNUSED != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_UNUSED == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -13612,7 +14030,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -13629,7 +14047,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { @@ -13639,11 +14057,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_TMP_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_TMP_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -13651,7 +14068,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -13673,11 +14089,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -13698,11 +14117,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -13746,12 +14168,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CV_HANDL { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -13766,12 +14189,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CV_H { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_TMP_VAR|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -13801,7 +14225,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -13845,7 +14268,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_CV_HANDLE offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_TMP_VAR == IS_CONST || + (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -13926,7 +14350,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -13955,11 +14378,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_CV_HANDLER(Z var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - rope[opline->extended_value] = zval_get_string(var); + var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CV == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -13979,11 +14413,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CV_HANDLER(Z var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - rope[opline->extended_value] = zval_get_string(var); + var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CV == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } for (i = 0; i <= opline->extended_value; i++) { len += rope[i]->len; @@ -14009,11 +14454,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_ SAVE_OPLINE(); if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -14023,11 +14467,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_ } else { expr_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_TMP_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_TMP_VAR == IS_CV) { @@ -14053,48 +14497,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_CV_ if (IS_CV != IS_UNUSED) { - zval *offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); zend_string *str; zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CV != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -14142,7 +14584,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -14159,7 +14601,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { @@ -14169,11 +14611,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_TMP_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_TMP_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -14181,7 +14622,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -14203,11 +14643,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -14228,11 +14671,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -14291,7 +14737,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_TM if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -14335,7 +14780,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_TMP_TMPVAR_HA offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_TMP_VAR == IS_CONST || + (IS_TMP_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -14417,7 +14863,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TM if (IS_TMP_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_TMP_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -14446,11 +14891,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDL var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - rope[opline->extended_value] = zval_get_string(var); - zval_ptr_dtor_nogc(free_op2); - CHECK_EXCEPTION(); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -14470,11 +14926,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDL var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - rope[opline->extended_value] = zval_get_string(var); - zval_ptr_dtor_nogc(free_op2); - CHECK_EXCEPTION(); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + rope[opline->extended_value] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[opline->extended_value] = _zval_get_string_func(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + } } for (i = 0; i <= opline->extended_value; i++) { len += rope[i]->len; @@ -14500,11 +14967,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMP SAVE_OPLINE(); if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = NULL; if (IS_TMP_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -14514,11 +14980,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMP } else { expr_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_TMP_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_TMP_VAR == IS_CV) { @@ -14549,43 +15015,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_TMP zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } zval_ptr_dtor_nogc(free_op2); } else { @@ -14637,7 +15101,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_ if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -14685,7 +15148,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_VAR_HANDLER(ZEND_ if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -14733,7 +15195,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_SPEC_VAR_HANDLER(ZEND if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -14774,7 +15235,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_SPEC_VAR_HANDLER(ZEND if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -14814,15 +15274,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_O if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R); - CHECK_EXCEPTION(); - } - - if (!EX(return_value)) { + if (EX(return_value)) { + ZVAL_NULL(EX(return_value)); + } + } else if (!EX(return_value)) { if (IS_VAR == IS_VAR || IS_VAR == IS_TMP_VAR ) { if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) { SAVE_OPLINE(); zval_dtor_func_for_ptr(Z_COUNTED_P(free_op1)); - CHECK_EXCEPTION(); } } } else { @@ -14888,7 +15347,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER if (IS_VAR == IS_VAR && UNEXPECTED(retval_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot return string offsets by reference"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -14980,6 +15438,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_VAR_HANDLER(ZEND_OP break; } } + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -15089,7 +15550,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND if (IS_VAR == IS_VAR && UNEXPECTED(varptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Only variables can be passed by reference"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -15365,8 +15825,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC } } } else { - ZVAL_COPY_VALUE(result, expr); - zval_opt_copy_ctor(result); + ZVAL_COPY(result, expr); convert_to_object(result); } } @@ -16238,7 +16697,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZE SAVE_OPLINE(); value = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) { - if (UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { + if (IS_VAR != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(value); if (UNEXPECTED(ce->name->len != sizeof("__PHP_Incomplete_Class") - 1) || @@ -16269,12 +16728,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HA { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -16289,12 +16749,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONS { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -16330,7 +16791,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -16360,42 +16820,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -16427,7 +16852,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -16497,7 +16921,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -16741,7 +17164,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CONST_H #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1; @@ -16763,7 +17186,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -16784,51 +17206,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -16839,15 +17237,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1; @@ -16869,7 +17267,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -16888,44 +17285,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -16936,12 +17314,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -16955,7 +17333,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CONST_HAN if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -16979,7 +17356,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_CONST_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -17011,7 +17387,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CO if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -17048,7 +17423,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_CONST if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -17080,7 +17454,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_CONST_HAN offset = EX_CONSTANT(opline->op2); - if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_VAR == IS_CONST || + (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -17152,7 +17527,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CONST_HAN if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -17185,7 +17559,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CONST_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -17226,7 +17599,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CO if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -17262,7 +17634,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CONST if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -17296,7 +17667,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CONST_HAND if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_VAR, property_name, IS_CONST, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -17325,11 +17695,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CONST_HAND zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_CONST == IS_UNUSED) { @@ -17357,53 +17725,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = EX_CONSTANT(opline->op2); + zval *property_name = EX_CONSTANT(opline->op2); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_CONST == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - HANDLE_EXCEPTION(); - } else { - zend_long offset; + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_CONST == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = EX_CONSTANT(opline->op2); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = EX_CONSTANT(opline->op2); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; + } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = EX_CONSTANT(opline->op2); + dim = EX_CONSTANT(opline->op2); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -17486,6 +17859,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V function_name = EX_CONSTANT(opline->op2); if (IS_CONST != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -17579,7 +17955,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_ SAVE_OPLINE(); if (IS_VAR == IS_UNUSED) { zend_constant *c; - zval *retval; if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); @@ -17605,67 +17980,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_ } else { CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c); } - retval = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(retval, &c->value); - if (Z_OPT_COPYABLE_P(retval) || Z_OPT_REFCOUNTED_P(retval)) { - if (Z_OPT_COPYABLE_P(retval)) { - zval_copy_ctor_func(retval); - } else { - Z_ADDREF_P(retval); - } +#ifdef ZTS + if (c->flags & CONST_PERSISTENT) { + ZVAL_DUP(EX_VAR(opline->result.var), &c->value); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); +#endif } else { /* class constant */ zend_class_entry *ce; zval *value; - if (IS_VAR == IS_CONST) { - if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { - value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; - } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { - ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); - } else { - ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + do { + if (IS_VAR == IS_CONST) { + if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { + value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); + ZVAL_DEREF(value); +#ifdef ZTS + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); +#endif + break; + } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); + } else { + ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); + HANDLE_EXCEPTION(); + } + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - if (UNEXPECTED(ce == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); - HANDLE_EXCEPTION(); + } else { + ce = Z_CE_P(EX_VAR(opline->op1.var)); + if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { + ZVAL_DEREF(value); + break; } - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); - if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; - } - } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); - if (Z_CONSTANT_P(value)) { - EG(scope) = ce; - zval_update_constant_ex(value, 1, NULL); - EG(scope) = EX(func)->op_array.scope; - } - if (IS_VAR == IS_CONST) { - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + ZVAL_DEREF(value); + if (Z_CONSTANT_P(value)) { + EG(scope) = ce; + zval_update_constant_ex(value, 1, NULL); + EG(scope) = EX(func)->op_array.scope; + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + if (IS_VAR == IS_CONST) { + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + } else { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + } } else { - CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); } + } while (0); +#ifdef ZTS + if (ce->type == ZEND_INTERNAL_CLASS) { ZVAL_DUP(EX_VAR(opline->result.var), value); } else { - zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + ZVAL_COPY(EX_VAR(opline->result.var), value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), value); +#endif } -constant_fetch_end: - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -17677,11 +18065,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CON SAVE_OPLINE(); if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -17691,11 +18078,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CON } else { expr_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_VAR == IS_CV) { @@ -17726,43 +18113,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CON zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CONST != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -17810,6 +18195,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CONST_HANDL zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -17821,72 +18207,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CONST_HANDL if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); -unset_dim_again: - if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -17910,7 +18304,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CONST_HANDL if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); @@ -17945,7 +18338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -17962,7 +18355,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { @@ -17972,11 +18365,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -17984,7 +18376,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -18007,11 +18398,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); zval_ptr_dtor_nogc(free_op1); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -18032,11 +18426,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -18085,12 +18482,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -18105,12 +18503,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_ { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -18157,7 +18556,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -18174,7 +18573,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { @@ -18184,11 +18583,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -18196,7 +18594,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -18219,11 +18616,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); zval_ptr_dtor_nogc(free_op1); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -18244,11 +18644,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -18292,12 +18695,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -18312,12 +18716,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_ { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -18369,7 +18774,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_VAR_HANDLE if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - if (free_op2) {zval_ptr_dtor_nogc(free_op2);}; zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); HANDLE_EXCEPTION(); } @@ -18392,7 +18796,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_VAR_HANDLE if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); if (free_op2) {zval_ptr_dtor_nogc(free_op2);}; - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } if (IS_VAR == IS_VAR && @@ -18428,7 +18831,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -18445,7 +18848,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { @@ -18455,11 +18858,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -18467,7 +18869,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -18490,11 +18891,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); zval_ptr_dtor_nogc(free_op1); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -18515,11 +18919,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); zval_ptr_dtor_nogc(free_op2); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -18578,7 +18985,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -18865,7 +19271,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_UNUSED_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -18889,7 +19294,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_UNUSED_H if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -18921,7 +19325,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UN if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -18963,11 +19366,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_UNUSED_HAN zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_UNUSED == IS_UNUSED) { @@ -18995,53 +19396,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = NULL; + zval *property_name = NULL; - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_UNUSED == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - HANDLE_EXCEPTION(); - } else { - zend_long offset; + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_UNUSED == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = NULL; - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = NULL; + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; + } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = NULL; + dim = NULL; - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -19095,6 +19501,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V function_name = NULL; if (IS_UNUSED != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -19224,6 +19633,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor_nogc(free_op1); + } #endif } CHECK_EXCEPTION(); @@ -19238,11 +19651,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNU SAVE_OPLINE(); if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -19252,11 +19664,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNU } else { expr_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_VAR == IS_CV) { @@ -19287,43 +19699,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_UNU zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_UNUSED != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_UNUSED != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_UNUSED == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -19388,7 +19798,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -19405,7 +19815,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { @@ -19415,11 +19825,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -19427,7 +19836,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -19450,11 +19858,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); zval_ptr_dtor_nogc(free_op1); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -19475,11 +19886,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -19523,12 +19937,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CV_HANDL { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -19543,12 +19958,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CV_H { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, (IS_VAR|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -19584,7 +20000,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -19614,42 +20029,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -19681,7 +20061,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -19751,7 +20130,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -19995,7 +20373,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_CV_HAND #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1; @@ -20017,7 +20395,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -20038,51 +20415,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -20093,15 +20446,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1; @@ -20123,7 +20476,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -20142,44 +20494,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -20190,12 +20523,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -20209,7 +20542,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_CV_HANDLE if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -20233,7 +20565,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_CV_HANDL if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -20265,7 +20596,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -20302,7 +20632,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_CV_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -20334,7 +20663,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_CV_HANDLE offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_VAR == IS_CONST || + (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -20406,7 +20736,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLE if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -20439,7 +20768,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDL if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -20480,7 +20808,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -20516,7 +20843,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -20550,7 +20876,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_CV_HANDLER if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_VAR, property_name, IS_CV, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -20579,11 +20904,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_CV_HANDLER zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_CV == IS_UNUSED) { @@ -20611,53 +20934,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_CV == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - HANDLE_EXCEPTION(); - } else { - zend_long offset; + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_CV == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; + } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -20708,7 +21036,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); HANDLE_EXCEPTION(); } @@ -20731,7 +21058,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_VAR_CV_HANDLER if (IS_VAR == IS_VAR && UNEXPECTED(variable_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } if (IS_VAR == IS_VAR && @@ -20800,9 +21126,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V } else if (IS_CV != IS_UNUSED) { - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -20897,11 +21226,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_ SAVE_OPLINE(); if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -20911,11 +21239,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_ } else { expr_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_VAR == IS_CV) { @@ -20941,48 +21269,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CV_ if (IS_CV != IS_UNUSED) { - zval *offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); zend_string *str; zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CV != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -21030,6 +21356,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER( zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -21041,72 +21368,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_CV_HANDLER( if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -unset_dim_again: - if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -21130,7 +21465,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_CV_HANDLER( if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); @@ -21165,7 +21499,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); @@ -21182,7 +21516,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { @@ -21192,11 +21526,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_VAR != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_VAR != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -21204,7 +21537,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - zval_ptr_dtor_nogc(free_op1); HANDLE_EXCEPTION(); } @@ -21227,11 +21559,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); zval_ptr_dtor_nogc(free_op1); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -21252,11 +21587,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -21326,7 +21664,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -21356,42 +21693,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -21423,7 +21725,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -21494,7 +21795,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_V if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -21739,7 +22039,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_VAR_TMPVAR_ #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1, free_op2; @@ -21761,7 +22061,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -21782,51 +22081,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -21838,15 +22113,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_VAR_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op1, free_op2; @@ -21868,7 +22143,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -21887,44 +22161,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -21936,12 +22191,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_VAR_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -21955,7 +22210,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_VAR_TMPVAR_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -21979,7 +22233,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_VAR_TMPVAR_H if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -22011,7 +22264,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TM if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -22048,7 +22300,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_VAR_TMPVA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -22080,7 +22331,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_VAR_TMPVAR_HA offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_VAR == IS_CONST || + (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -22153,7 +22405,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } @@ -22186,7 +22437,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_H if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -22227,7 +22477,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TM if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -22263,7 +22512,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVA if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -22297,7 +22545,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_VAR_TMPVAR_HAN if (IS_VAR == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(free_op2); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_VAR, property_name, (IS_TMP_VAR|IS_VAR), (opline+1)->op1_type, (opline+1)->op1, execute_data, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -22326,11 +22573,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_VAR_TMPVAR_HAN zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { @@ -22358,53 +22603,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zend_free_op free_op2; - zval *property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_free_op free_op2; + zval *property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - zval_ptr_dtor_nogc(free_op2); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - HANDLE_EXCEPTION(); - } else { - zend_long offset; + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zval_ptr_dtor_nogc(free_op2); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - zval_ptr_dtor_nogc(free_op2); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + zval_ptr_dtor_nogc(free_op2); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_VAR == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; + } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zval_ptr_dtor_nogc(free_op2); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + zval_ptr_dtor_nogc(free_op2); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -22458,6 +22708,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V function_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { if (UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -22552,11 +22805,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMP SAVE_OPLINE(); if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -22566,11 +22818,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMP } else { expr_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_VAR == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_VAR == IS_CV) { @@ -22601,43 +22853,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_TMP zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } zval_ptr_dtor_nogc(free_op2); } else { @@ -22685,6 +22935,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HAND zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); @@ -22696,72 +22947,81 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_VAR_TMPVAR_HAND if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -unset_dim_again: - if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_VAR != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_VAR != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_VAR == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_VAR != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); + zval_ptr_dtor_nogc(free_op2); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -22785,7 +23045,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_VAR_TMPVAR_HAND if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); @@ -22844,7 +23103,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { break; } - } + } + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(obj, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -22954,7 +23216,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -22984,42 +23245,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -23051,7 +23277,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -23327,7 +23552,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CONS #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -23349,7 +23574,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -23370,51 +23594,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -23425,15 +23625,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -23455,7 +23655,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -23474,44 +23673,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -23522,12 +23702,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -23549,7 +23729,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CONST_ offset = EX_CONSTANT(opline->op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -23621,7 +23802,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_ if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } @@ -23654,7 +23834,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -23686,7 +23865,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CONST offset = EX_CONSTANT(opline->op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -23767,7 +23947,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -23803,7 +23982,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CO if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -23837,7 +24015,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_H if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_UNUSED, property_name, IS_CONST, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -23862,11 +24039,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_CONST_HA var = EX_CONSTANT(opline->op2); rope[0] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = EX_CONSTANT(opline->op2); - rope[0] = zval_get_string(var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CONST == IS_CV) { + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + rope[0] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[0] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -23896,6 +24084,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -23916,13 +24107,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C if (IS_UNUSED != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -23969,7 +24163,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -23991,7 +24186,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CON SAVE_OPLINE(); if (IS_UNUSED == IS_UNUSED) { zend_constant *c; - zval *retval; if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { c = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); @@ -24017,67 +24211,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CON } else { CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), c); } - retval = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(retval, &c->value); - if (Z_OPT_COPYABLE_P(retval) || Z_OPT_REFCOUNTED_P(retval)) { - if (Z_OPT_COPYABLE_P(retval)) { - zval_copy_ctor_func(retval); - } else { - Z_ADDREF_P(retval); - } +#ifdef ZTS + if (c->flags & CONST_PERSISTENT) { + ZVAL_DUP(EX_VAR(opline->result.var), &c->value); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), &c->value); +#endif } else { /* class constant */ zend_class_entry *ce; zval *value; - if (IS_UNUSED == IS_CONST) { - if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { - value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; - } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { - ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); - } else { - ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + do { + if (IS_UNUSED == IS_CONST) { + if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)))) { + value = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2))); + ZVAL_DEREF(value); +#ifdef ZTS + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); +#endif + break; + } else if (CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)))) { + ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1))); + } else { + ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + if (UNEXPECTED(ce == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); + HANDLE_EXCEPTION(); + } + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - if (UNEXPECTED(ce == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Class '%s' not found", Z_STRVAL_P(EX_CONSTANT(opline->op1))); - HANDLE_EXCEPTION(); + } else { + ce = Z_CE_P(EX_VAR(opline->op1.var)); + if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { + ZVAL_DEREF(value); + break; } - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce); } - } else { - ce = Z_CE_P(EX_VAR(opline->op1.var)); - if ((value = CACHED_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce)) != NULL) { - ZVAL_DEREF(value); - ZVAL_DUP(EX_VAR(opline->result.var), value); - goto constant_fetch_end; - } - } - if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { - ZVAL_DEREF(value); - if (Z_CONSTANT_P(value)) { - EG(scope) = ce; - zval_update_constant_ex(value, 1, NULL); - EG(scope) = EX(func)->op_array.scope; - } - if (IS_UNUSED == IS_CONST) { - CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + if (EXPECTED((value = zend_hash_find(&ce->constants_table, Z_STR_P(EX_CONSTANT(opline->op2)))) != NULL)) { + ZVAL_DEREF(value); + if (Z_CONSTANT_P(value)) { + EG(scope) = ce; + zval_update_constant_ex(value, 1, NULL); + EG(scope) = EX(func)->op_array.scope; + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + if (IS_UNUSED == IS_CONST) { + CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), value); + } else { + CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + } } else { - CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op2)), ce, value); + zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + HANDLE_EXCEPTION(); } + } while (0); +#ifdef ZTS + if (ce->type == ZEND_INTERNAL_CLASS) { ZVAL_DUP(EX_VAR(opline->result.var), value); } else { - zend_error(E_EXCEPTION | E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(EX_CONSTANT(opline->op2))); + ZVAL_COPY(EX_VAR(opline->result.var), value); } +#else + ZVAL_COPY(EX_VAR(opline->result.var), value); +#endif } -constant_fetch_end: - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -24119,6 +24326,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CONST_HA zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_obj_zval_ptr_unused(execute_data); @@ -24130,72 +24338,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CONST_HA if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); -unset_dim_again: - if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); CHECK_EXCEPTION(); @@ -24219,7 +24435,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CONST_HA if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); @@ -24267,12 +24482,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNU offset = EX_CONSTANT(opline->op2); -isset_dim_obj_again: if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -24287,31 +24503,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -24321,50 +24537,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_UNUSED == IS_UNUSED || + (IS_UNUSED != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -24391,7 +24619,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN offset = EX_CONSTANT(opline->op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -24406,10 +24635,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -24426,7 +24654,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -24443,7 +24671,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE if (IS_UNUSED != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { @@ -24453,11 +24681,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE value = NULL; ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_UNUSED != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_UNUSED != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -24465,7 +24692,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -24487,11 +24713,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_UNUSED == IS_CV || IS_UNUSED == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -24512,11 +24741,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -24563,7 +24795,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -24580,7 +24812,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( if (IS_UNUSED != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { @@ -24590,11 +24822,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( value = NULL; ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_UNUSED != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_UNUSED != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -24602,7 +24833,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -24624,11 +24854,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_UNUSED == IS_CV || IS_UNUSED == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -24649,11 +24882,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -24700,7 +24936,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -24717,7 +24953,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( if (IS_UNUSED != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { @@ -24727,11 +24963,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( value = NULL; ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_UNUSED != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_UNUSED != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -24739,7 +24974,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -24761,11 +24995,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_UNUSED == IS_CV || IS_UNUSED == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -24786,11 +25023,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER( /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); zval_ptr_dtor_nogc(free_op2); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -24849,7 +25089,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -25168,6 +25407,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + + } #endif } CHECK_EXCEPTION(); @@ -25212,7 +25455,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -25229,7 +25472,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL if (IS_UNUSED != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { @@ -25239,11 +25482,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL value = NULL; ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_UNUSED != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_UNUSED != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -25251,7 +25493,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -25273,11 +25514,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_UNUSED == IS_CV || IS_UNUSED == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -25298,11 +25542,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -25367,7 +25614,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -25397,42 +25643,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -25464,7 +25675,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -25740,7 +25950,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_CV_H #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -25762,7 +25972,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -25783,51 +25992,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -25838,15 +26023,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -25868,7 +26053,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -25887,44 +26071,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -25935,12 +26100,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -25962,7 +26127,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_CV_HAN offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -26034,7 +26200,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HAN if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } @@ -26067,7 +26232,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HA if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -26099,7 +26263,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_CV_HA offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -26180,7 +26345,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -26216,7 +26380,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -26250,7 +26413,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_HAND if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_UNUSED, property_name, IS_CV, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -26275,11 +26437,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_CV_HANDL var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); rope[0] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); - var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - rope[0] = zval_get_string(var); + var = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if (IS_CV == IS_CV) { + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + rope[0] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[0] = _zval_get_string_func(var); - CHECK_EXCEPTION(); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -26298,7 +26471,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C SAVE_OPLINE(); - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { @@ -26309,6 +26482,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -26329,13 +26505,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C if (IS_UNUSED != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -26382,7 +26561,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -26435,6 +26615,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CV_HANDL zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_obj_zval_ptr_unused(execute_data); @@ -26446,72 +26627,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_CV_HANDL if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -unset_dim_again: - if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); CHECK_EXCEPTION(); @@ -26535,7 +26724,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_CV_HANDL if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); @@ -26581,14 +26769,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNU HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -isset_dim_obj_again: if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -26603,31 +26792,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -26637,50 +26826,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_UNUSED == IS_UNUSED || + (IS_UNUSED != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } - } - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -26707,7 +26908,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -26722,10 +26924,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -26742,7 +26943,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -26759,7 +26960,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z if (IS_UNUSED != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { @@ -26769,11 +26970,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z value = NULL; ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_UNUSED != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_UNUSED != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = NULL; @@ -26781,7 +26981,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -26803,11 +27002,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_UNUSED == IS_CV || IS_UNUSED == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -26828,11 +27030,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -26897,7 +27102,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -26927,42 +27131,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -26994,7 +27163,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -27271,7 +27439,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_UNUSED_TMPV #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op2; @@ -27293,7 +27461,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -27314,51 +27481,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -27370,15 +27513,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_UNUSED_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op2; @@ -27400,7 +27543,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -27419,44 +27561,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -27468,12 +27591,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_UNUSED_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -27495,7 +27618,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_UNUSED_TMPVAR offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -27568,7 +27692,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -27601,7 +27724,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVA if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -27633,7 +27755,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_UNUSED_TMPVA offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -27715,7 +27838,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -27751,7 +27873,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TM if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -27785,7 +27906,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ if (IS_UNUSED == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_UNUSED, property_name, (IS_TMP_VAR|IS_VAR), (opline+1)->op1_type, (opline+1)->op1, execute_data, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -27810,11 +27930,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_H var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); rope[0] = zend_string_copy(Z_STR_P(var)); } else { - SAVE_OPLINE(); var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - rope[0] = zval_get_string(var); - zval_ptr_dtor_nogc(free_op2); - CHECK_EXCEPTION(); + if (EXPECTED(Z_TYPE_P(var) == IS_STRING)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + rope[0] = Z_STR_P(var); + } + } else { + SAVE_OPLINE(); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(var) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(var, BP_VAR_R); + } + rope[0] = _zval_get_string_func(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + } } ZEND_VM_NEXT_OPCODE(); } @@ -27844,6 +27975,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -27864,13 +27998,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T if (IS_UNUSED != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_UNUSED == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -27917,7 +28054,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_UNUSED & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -27971,6 +28109,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_TMPVAR_H zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_obj_zval_ptr_unused(execute_data); @@ -27982,72 +28121,81 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_UNUSED_TMPVAR_H if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -unset_dim_again: - if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_UNUSED != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); + zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -28071,7 +28219,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_UNUSED_TMPVAR_H if (IS_UNUSED == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); @@ -28120,12 +28267,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_UNU offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -isset_dim_obj_again: if (IS_UNUSED != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -28140,31 +28288,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -28174,50 +28322,61 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_UNUSED == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_UNUSED == IS_UNUSED || + (IS_UNUSED != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if ((IS_TMP_VAR|IS_VAR) & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, 1); @@ -28245,7 +28404,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_UNUSED == IS_CONST || + (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -28260,10 +28420,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UN isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op2); @@ -28323,7 +28482,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_SPEC_CV_HANDLER(ZEND_O if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -28370,7 +28528,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_SPEC_CV_HANDLER(ZEND_O if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -28417,7 +28574,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_SPEC_CV_HANDLER(ZEND_ if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -28457,7 +28613,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_SPEC_CV_HANDLER(ZEND_ if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { SAVE_OPLINE(); zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -28493,7 +28648,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ECHO_SPEC_CV_HANDLER(ZEND_OPCO zval *z; SAVE_OPLINE(); - z = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (Z_TYPE_P(z) == IS_STRING) { zend_string *str = Z_STR_P(z); @@ -28506,6 +28661,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ECHO_SPEC_CV_HANDLER(ZEND_OPCO if (str->len != 0) { zend_write(str->val, str->len); + } else if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(z) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(z, BP_VAR_R); } zend_string_release(str); } @@ -28723,15 +28880,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OP if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) { SAVE_OPLINE(); retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R); - CHECK_EXCEPTION(); - } - - if (!EX(return_value)) { + if (EX(return_value)) { + ZVAL_NULL(EX(return_value)); + } + } else if (!EX(return_value)) { if (IS_CV == IS_VAR || IS_CV == IS_TMP_VAR ) { if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) { SAVE_OPLINE(); zval_dtor_func_for_ptr(Z_COUNTED_P(free_op1)); - CHECK_EXCEPTION(); } } } else { @@ -28797,7 +28953,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER( if (IS_CV == IS_VAR && UNEXPECTED(retval_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot return string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -28878,7 +29033,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_CV_HANDLER(ZEND_OPC SAVE_OPLINE(); - value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + value = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); do { if (IS_CV == IS_CONST || UNEXPECTED(Z_TYPE_P(value) != IS_OBJECT)) { @@ -28888,6 +29043,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_THROW_SPEC_CV_HANDLER(ZEND_OPC break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -28997,7 +29155,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_ if (IS_CV == IS_VAR && UNEXPECTED(varptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Only variables can be passed by reference"); - HANDLE_EXCEPTION(); } @@ -29157,7 +29314,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC zend_object_clone_obj_t clone_call; SAVE_OPLINE(); - obj = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + obj = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(obj) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -29172,7 +29329,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { break; } - } + } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(obj, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -29318,8 +29478,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO } } } else { - ZVAL_COPY_VALUE(result, expr); - zval_opt_copy_ctor(result); + ZVAL_COPY(result, expr); convert_to_object(result); } } @@ -29339,10 +29498,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE zend_bool failure_retval=0; SAVE_OPLINE(); - inc_filename = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + inc_filename = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); ZVAL_UNDEF(&tmp_inc_filename); if (Z_TYPE_P(inc_filename) != IS_STRING) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(inc_filename) == IS_UNDEF)) { + inc_filename = GET_OP1_UNDEF_CV(inc_filename, BP_VAR_R); + } ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); inc_filename = &tmp_inc_filename; } @@ -29958,13 +30120,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP SAVE_OPLINE(); - value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + value = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); try_strlen: if (EXPECTED(Z_TYPE_P(value) == IS_STRING)) { ZVAL_LONG(EX_VAR(opline->result.var), Z_STRLEN_P(value)); } else { zend_bool strict; + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + value = GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; @@ -30002,7 +30167,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_CV_HANDLER(ZEN SAVE_OPLINE(); value = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); if (EXPECTED(Z_TYPE_P(value) == opline->extended_value)) { - if (UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { + if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(value) == IS_OBJECT)) { zend_class_entry *ce = Z_OBJCE_P(value); if (UNEXPECTED(ce->name->len != sizeof("__PHP_Incomplete_Class") - 1) || @@ -30168,11 +30333,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CONST_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30222,11 +30388,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CV_CONST_HANDLER(ZEND_ { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30237,11 +30404,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CV_CONST_HANDLER(ZEND_ { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30252,12 +30420,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CONST_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + + do { + if ((IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CV != IS_CONST && IS_CV != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -30267,12 +30481,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CONST_HAN { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -30287,12 +30502,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CONST { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + result = fast_is_not_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_CONST) & (IS_VAR|IS_TMP_VAR)); @@ -30314,18 +30530,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -30357,10 +30573,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -30383,18 +30599,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HAN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -30426,10 +30642,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HAN } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -30547,12 +30763,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_CONST_HANDLE { USE_OPLINE - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + compare_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30563,11 +30779,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CV_CONST_HANDLER(ZE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30578,11 +30795,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CV_CONST_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30593,11 +30811,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CV_CONST_HANDLER(Z { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30608,11 +30827,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CV_CONST_HANDLER { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -30644,7 +30864,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -30674,42 +30893,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -30741,7 +30925,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -30811,7 +30994,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -31055,7 +31237,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CONST_HA #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -31077,7 +31259,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -31098,51 +31279,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -31153,15 +31310,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_CONST(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_CONST(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -31183,7 +31340,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -31202,44 +31358,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -31250,12 +31387,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_CONST(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_CONST(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_CONST(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_CONST(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_CONST(int type ZEND_OPCODE_HANDLER_ARGS_DC) @@ -31268,7 +31405,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ HashTable *target_symbol_table; SAVE_OPLINE(); - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { name = Z_STR_P(varname); @@ -31276,6 +31413,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -31468,7 +31608,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_CONST_HAND if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -31492,7 +31631,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_CONST_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -31539,7 +31677,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CON if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -31576,7 +31713,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_CONST_ if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, EX_CONSTANT(opline->op2), IS_CONST); @@ -31608,7 +31744,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CONST_HAND offset = EX_CONSTANT(opline->op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -31680,7 +31817,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HAND if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } @@ -31713,7 +31849,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -31745,7 +31880,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CONST_HAN offset = EX_CONSTANT(opline->op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -31826,7 +31962,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CON if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -31862,7 +31997,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_ if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -31882,7 +32016,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_SPEC_CV_CONST_HANDL zval *container; SAVE_OPLINE(); - container = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + container = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); try_fetch_list: if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { @@ -31894,7 +32028,8 @@ try_fetch_list: } else { ZVAL_COPY(EX_VAR(opline->result.var), value); } - } else if (UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && + } else if (IS_CV != IS_CONST && + UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && EXPECTED(Z_OBJ_HT_P(container)->read_dimension)) { zval *result = EX_VAR(opline->result.var); zval *retval = Z_OBJ_HT_P(container)->read_dimension(container, EX_CONSTANT(opline->op2), BP_VAR_R, result); @@ -31906,10 +32041,13 @@ try_fetch_list: } else { ZVAL_NULL(result); } - } else if (Z_TYPE_P(container) == IS_REFERENCE) { + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_TYPE_P(container) == IS_REFERENCE) { container = Z_REFVAL_P(container); goto try_fetch_list; } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(container, BP_VAR_R); + } ZVAL_NULL(EX_VAR(opline->result.var)); } CHECK_EXCEPTION(); @@ -31937,7 +32075,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CONST_HANDL if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_CV, property_name, IS_CONST, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -31966,11 +32103,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CONST_HANDL zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_CONST == IS_UNUSED) { @@ -31998,53 +32133,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = EX_CONSTANT(opline->op2); + zval *property_name = EX_CONSTANT(opline->op2); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_CONST == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_CONST == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); - } else { - zend_long offset; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = EX_CONSTANT(opline->op2); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = EX_CONSTANT(opline->op2); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; + } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = EX_CONSTANT(opline->op2); + dim = EX_CONSTANT(opline->op2); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } @@ -32091,28 +32231,60 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CONST_HAND zend_string *op1_str, *op2_str, *str; SAVE_OPLINE(); - op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - op2 = EX_CONSTANT(opline->op2); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = EX_CONSTANT(opline->op2); if (IS_CONST == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CV != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CONST != IS_CONST) { - zend_string_release(op2_str); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); CHECK_EXCEPTION(); @@ -32144,6 +32316,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -32154,7 +32329,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST } while (0); } - object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -32164,13 +32339,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST if (IS_CV != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CV == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -32217,7 +32395,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CV & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CV & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -32243,18 +32422,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_CONST_HANDLER(ZEN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -32285,10 +32464,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_CONST_HANDLER(ZEN } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -32307,11 +32486,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONS SAVE_OPLINE(); if ((IS_CV == IS_VAR || IS_CV == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); if (IS_CV == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -32321,11 +32499,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONS } else { expr_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CV == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CV == IS_CV) { @@ -32356,43 +32534,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CONS zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CONST != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -32463,10 +32639,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_CONST_HANDLE ZEND_VM_NEXT_OPCODE(); } - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); ZVAL_UNDEF(&tmp); if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -32519,6 +32698,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLE zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var); @@ -32530,72 +32710,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CONST_HANDLE if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); -unset_dim_again: - if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CONST != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); CHECK_EXCEPTION(); @@ -32619,7 +32807,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CONST_HANDLE if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = EX_CONSTANT(opline->op2); @@ -32772,12 +32959,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_ offset = EX_CONSTANT(opline->op2); -isset_dim_obj_again: if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -32792,31 +32980,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -32826,50 +33014,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CV == IS_UNUSED || + (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -32896,7 +33096,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV offset = EX_CONSTANT(opline->op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -32911,10 +33112,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -32932,7 +33132,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_CONST_HANDL zend_bool result; SAVE_OPLINE(); - expr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + expr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); try_instanceof: if (Z_TYPE_P(expr) == IS_OBJECT) { @@ -32959,6 +33159,9 @@ try_instanceof: expr = Z_REFVAL_P(expr); goto try_instanceof; } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(expr) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(expr, BP_VAR_R); + } result = 0; } @@ -32975,7 +33178,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -32992,7 +33195,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE if (IS_CV != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { @@ -33002,11 +33205,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CV != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CV != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); @@ -33014,7 +33216,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -33036,11 +33237,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -33061,11 +33265,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE /* Consts, temporary variables and references need copying */ if (IS_CONST == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CONST == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -33109,11 +33316,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CV_CONST_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + pow_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -33210,12 +33418,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_TMP_HANDL { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -33230,12 +33439,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_TMP_H { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_TMP_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -33282,7 +33492,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -33299,7 +33509,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND if (IS_CV != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { @@ -33309,11 +33519,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CV != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CV != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); @@ -33321,7 +33530,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -33343,11 +33551,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -33368,11 +33579,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_TMP_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_TMP_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_TMP_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -33416,12 +33630,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_VAR_HANDL { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -33436,12 +33651,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_VAR_H { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var_deref(opline->op2.var, execute_data, &free_op2); + result = fast_is_not_identical_function(op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_VAR) & (IS_VAR|IS_TMP_VAR)); @@ -33462,7 +33678,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ HashTable *target_symbol_table; SAVE_OPLINE(); - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { name = Z_STR_P(varname); @@ -33470,6 +33686,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -33677,7 +33896,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - if (free_op2) {zval_ptr_dtor_nogc(free_op2);}; HANDLE_EXCEPTION(); } @@ -33700,7 +33918,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_VAR_HANDLER if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); if (free_op2) {zval_ptr_dtor_nogc(free_op2);}; - HANDLE_EXCEPTION(); } if (IS_CV == IS_VAR && @@ -33758,10 +33975,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_VAR_HANDLER( ZEND_VM_NEXT_OPCODE(); } - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); ZVAL_UNDEF(&tmp); if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -33920,7 +34140,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_VAR_HANDLER zend_bool result; SAVE_OPLINE(); - expr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + expr = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); try_instanceof: if (Z_TYPE_P(expr) == IS_OBJECT) { @@ -33947,6 +34167,9 @@ try_instanceof: expr = Z_REFVAL_P(expr); goto try_instanceof; } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(expr) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(expr, BP_VAR_R); + } result = 0; } @@ -33963,7 +34186,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -33980,7 +34203,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND if (IS_CV != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { @@ -33990,11 +34213,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CV != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CV != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); @@ -34002,7 +34224,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -34024,11 +34245,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -34049,11 +34273,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND /* Consts, temporary variables and references need copying */ if (IS_VAR == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_VAR == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_VAR & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); zval_ptr_dtor_nogc(free_op2); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -34112,7 +34339,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -34398,7 +34624,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ HashTable *target_symbol_table; SAVE_OPLINE(); - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { name = Z_STR_P(varname); @@ -34406,6 +34632,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -34583,7 +34812,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_UNUSED_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -34607,7 +34835,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_UNUSED_HA if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -34639,7 +34866,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNU if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, NULL, IS_UNUSED); @@ -34681,11 +34907,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_UNUSED_HAND zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_UNUSED == IS_UNUSED) { @@ -34713,53 +34937,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = NULL; + zval *property_name = NULL; - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_UNUSED == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_UNUSED == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); - } else { - zend_long offset; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = NULL; - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = NULL; + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; + } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = NULL; + dim = NULL; - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } @@ -34812,6 +35041,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU } } zend_verify_return_type(EX(func), retval_ptr); + + if (UNEXPECTED(EG(exception) != NULL)) { + + } #endif } CHECK_EXCEPTION(); @@ -34826,11 +35059,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUS SAVE_OPLINE(); if ((IS_CV == IS_VAR || IS_CV == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); if (IS_CV == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -34840,11 +35072,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUS } else { expr_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CV == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CV == IS_CV) { @@ -34875,43 +35107,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUS zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_UNUSED != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_UNUSED != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_UNUSED == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -34982,10 +35212,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_CV_UNUSED_HANDL ZEND_VM_NEXT_OPCODE(); } - varname = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + varname = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); ZVAL_UNDEF(&tmp); if (IS_CV != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -35143,7 +35376,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -35160,7 +35393,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z if (IS_CV != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { @@ -35170,11 +35403,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CV != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CV != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); @@ -35182,7 +35414,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -35204,11 +35435,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -35229,11 +35463,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z /* Consts, temporary variables and references need copying */ if (IS_UNUSED == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_UNUSED == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_UNUSED == IS_VAR || IS_UNUSED == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_UNUSED & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -35412,11 +35649,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_CV_HANDLER(ZEND_OP { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + fast_div_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35466,11 +35704,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CV_CV_HANDLER(ZEND_OPC { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_left_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35481,11 +35720,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CV_CV_HANDLER(ZEND_OPC { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_right_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35496,12 +35736,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_CV_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + + do { + if ((IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CV != IS_CONST && IS_CV != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -35511,12 +35797,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_CV_CV_HANDLE { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -35531,12 +35818,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_CV_CV_HA { USE_OPLINE + zval *op1, *op2; int result; SAVE_OPLINE(); - result = fast_is_not_identical_function( - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op2.var); + result = fast_is_not_identical_function(op1, op2); ZEND_VM_SMART_BRANCH(result, (IS_CV|IS_CV) & (IS_VAR|IS_TMP_VAR)); @@ -35558,18 +35846,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -35601,10 +35889,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -35627,18 +35915,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -35670,10 +35958,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLE } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -35791,12 +36079,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_CV_HANDLER(Z { USE_OPLINE - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + compare_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35807,11 +36095,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CV_CV_HANDLER(ZEND_ { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35822,11 +36111,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CV_CV_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35837,11 +36127,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CV_CV_HANDLER(ZEND { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35852,11 +36143,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CV_CV_HANDLER(ZE { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -35888,7 +36180,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -35918,42 +36209,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -35985,7 +36241,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } @@ -36055,7 +36310,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -36299,7 +36553,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_CV_HANDL #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -36321,7 +36575,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -36342,51 +36595,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -36397,15 +36626,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_CV(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_CV(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE @@ -36427,7 +36656,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); - HANDLE_EXCEPTION(); } @@ -36446,44 +36674,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -36494,12 +36703,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_CV(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_CV(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_CV(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_CV(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -36528,7 +36737,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_CV_HANDLER if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -36552,7 +36760,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_CV_HANDLE if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -36599,7 +36806,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_ if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -36636,7 +36842,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_CV_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var), IS_CV); @@ -36668,7 +36873,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_CV_HANDLER offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -36740,7 +36946,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } @@ -36773,7 +36978,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLE if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -36805,7 +37009,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_CV_HANDLE offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -36886,7 +37091,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_ if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -36922,7 +37126,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -36956,7 +37159,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_CV_HANDLER( if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_CV, property_name, IS_CV, (opline+1)->op1_type, (opline+1)->op1, execute_data, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -36985,11 +37187,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_CV_HANDLER( zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if (IS_CV == IS_UNUSED) { @@ -37017,53 +37217,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *property_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if (IS_CV == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if (IS_CV == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); - } else { - zend_long offset; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; + } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } @@ -37115,7 +37320,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER( if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - HANDLE_EXCEPTION(); } if (IS_CV == IS_VAR && @@ -37137,7 +37341,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER( if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets nor overloaded objects"); - HANDLE_EXCEPTION(); } if (IS_CV == IS_VAR && @@ -37172,28 +37375,60 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CV_HANDLER zend_string *op1_str, *op2_str, *str; SAVE_OPLINE(); - op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CV != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CV != IS_CONST) { - zend_string_release(op2_str); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); CHECK_EXCEPTION(); @@ -37214,7 +37449,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA SAVE_OPLINE(); - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { @@ -37225,6 +37460,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -37235,7 +37473,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA } while (0); } - object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -37245,13 +37483,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA if (IS_CV != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CV == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -37298,7 +37539,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CV & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CV & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -37324,18 +37566,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_CV_HANDLER(ZEND_O do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -37366,10 +37608,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_CV_HANDLER(ZEND_O } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -37388,11 +37630,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_H SAVE_OPLINE(); if ((IS_CV == IS_VAR || IS_CV == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); if (IS_CV == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -37402,11 +37643,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_H } else { expr_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CV == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CV == IS_CV) { @@ -37432,48 +37673,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_CV_H if (IS_CV != IS_UNUSED) { - zval *offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + zval *offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); zend_string *str; zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if (IS_CV != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } } else { @@ -37521,6 +37760,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CV_HANDLER(Z zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var); @@ -37532,72 +37772,80 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_CV_HANDLER(Z if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -unset_dim_again: - if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if (IS_CV != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); CHECK_EXCEPTION(); @@ -37621,7 +37869,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER(Z if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); @@ -37667,14 +37914,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_ HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -isset_dim_obj_again: if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -37689,31 +37937,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -37723,50 +37971,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CV == IS_UNUSED || + (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -37793,7 +38053,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -37808,10 +38069,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } @@ -37828,7 +38088,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ zend_generator *generator = zend_get_running_generator(execute_data); SAVE_OPLINE(); - if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + if (UNEXPECTED(generator->flags & ZEND_GENERATOR_FORCED_CLOSE)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield from finally in a force-closed generator"); @@ -37845,7 +38105,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ if (IS_CV != IS_UNUSED) { - if (EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { @@ -37855,11 +38115,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ value = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); ZVAL_COPY_VALUE(&generator->value, value); - if (Z_OPT_REFCOUNTED(generator->value)) Z_SET_REFCOUNT(generator->value, 1); - - /* Temporary variables don't need ctor copying */ - if (IS_CV != IS_TMP_VAR) { - zval_opt_copy_ctor(&generator->value); + if (IS_CV != IS_CONST) { + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } } else { zval *value_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); @@ -37867,7 +38126,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot yield string offsets by reference"); - HANDLE_EXCEPTION(); } @@ -37889,11 +38147,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->value, value); + ZVAL_COPY_VALUE(&generator->value, value); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->value))) { + zval_copy_ctor_func(&generator->value); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->value, value); - } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_ISREF_P(value)) { - ZVAL_DUP(&generator->value, Z_REFVAL_P(value)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(value)) { + ZVAL_COPY(&generator->value, Z_REFVAL_P(value)); } else { ZVAL_COPY_VALUE(&generator->value, value); @@ -37914,11 +38175,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ /* Consts, temporary variables and references need copying */ if (IS_CV == IS_CONST) { - ZVAL_DUP(&generator->key, key); + ZVAL_COPY_VALUE(&generator->key, key); + if (UNEXPECTED(Z_OPT_COPYABLE(generator->key))) { + zval_copy_ctor_func(&generator->key); + } } else if (IS_CV == IS_TMP_VAR) { ZVAL_COPY_VALUE(&generator->key, key); - } else if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(key)) { - ZVAL_DUP(&generator->key, Z_REFVAL_P(key)); + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(key)) { + ZVAL_COPY(&generator->key, Z_REFVAL_P(key)); } else { ZVAL_COPY_VALUE(&generator->key, key); @@ -37962,11 +38226,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CV_CV_HANDLER(ZEND_OP { USE_OPLINE + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + pow_function(EX_VAR(opline->result.var), op1, op2); CHECK_EXCEPTION(); @@ -38117,11 +38382,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_CV_TMPVAR_HANDLER(ZEN { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38171,11 +38437,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_CV_TMPVAR_HANDLER(ZEND { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38186,11 +38453,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_CV_TMPVAR_HANDLER(ZEND { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38201,12 +38469,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_CV_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + + do { + if ((IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + ((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + + break; + } + } + if (IS_CV != IS_CONST && IS_CV != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + + } while (0); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -38223,18 +38537,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -38266,10 +38580,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLE } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -38292,18 +38606,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HA do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -38335,10 +38649,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HA } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -38456,12 +38770,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_CV_TMPVAR_HANDL { USE_OPLINE zend_free_op free_op2; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + compare_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38472,11 +38786,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_CV_TMPVAR_HANDLER(Z { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38487,11 +38802,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_CV_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38502,11 +38818,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_CV_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38517,11 +38834,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_CV_TMPVAR_HANDLE { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -38553,7 +38871,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -38583,42 +38900,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_obj_helper_SP ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval *z; - zval rv, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - if (Z_OBJ_HT(obj)->read_property && - (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv)) != NULL) { - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - zval_ptr_dtor(zptr); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } - OBJ_RELEASE(Z_OBJ(obj)); + zend_assign_op_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), value, binary_op, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -38650,7 +38932,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_dim_helper_SP zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } @@ -38721,7 +39002,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_binary_assign_op_helper_SPEC_C if (IS_CV == IS_VAR && UNEXPECTED(var_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use assign-op operators with overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -38966,7 +39246,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_BW_XOR_SPEC_CV_TMPVAR_H #endif } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op2; @@ -38988,7 +39268,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -39009,51 +39288,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - SEPARATE_ZVAL_NOREF(zptr); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); + } + } else { + ZVAL_DEREF(zptr); + SEPARATE_ZVAL_NOREF(zptr); - incdec_op(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), zptr); } } else { - zval rv; - - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - incdec_op(z); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), z); - } - Z_OBJ_HT(obj)->write_property(&obj, property, z, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(z); - } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - } + zend_pre_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); } } while (0); @@ -39065,15 +39320,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_pre_incdec_property_helper_SPE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_OBJ_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_pre_incdec_property_helper_SPEC_CV_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_TMPVAR(incdec_t incdec_op ZEND_OPCODE_HANDLER_ARGS_DC) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SPEC_CV_TMPVAR(int inc ZEND_OPCODE_HANDLER_ARGS_DC) { USE_OPLINE zend_free_op free_op2; @@ -39095,7 +39350,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -39114,44 +39368,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP if (EXPECTED(Z_OBJ_HT_P(object)->get_property_ptr_ptr) && EXPECTED((zptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL))) != NULL)) { - ZVAL_DEREF(zptr); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); - zval_opt_copy_ctor(zptr); - incdec_op(zptr); - } else { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), &rv); - if (UNEXPECTED(EG(exception))) { - OBJ_RELEASE(Z_OBJ(obj)); - break; - } - - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (EXPECTED(Z_TYPE_P(zptr) == IS_LONG)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + if (inc) { + fast_long_increment_function(zptr); + } else { + fast_long_decrement_function(zptr); } - ZVAL_DUP(EX_VAR(opline->result.var), z); - ZVAL_DUP(&z_copy, z); - incdec_op(&z_copy); - if (Z_REFCOUNTED_P(z)) Z_ADDREF_P(z); - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL)); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + ZVAL_DEREF(zptr); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), zptr); + zval_opt_copy_ctor(zptr); + if (inc) { + increment_function(zptr); + } else { + decrement_function(zptr); + } } + } else { + zend_post_incdec_overloaded_property(object, property, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), inc, EX_VAR(opline->result.var)); } } while (0); @@ -39163,12 +39398,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_post_incdec_property_helper_SP static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_OBJ_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_TMPVAR(increment_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_TMPVAR(1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_DEC_OBJ_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - return zend_post_incdec_property_helper_SPEC_CV_TMPVAR(decrement_function ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); + return zend_post_incdec_property_helper_SPEC_CV_TMPVAR(0 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC); } static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -39197,7 +39432,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_W_SPEC_CV_TMPVAR_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -39221,7 +39455,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_RW_SPEC_CV_TMPVAR_HA if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_RW(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -39268,7 +39501,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMP if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_W(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -39305,7 +39537,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_UNSET_SPEC_CV_TMPVAR if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } zend_fetch_dimension_address_UNSET(EX_VAR(opline->result.var), container, _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2), (IS_TMP_VAR|IS_VAR)); @@ -39337,7 +39568,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_R_SPEC_CV_TMPVAR_HAN offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -39410,7 +39642,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HAN if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } @@ -39443,7 +39674,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HA if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_RW); @@ -39475,7 +39705,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_CV_TMPVAR_HA offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -39557,7 +39788,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMP if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_W); @@ -39593,7 +39823,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an object"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_fetch_property_address(EX_VAR(opline->result.var), container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property)) : NULL), BP_VAR_UNSET); @@ -39627,7 +39856,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_CV_TMPVAR_HAND if (IS_CV == IS_VAR && UNEXPECTED(object == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); zval_ptr_dtor_nogc(free_op2); - HANDLE_EXCEPTION(); } zend_assign_to_object(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object, IS_CV, property_name, (IS_TMP_VAR|IS_VAR), (opline+1)->op1_type, (opline+1)->op1, execute_data, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL)); @@ -39656,11 +39884,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_SPEC_CV_TMPVAR_HAND zend_error(E_EXCEPTION | E_ERROR, "Cannot use string offset as an array"); FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } -try_assign_dim: if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { try_assign_dim_array: if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { @@ -39688,53 +39914,58 @@ try_assign_dim_array: ZVAL_COPY(EX_VAR(opline->result.var), value); } } - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { - zend_free_op free_op2; - zval *property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + } else { + if (EXPECTED(Z_ISREF_P(object_ptr))) { + object_ptr = Z_REFVAL_P(object_ptr); + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_ARRAY)) { + goto try_assign_dim_array; + } + } + if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { + zend_free_op free_op2; + zval *property_name = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); - zval_ptr_dtor_nogc(free_op2); - } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { - if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { - if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { - zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); - FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); + zend_assign_to_object_dim(UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL, object_ptr, property_name, (opline+1)->op1_type, (opline+1)->op1, execute_data); + zval_ptr_dtor_nogc(free_op2); + } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) { + if (EXPECTED(Z_STRLEN_P(object_ptr) != 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED) { + zend_error(E_EXCEPTION | E_ERROR, "[] operator not supported for strings"); + FREE_UNFETCHED_OP((opline+1)->op1_type, (opline+1)->op1.var); - HANDLE_EXCEPTION(); - } else { - zend_long offset; + HANDLE_EXCEPTION(); + } else { + zend_long offset; - dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); - zval_ptr_dtor_nogc(free_op2); - value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); - FREE_OP(free_op_data1); + dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W); + zval_ptr_dtor_nogc(free_op2); + value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL)); + FREE_OP(free_op_data1); + } + } else { + zval_ptr_dtor_nogc(object_ptr); +assign_dim_convert_to_array: + ZVAL_NEW_ARR(object_ptr); + zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); + goto try_assign_dim_array; + } + } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { + if (IS_CV == IS_VAR && UNEXPECTED(object_ptr == &EG(error_zval))) { + goto assign_dim_clean; } + goto assign_dim_convert_to_array; } else { - zval_ptr_dtor_nogc(object_ptr); -assign_dim_convert_to_array: - ZVAL_NEW_ARR(object_ptr); - zend_hash_init(Z_ARRVAL_P(object_ptr), 8, NULL, ZVAL_PTR_DTOR, 0); - goto try_assign_dim_array; - } - } else if (EXPECTED(Z_ISREF_P(object_ptr))) { - object_ptr = Z_REFVAL_P(object_ptr); - goto try_assign_dim; - } else if (EXPECTED(Z_TYPE_P(object_ptr) <= IS_FALSE)) { - if (UNEXPECTED(object_ptr == &EG(error_zval))) { - goto assign_dim_clean; - } - goto assign_dim_convert_to_array; - } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_error(E_WARNING, "Cannot use a scalar value as an array"); assign_dim_clean: - dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - zval_ptr_dtor_nogc(free_op2); - value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); - FREE_OP(free_op_data1); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); + dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + zval_ptr_dtor_nogc(free_op2); + value = get_zval_ptr((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R); + FREE_OP(free_op_data1); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } } } @@ -39752,28 +39983,60 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HAN zend_string *op1_str, *op2_str, *str; SAVE_OPLINE(); - op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + op1 = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if (IS_CV != IS_CONST) { - zend_string_release(op1_str); - } - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op2_str); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -39805,6 +40068,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -39815,7 +40081,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA } while (0); } - object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + object = _get_zval_ptr_cv_undef(execute_data, opline->op1.var); if (IS_CV == IS_UNUSED && UNEXPECTED(Z_OBJ_P(object) == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Using $this when not in object context"); @@ -39825,13 +40091,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA if (IS_CV != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if (IS_CV == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -39878,7 +40147,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if (IS_CV & (IS_VAR|IS_TMP_VAR)) { + } else if (IS_CV & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -39905,18 +40175,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_TMPVAR_HANDLER(ZE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -39947,10 +40217,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_CV_TMPVAR_HANDLER(ZE } while (0); SAVE_OPLINE(); - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -39969,11 +40239,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPV SAVE_OPLINE(); if ((IS_CV == IS_VAR || IS_CV == IS_CV) && - (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { + UNEXPECTED(opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) { expr_ptr = _get_zval_ptr_cv_BP_VAR_W(execute_data, opline->op1.var); if (IS_CV == IS_VAR && UNEXPECTED(expr_ptr == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot create references to/from string offsets"); - zend_array_destroy(Z_ARRVAL_P(EX_VAR(opline->result.var))); HANDLE_EXCEPTION(); } @@ -39983,11 +40252,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPV } else { expr_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&new_expr, expr_ptr); - expr_ptr = &new_expr; + /* pass */ } else if (IS_CV == IS_CONST) { - if (!Z_IMMUTABLE_P(expr_ptr)) { - ZVAL_DUP(&new_expr, expr_ptr); + if (UNEXPECTED(Z_OPT_COPYABLE_P(expr_ptr))) { + ZVAL_COPY_VALUE(&new_expr, expr_ptr); + zval_copy_ctor_func(&new_expr); expr_ptr = &new_expr; } } else if (IS_CV == IS_CV) { @@ -40018,43 +40287,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_TMPV zend_ulong hval; add_again: - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index: - zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); - break; - case IS_STRING: - str = Z_STR_P(offset); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(str, hval)) { - goto num_index; - } + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + str = Z_STR_P(offset); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_index; } + } str_index: - zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); - break; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index; - case IS_FALSE: - hval = 0; - goto num_index; - case IS_TRUE: - hval = 1; - goto num_index; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto add_again; - break; - default: - zend_error(E_WARNING, "Illegal offset type"); - zval_ptr_dtor(expr_ptr); - /* do nothing */ - break; + zend_hash_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), str, expr_ptr); + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index: + zend_hash_index_update(Z_ARRVAL_P(EX_VAR(opline->result.var)), hval, expr_ptr); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto add_again; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index; + } else { + zend_error(E_WARNING, "Illegal offset type"); + zval_ptr_dtor(expr_ptr); } zval_ptr_dtor_nogc(free_op2); } else { @@ -40102,6 +40369,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDL zval *container; zval *offset; zend_ulong hval; + zend_string *key; SAVE_OPLINE(); container = _get_zval_ptr_cv_BP_VAR_UNSET(execute_data, opline->op1.var); @@ -40113,72 +40381,81 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_DIM_SPEC_CV_TMPVAR_HANDL if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -unset_dim_again: - if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht; + do { + if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + HashTable *ht; +unset_dim_array: + SEPARATE_ARRAY(container); + ht = Z_ARRVAL_P(container); offset_again: - SEPARATE_ARRAY(container); - ht = Z_ARRVAL_P(container); - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - zend_hash_index_del(ht, hval); - break; - case IS_LONG: - hval = Z_LVAL_P(offset); -num_index_dim: - zend_hash_index_del(ht, hval); - break; - case IS_STRING: + if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { + key = Z_STR_P(offset); if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), hval)) { + if (ZEND_HANDLE_NUMERIC(key, hval)) { goto num_index_dim; } } +str_index_dim: if (ht == &EG(symbol_table)) { - zend_delete_global_variable(Z_STR_P(offset)); + zend_delete_global_variable(key); } else { - zend_hash_del(ht, Z_STR_P(offset)); + zend_hash_del(ht, key); } - break; - case IS_NULL: - zend_hash_del(ht, STR_EMPTY_ALLOC()); - break; - case IS_FALSE: + } else if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + hval = Z_LVAL_P(offset); +num_index_dim: + zend_hash_index_del(ht, hval); + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_TYPE_P(offset) == IS_REFERENCE)) { + offset = Z_REFVAL_P(offset); + goto offset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_dim; + } else if (Z_TYPE_P(offset) == IS_NULL) { + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else if (Z_TYPE_P(offset) == IS_FALSE) { hval = 0; goto num_index_dim; - case IS_TRUE: + } else if (Z_TYPE_P(offset) == IS_TRUE) { hval = 1; goto num_index_dim; - case IS_RESOURCE: + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { hval = Z_RES_HANDLE_P(offset); goto num_index_dim; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto offset_again; - break; - default: + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + key = STR_EMPTY_ALLOC(); + goto str_index_dim; + } else { zend_error(E_WARNING, "Illegal offset type in unset"); - break; + } + break; + } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto unset_dim_array; + } } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); - } else { - Z_OBJ_HT_P(container)->unset_dimension(container, offset); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); } - } else if (IS_CV != IS_UNUSED && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto unset_dim_again; - } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { - zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); - } + if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (UNEXPECTED(Z_OBJ_HT_P(container)->unset_dimension == NULL)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot use object as array"); + } else { + Z_OBJ_HT_P(container)->unset_dimension(container, offset); + } + } else if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { + zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); + } + } while (0); + zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -40202,7 +40479,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDL if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) { zend_error(E_EXCEPTION | E_ERROR, "Cannot unset string offsets"); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); - HANDLE_EXCEPTION(); } offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); @@ -40251,12 +40527,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_CV_ offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -isset_dim_obj_again: if (IS_CV != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -40271,31 +40548,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -40305,50 +40582,61 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if (IS_CV == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if (IS_CV == IS_UNUSED || + (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if ((IS_TMP_VAR|IS_VAR) & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: zval_ptr_dtor_nogc(free_op2); ZEND_VM_SMART_BRANCH(result, 1); @@ -40376,7 +40664,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if (IS_CV == IS_CONST || + (IS_CV != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if ((IS_CV & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -40391,10 +40680,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op2); @@ -40409,11 +40697,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_CV_TMPVAR_HANDLER(ZEN { USE_OPLINE zend_free_op free_op2; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + pow_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -40483,6 +40772,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ECHO_SPEC_TMPVAR_HANDLER(ZEND_ if (str->len != 0) { zend_write(str->val, str->len); + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(z) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(z, BP_VAR_R); } zend_string_release(str); } @@ -40766,7 +41057,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND if (EXPECTED(Z_TYPE_P(obj) == IS_OBJECT)) { break; } - } + } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(obj, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -40834,6 +41128,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA ZVAL_UNDEF(&tmp_inc_filename); if (Z_TYPE_P(inc_filename) != IS_STRING) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(inc_filename) == IS_UNDEF)) { + inc_filename = GET_OP1_UNDEF_CV(inc_filename, BP_VAR_R); + } ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); inc_filename = &tmp_inc_filename; } @@ -40991,6 +41288,9 @@ try_strlen: } else { zend_bool strict; + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + value = GET_OP1_UNDEF_CV(value, BP_VAR_R); + } if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(value) == IS_REFERENCE) { value = Z_REFVAL_P(value); goto try_strlen; @@ -41157,11 +41457,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_TMPVAR_CONST_HANDLER( { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41211,11 +41512,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVAR_CONST_HANDLER(Z { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41226,11 +41528,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVAR_CONST_HANDLER(Z { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41241,12 +41544,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CONST_HANDL { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); - zval_ptr_dtor_nogc(free_op1); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + + do { + if (((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CONST == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST && (IS_TMP_VAR|IS_VAR) != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + zval_ptr_dtor_nogc(free_op1); + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -41263,18 +41612,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HAN do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -41306,10 +41655,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HAN } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -41332,18 +41681,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -41375,10 +41724,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -41496,12 +41845,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_TMPVAR_CONST_HA { USE_OPLINE zend_free_op free_op1; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + compare_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41512,11 +41861,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVAR_CONST_HANDLE { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41527,11 +41877,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVAR_CONST_HANDL { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41542,11 +41893,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVAR_CONST_HANDL { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41557,11 +41909,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_TMPVAR_CONST_HAN { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41586,6 +41939,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -41802,7 +42158,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CONST offset = EX_CONSTANT(opline->op2); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -41874,7 +42231,8 @@ try_fetch_list: } else { ZVAL_COPY(EX_VAR(opline->result.var), value); } - } else if (UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && + } else if ((IS_TMP_VAR|IS_VAR) != IS_CONST && + UNEXPECTED(Z_TYPE_P(container) == IS_OBJECT) && EXPECTED(Z_OBJ_HT_P(container)->read_dimension)) { zval *result = EX_VAR(opline->result.var); zval *retval = Z_OBJ_HT_P(container)->read_dimension(container, EX_CONSTANT(opline->op2), BP_VAR_R, result); @@ -41886,10 +42244,13 @@ try_fetch_list: } else { ZVAL_NULL(result); } - } else if (Z_TYPE_P(container) == IS_REFERENCE) { + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_TYPE_P(container) == IS_REFERENCE) { container = Z_REFVAL_P(container); goto try_fetch_list; } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(container, BP_VAR_R); + } ZVAL_NULL(EX_VAR(opline->result.var)); } CHECK_EXCEPTION(); @@ -41905,27 +42266,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_ SAVE_OPLINE(); op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - op2 = EX_CONSTANT(opline->op2); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = EX_CONSTANT(opline->op2); if (IS_CONST == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CONST != IS_CONST) { - zend_string_release(op2_str); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CONST == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CONST != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -41957,6 +42350,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C break; } } + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -41977,13 +42373,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -42030,7 +42429,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) { + } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -42057,18 +42457,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -42099,10 +42499,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -42147,6 +42547,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_CONST_HA ZVAL_UNDEF(&tmp); if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -42318,12 +42721,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP offset = EX_CONSTANT(opline->op2); -isset_dim_obj_again: if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -42338,31 +42742,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CONST & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CONST == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -42372,50 +42776,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || + ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CONST & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } - } - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -42442,7 +42858,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM offset = EX_CONSTANT(opline->op2); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -42457,10 +42874,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CONST == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op1); @@ -42505,6 +42921,9 @@ try_instanceof: expr = Z_REFVAL_P(expr); goto try_instanceof; } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(expr) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(expr, BP_VAR_R); + } result = 0; } zval_ptr_dtor_nogc(free_op1); @@ -42518,11 +42937,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_TMPVAR_CONST_HANDLER( { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - EX_CONSTANT(opline->op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + pow_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -42547,6 +42967,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -42748,6 +43171,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_VAR_HAND ZVAL_UNDEF(&tmp); if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -42934,6 +43360,9 @@ try_instanceof: expr = Z_REFVAL_P(expr); goto try_instanceof; } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(expr) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(expr, BP_VAR_R); + } result = 0; } zval_ptr_dtor_nogc(free_op1); @@ -42961,6 +43390,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_ name = Z_STR_P(varname); zend_string_addref(name); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } name = zval_get_string(varname); } @@ -43162,6 +43594,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_UNSET_VAR_SPEC_TMPVAR_UNUSED_H ZVAL_UNDEF(&tmp); if ((IS_TMP_VAR|IS_VAR) != IS_CONST && Z_TYPE_P(varname) != IS_STRING) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + varname = GET_OP1_UNDEF_CV(varname, BP_VAR_R); + } ZVAL_STR(&tmp, zval_get_string(varname)); varname = &tmp; } @@ -43452,11 +43887,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_TMPVAR_CV_HANDLER(ZEN { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + fast_div_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43506,11 +43942,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVAR_CV_HANDLER(ZEND { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_left_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43521,11 +43958,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVAR_CV_HANDLER(ZEND { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + shift_right_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43536,12 +43974,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_CV_HANDLER( { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); - zval_ptr_dtor_nogc(free_op1); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); + + do { + if (((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + (IS_CV == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST && (IS_TMP_VAR|IS_VAR) != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + zval_ptr_dtor_nogc(free_op1); + } while (0); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -43558,18 +44042,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -43601,10 +44085,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CV_HANDLE } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -43627,18 +44111,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HA do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -43670,10 +44154,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CV_HA } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -43791,12 +44275,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_TMPVAR_CV_HANDL { USE_OPLINE zend_free_op free_op1; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + compare_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43807,11 +44291,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVAR_CV_HANDLER(Z { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43822,11 +44307,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVAR_CV_HANDLER( { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43837,11 +44323,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVAR_CV_HANDLER( { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43852,11 +44339,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_TMPVAR_CV_HANDLE { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -43912,7 +44400,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_CV_HA offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -43974,27 +44463,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HAN SAVE_OPLINE(); op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op1_str); - } - if (IS_CV != IS_CONST) { - zend_string_release(op2_str); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if (IS_CV == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if (IS_CV != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -44015,7 +44536,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C SAVE_OPLINE(); - function_name = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + function_name = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (IS_CV != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { @@ -44026,6 +44547,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C break; } } + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -44046,13 +44570,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -44099,7 +44626,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) { + } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -44126,18 +44654,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CV_HANDLER(ZE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -44168,10 +44696,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CV_HANDLER(ZE } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -44200,14 +44728,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP HANDLE_EXCEPTION(); } - offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + offset = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); -isset_dim_obj_again: if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -44222,31 +44751,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if (IS_CV == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -44256,50 +44785,62 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || + ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if (IS_CV & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: + zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, 1); ZVAL_BOOL(EX_VAR(opline->result.var), result); @@ -44326,7 +44867,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM offset = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -44341,10 +44883,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, ((IS_CV == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op1); @@ -44358,11 +44899,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_TMPVAR_CV_HANDLER(ZEN { USE_OPLINE zend_free_op free_op1; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + pow_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); CHECK_EXCEPTION(); @@ -44508,11 +45050,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DIV_SPEC_TMPVAR_TMPVAR_HANDLER { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - fast_div_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + fast_div_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44562,11 +45105,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SL_SPEC_TMPVAR_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_left_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_left_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44577,11 +45121,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SR_SPEC_TMPVAR_TMPVAR_HANDLER( { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - shift_right_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + shift_right_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44592,12 +45137,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CONCAT_SPEC_TMPVAR_TMPVAR_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - concat_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); - zval_ptr_dtor_nogc(free_op1); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + + do { + if (((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op1) == IS_STRING)) && + ((IS_TMP_VAR|IS_VAR) == IS_CONST || EXPECTED(Z_TYPE_P(op2) == IS_STRING))) { + zend_string *op1_str = Z_STR_P(op1); + zend_string *op2_str = Z_STR_P(op2); + zend_string *str; + + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op2_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), op1_str); + zval_ptr_dtor_nogc(free_op1); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST && (IS_TMP_VAR|IS_VAR) != IS_CV && + !IS_INTERNED(op1_str) && GC_REFCOUNT(op1_str) == 1) { + size_t len = op1_str->len; + + str = zend_string_realloc(op1_str, len + op2_str->len, 0); + memcpy(str->val + len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + break; + } else { + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + } + } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + concat_function(EX_VAR(opline->result.var), op1, op2); + } + zval_ptr_dtor_nogc(free_op1); + } while (0); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); @@ -44614,18 +45205,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HA do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -44657,10 +45248,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HA } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -44683,18 +45274,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) != Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) != Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) != Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) != ((double)Z_LVAL_P(op2))); } else { break; @@ -44726,10 +45317,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -44847,12 +45438,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPACESHIP_SPEC_TMPVAR_TMPVAR_H { USE_OPLINE zend_free_op free_op1, free_op2; - zval *result = EX_VAR(opline->result.var); + zval *op1, *op2; SAVE_OPLINE(); - compare_function(result, - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + compare_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44863,11 +45454,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_OR_SPEC_TMPVAR_TMPVAR_HANDL { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_or_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_or_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44878,11 +45470,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_AND_SPEC_TMPVAR_TMPVAR_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_and_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_and_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44893,11 +45486,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_XOR_SPEC_TMPVAR_TMPVAR_HAND { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - bitwise_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + bitwise_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44908,11 +45502,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BOOL_XOR_SPEC_TMPVAR_TMPVAR_HA { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - boolean_xor_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + boolean_xor_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -44968,7 +45563,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_IS_SPEC_TMPVAR_TMPVA offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -45031,27 +45627,59 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR SAVE_OPLINE(); op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op1_str = Z_STR_P(op1); + } else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) { + op1_str = zend_string_copy(Z_STR_P(op1)); } else { - op1_str = zval_get_string(op1); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(op1, BP_VAR_R); + } + op1_str = _zval_get_string_func(op1); } + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { op2_str = Z_STR_P(op2); + } else if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { + op2_str = zend_string_copy(Z_STR_P(op2)); } else { - op2_str = zval_get_string(op2); - } - str = zend_string_alloc(op1_str->len + op2_str->len, 0); - memcpy(str->val, op1_str->val, op1_str->len); - memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); - ZVAL_NEW_STR(EX_VAR(opline->result.var), str); - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op1_str); - } - if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { - zend_string_release(op2_str); + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(op2, BP_VAR_R); + } + op2_str = _zval_get_string_func(op2); } + do { + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op1_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op2_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op2_str); + zend_string_release(op1_str); + break; + } + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + if (UNEXPECTED(op2_str->len == 0)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + zend_string_addref(op1_str); + } + ZVAL_STR(EX_VAR(opline->result.var), op1_str); + zend_string_release(op2_str); + break; + } + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + } while (0); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -45083,6 +45711,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { + GET_OP2_UNDEF_CV(function_name, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -45103,13 +45734,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) { do { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { + GET_OP1_UNDEF_CV(object, BP_VAR_R); + } if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } @@ -45156,7 +45790,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; - } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR)) { + } else if ((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_TMP_VAR|IS_CV)) { + /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } @@ -45184,18 +45819,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLE do { int result; - if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_LVAL_P(op1) == Z_LVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = ((double)Z_LVAL_P(op1) == Z_DVAL_P(op2)); } else { break; } - } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { - if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { + } else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) { + if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) { result = (Z_DVAL_P(op1) == Z_DVAL_P(op2)); - } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { + } else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { result = (Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2))); } else { break; @@ -45226,10 +45861,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLE } while (0); SAVE_OPLINE(); - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } - if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); @@ -45260,12 +45895,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_DIM_OBJ_SPEC_TMP offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); -isset_dim_obj_again: if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - HashTable *ht = Z_ARRVAL_P(container); + HashTable *ht; zval *value; zend_string *str; +isset_dim_obj_array: + ht = Z_ARRVAL_P(container); isset_again: if (EXPECTED(Z_TYPE_P(offset) == IS_STRING)) { str = Z_STR_P(offset); @@ -45280,31 +45916,31 @@ str_index_prop: hval = Z_LVAL_P(offset); num_index_prop: value = zend_hash_index_find(ht, hval); - } else { - switch (Z_TYPE_P(offset)) { - case IS_DOUBLE: - hval = zend_dval_to_lval(Z_DVAL_P(offset)); - goto num_index_prop; - case IS_NULL: - str = STR_EMPTY_ALLOC(); - goto str_index_prop; - case IS_FALSE: - hval = 0; - goto num_index_prop; - case IS_TRUE: - hval = 1; - goto num_index_prop; - case IS_RESOURCE: - hval = Z_RES_HANDLE_P(offset); - goto num_index_prop; - case IS_REFERENCE: - offset = Z_REFVAL_P(offset); - goto isset_again; - default: - zend_error(E_WARNING, "Illegal offset type in isset or empty"); - value = NULL; - break; - } + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(offset))) { + offset = Z_REFVAL_P(offset); + goto isset_again; + } else if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_NULL) { + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_index_prop; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_index_prop; + } else if ((IS_TMP_VAR|IS_VAR) == IS_CV && Z_TYPE_P(offset) == IS_UNDEF) { + GET_OP2_UNDEF_CV(offset, BP_VAR_R); + str = STR_EMPTY_ALLOC(); + goto str_index_prop; + } else { + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + goto isset_not_found; } if (opline->extended_value & ZEND_ISSET) { @@ -45314,50 +45950,61 @@ num_index_prop: } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { result = (value == NULL || !i_zend_is_true(value)); } - } else if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + goto isset_dim_obj_exit; + } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { + container = Z_REFVAL_P(container); + if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { + goto isset_dim_obj_array; + } + } + + if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + offset = GET_OP2_UNDEF_CV(offset, BP_VAR_R); + } + + if ((IS_TMP_VAR|IS_VAR) == IS_UNUSED || + ((IS_TMP_VAR|IS_VAR) != IS_CONST && EXPECTED(Z_TYPE_P(container) == IS_OBJECT))) { if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { - result = Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_dimension(container, offset, (opline->extended_value & ZEND_ISSET) == 0); } else { zend_error(E_NOTICE, "Trying to check element of non-array"); - result = 0; - } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; + goto isset_not_found; } } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ - zval tmp; + zend_long lval; - result = 0; - if (UNEXPECTED(Z_TYPE_P(offset) != IS_LONG)) { + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +isset_str_offset: + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + if (opline->extended_value & ZEND_ISSET) { + result = 1; + } else { + result = (Z_STRVAL_P(container)[lval] == '0'); + } + } else { + goto isset_not_found; + } + } else { if ((IS_TMP_VAR|IS_VAR) & (IS_CV|IS_VAR)) { ZVAL_DEREF(offset); } if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { - ZVAL_DUP(&tmp, offset); - convert_to_long(&tmp); - offset = &tmp; - } - } - if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { - if (offset->value.lval >= 0 && (size_t)offset->value.lval < Z_STRLEN_P(container)) { - if ((opline->extended_value & ZEND_ISSET) || - Z_STRVAL_P(container)[offset->value.lval] != '0') { - result = 1; - } + lval = zval_get_long(offset); + goto isset_str_offset; } + goto isset_not_found; } - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } - } else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { - container = Z_REFVAL_P(container); - goto isset_dim_obj_again; } else { +isset_not_found: result = ((opline->extended_value & ZEND_ISSET) == 0); } +isset_dim_obj_exit: zval_ptr_dtor_nogc(free_op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_SMART_BRANCH(result, 1); @@ -45385,7 +46032,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM offset = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + if ((IS_TMP_VAR|IS_VAR) == IS_CONST || + ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT))) { if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) && Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { @@ -45400,10 +46048,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_TM isset_no_object: result = ((opline->extended_value & ZEND_ISSET) == 0); } else { - result = Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); - if ((opline->extended_value & ZEND_ISSET) == 0) { - result = !result; - } + result = + ((opline->extended_value & ZEND_ISSET) == 0) ^ + Z_OBJ_HT_P(container)->has_property(container, offset, (opline->extended_value & ZEND_ISSET) == 0, (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(offset)) : NULL)); } zval_ptr_dtor_nogc(free_op2); @@ -45418,11 +46065,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POW_SPEC_TMPVAR_TMPVAR_HANDLER { USE_OPLINE zend_free_op free_op1, free_op2; + zval *op1, *op2; SAVE_OPLINE(); - pow_function(EX_VAR(opline->result.var), - _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1), - _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + pow_function(EX_VAR(opline->result.var), op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); CHECK_EXCEPTION(); @@ -46689,52 +47337,52 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_BRK_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_BRK_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_BRK_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_BRK_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_BRK_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CONT_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CONT_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CONT_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CONT_SPEC_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_CONT_SPEC_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 7d49bea509..0b8caadc74 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -259,6 +259,26 @@ $op2_get_obj_zval_ptr = array( "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)", ); +$op1_get_obj_zval_ptr_undef = array( + "ANY" => "get_obj_zval_ptr_undef(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)", + "TMP" => "_get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1)", + "VAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)", + "CONST" => "EX_CONSTANT(opline->op1)", + "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)", + "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op1.var)", + "TMPVAR" => "_get_zval_ptr_var(opline->op1.var, execute_data, &free_op1)", +); + +$op2_get_obj_zval_ptr_undef = array( + "ANY" => "get_obj_zval_ptr_undef(opline->op2_type, opline->op2, execute_data, &free_op2, \\1)", + "TMP" => "_get_zval_ptr_tmp(opline->op2.var, execute_data, &free_op2)", + "VAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)", + "CONST" => "EX_CONSTANT(opline->op2)", + "UNUSED" => "_get_obj_zval_ptr_unused(execute_data)", + "CV" => "_get_zval_ptr_cv_undef(execute_data, opline->op2.var)", + "TMPVAR" => "_get_zval_ptr_var(opline->op2.var, execute_data, &free_op2)", +); + $op1_get_obj_zval_ptr_deref = array( "ANY" => "get_obj_zval_ptr(opline->op1_type, opline->op1, execute_data, &free_op1, \\1)", "TMP" => "_get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1)", @@ -449,6 +469,7 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) { $op1_get_zval_ptr_ptr, $op2_get_zval_ptr_ptr, $op1_get_zval_ptr_ptr_undef, $op2_get_zval_ptr_ptr_undef, $op1_get_obj_zval_ptr, $op2_get_obj_zval_ptr, + $op1_get_obj_zval_ptr_undef, $op2_get_obj_zval_ptr_undef, $op1_get_obj_zval_ptr_deref, $op2_get_obj_zval_ptr_deref, $op1_get_obj_zval_ptr_ptr, $op2_get_obj_zval_ptr_ptr, $op1_get_obj_zval_ptr_ptr_undef, $op2_get_obj_zval_ptr_ptr_undef, @@ -475,6 +496,8 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) { "/GET_OP2_ZVAL_PTR_PTR_UNDEF\(([^)]*)\)/", "/GET_OP1_OBJ_ZVAL_PTR\(([^)]*)\)/", "/GET_OP2_OBJ_ZVAL_PTR\(([^)]*)\)/", + "/GET_OP1_OBJ_ZVAL_PTR_UNDEF\(([^)]*)\)/", + "/GET_OP2_OBJ_ZVAL_PTR_UNDEF\(([^)]*)\)/", "/GET_OP1_OBJ_ZVAL_PTR_DEREF\(([^)]*)\)/", "/GET_OP2_OBJ_ZVAL_PTR_DEREF\(([^)]*)\)/", "/GET_OP1_OBJ_ZVAL_PTR_PTR\(([^)]*)\)/", @@ -517,6 +540,8 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name) { $op2_get_zval_ptr_ptr_undef[$op2], $op1_get_obj_zval_ptr[$op1], $op2_get_obj_zval_ptr[$op2], + $op1_get_obj_zval_ptr_undef[$op1], + $op2_get_obj_zval_ptr_undef[$op2], $op1_get_obj_zval_ptr_deref[$op1], $op2_get_obj_zval_ptr_deref[$op2], $op1_get_obj_zval_ptr_ptr[$op1], diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 95b8b859eb..705ab9cd29 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -72,8 +72,8 @@ const char *zend_vm_opcodes_map[173] = { "ZEND_JMPNZ_EX", "ZEND_CASE", NULL, - "ZEND_BRK", - "ZEND_CONT", + NULL, + NULL, "ZEND_BOOL", "ZEND_FAST_CONCAT", "ZEND_ROPE_INIT", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 89a0d3139b..f6de5b1b57 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -82,8 +82,6 @@ END_EXTERN_C() #define ZEND_JMPZ_EX 46 #define ZEND_JMPNZ_EX 47 #define ZEND_CASE 48 -#define ZEND_BRK 50 -#define ZEND_CONT 51 #define ZEND_BOOL 52 #define ZEND_FAST_CONCAT 53 #define ZEND_ROPE_INIT 54 diff --git a/configure.in b/configure.in index ffc714ff5f..b70f024252 100644 --- a/configure.in +++ b/configure.in @@ -1300,6 +1300,7 @@ PHP_SUBST_OLD(PHP_INSTALLED_SAPIS) PHP_SUBST(PHP_EXECUTABLE) +PHP_SUBST(PHP_FASTCGI_OBJS) PHP_SUBST(PHP_SAPI_OBJS) PHP_SUBST(PHP_BINARY_OBJS) PHP_SUBST(PHP_GLOBAL_OBJS) @@ -1435,10 +1436,10 @@ CC=$old_CC PHP_CONFIGURE_PART(Generating files) CXXFLAGS_CLEAN=$CXXFLAGS -CFLAGS_CLEAN=$CFLAGS +CFLAGS_CLEAN="$CFLAGS \$(PROF_FLAGS)" CFLAGS="\$(CFLAGS_CLEAN) $standard_libtool_flag" INLINE_CFLAGS="$INLINE_CFLAGS $standard_libtool_flag" -CXXFLAGS="$CXXFLAGS $standard_libtool_flag" +CXXFLAGS="$CXXFLAGS $standard_libtool_flag \$(PROF_FLAGS)" if test "$PHP_PHAR" != "no" && test "$PHP_CLI" != "no"; then pharcmd=pharcmd @@ -1466,6 +1467,8 @@ PHP_ADD_SOURCES(main, main.c snprintf.c spprintf.c php_sprintf.c \ network.c php_open_temporary_file.c \ output.c getopt.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) +PHP_ADD_SOURCES_X(main, fastcgi.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1, PHP_FASTCGI_OBJS, no) + PHP_ADD_SOURCES(main/streams, streams.c cast.c memory.c filter.c \ plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c \ glob_wrapper.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index e0b9176200..cae8c46519 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2562,9 +2562,9 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue) /* {{{ * use since curl needs a long not an int. */ form_error = curl_formadd(&first, &last, CURLFORM_COPYNAME, string_key->val, - CURLFORM_NAMELENGTH, (zend_long)string_key->len, + CURLFORM_NAMELENGTH, string_key->len, CURLFORM_COPYCONTENTS, postval->val, - CURLFORM_CONTENTSLENGTH, (zend_long)Z_STRLEN_P(current), + CURLFORM_CONTENTSLENGTH, postval->len, CURLFORM_END); if (form_error != CURL_FORMADD_OK) { @@ -3008,7 +3008,7 @@ PHP_FUNCTION(curl_getinfo) } #endif if (ch->header.str) { - CAASTR("request_header", ch->header.str); + CAASTR("request_header", zend_string_copy(ch->header.str)); } } else { switch (option) { diff --git a/ext/date/lib/timelib_structs.h b/ext/date/lib/timelib_structs.h index d68fbc1f28..33e9fce88c 100644 --- a/ext/date/lib/timelib_structs.h +++ b/ext/date/lib/timelib_structs.h @@ -121,7 +121,7 @@ typedef unsigned __int64 uint64_t; #include <strings.h> #endif -#if defined(__X86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64) +#if defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64) typedef int64_t timelib_long; typedef uint64_t timelib_ulong; # define TIMELIB_LONG_MAX INT64_MAX diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 4ca8d19e24..be48b13a8c 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -163,7 +163,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create_from_format, 0, 0, 2) ZEND_ARG_INFO(0, format) ZEND_ARG_INFO(0, time) - ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 1) + ZEND_ARG_INFO(0, object) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse, 0, 0, 1) @@ -889,6 +889,10 @@ PHP_MSHUTDOWN_FUNCTION(date) timelib_error_container_dtor(DATEG(last_errors)); } +#ifndef ZTS + DATEG(default_timezone) = NULL; +#endif + return SUCCESS; } /* }}} */ @@ -2569,18 +2573,16 @@ PHP_FUNCTION(date_create) zval *timezone_object = NULL; char *time_str = NULL; size_t time_str_len = 0; - zval datetime_object; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { RETURN_FALSE; } - php_date_instantiate(date_ce_date, &datetime_object); - if (!php_date_initialize(Z_PHPDATE_P(&datetime_object), time_str, time_str_len, NULL, timezone_object, 0)) { - zval_dtor(&datetime_object); + php_date_instantiate(date_ce_date, return_value); + if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) { + zval_ptr_dtor(return_value); RETURN_FALSE; } - RETVAL_ZVAL(&datetime_object, 0, 0); } /* }}} */ @@ -2592,18 +2594,16 @@ PHP_FUNCTION(date_create_immutable) zval *timezone_object = NULL; char *time_str = NULL; size_t time_str_len = 0; - zval datetime_object; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { RETURN_FALSE; } - php_date_instantiate(date_ce_immutable, &datetime_object); - if (!php_date_initialize(Z_PHPDATE_P(&datetime_object), time_str, time_str_len, NULL, timezone_object, 0)) { - zval_dtor(&datetime_object); + php_date_instantiate(date_ce_immutable, return_value); + if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) { + zval_ptr_dtor(return_value); RETURN_FALSE; } - RETVAL_ZVAL(&datetime_object, 0, 0); } /* }}} */ @@ -2615,18 +2615,16 @@ PHP_FUNCTION(date_create_from_format) zval *timezone_object = NULL; char *time_str = NULL, *format_str = NULL; size_t time_str_len = 0, format_str_len = 0; - zval datetime_object; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { RETURN_FALSE; } - php_date_instantiate(date_ce_date, &datetime_object); - if (!php_date_initialize(Z_PHPDATE_P(&datetime_object), time_str, time_str_len, format_str, timezone_object, 0)) { - zval_dtor(&datetime_object); + php_date_instantiate(date_ce_date, return_value); + if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, 0)) { + zval_ptr_dtor(return_value); RETURN_FALSE; } - RETVAL_ZVAL(&datetime_object, 0, 0); } /* }}} */ @@ -2638,18 +2636,16 @@ PHP_FUNCTION(date_create_immutable_from_format) zval *timezone_object = NULL; char *time_str = NULL, *format_str = NULL; size_t time_str_len = 0, format_str_len = 0; - zval datetime_object; if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { RETURN_FALSE; } - php_date_instantiate(date_ce_immutable, &datetime_object); - if (!php_date_initialize(Z_PHPDATE_P(&datetime_object), time_str, time_str_len, format_str, timezone_object, 0)) { - zval_dtor(&datetime_object); + php_date_instantiate(date_ce_immutable, return_value); + if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, 0)) { + zval_ptr_dtor(return_value); RETURN_FALSE; } - RETVAL_ZVAL(&datetime_object, 0, 0); } /* }}} */ @@ -3075,11 +3071,12 @@ PHP_FUNCTION(date_modify) RETURN_FALSE; } - if (php_date_modify(object, modify, modify_len)) { - RETURN_ZVAL(object, 1, 0); + if (!php_date_modify(object, modify, modify_len)) { + RETURN_FALSE; } - RETURN_FALSE; + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3096,11 +3093,11 @@ PHP_METHOD(DateTimeImmutable, modify) } date_clone_immutable(object, &new_object); - if (php_date_modify(&new_object, modify, modify_len)) { - RETURN_ZVAL(&new_object, 0, 1); + if (!php_date_modify(&new_object, modify, modify_len)) { + RETURN_FALSE; } - RETURN_FALSE; + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3133,7 +3130,8 @@ PHP_FUNCTION(date_add) php_date_add(object, interval, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3150,7 +3148,7 @@ PHP_METHOD(DateTimeImmutable, add) date_clone_immutable(object, &new_object); php_date_add(&new_object, interval, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3188,7 +3186,8 @@ PHP_FUNCTION(date_sub) php_date_sub(object, interval, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3205,7 +3204,7 @@ PHP_METHOD(DateTimeImmutable, sub) date_clone_immutable(object, &new_object); php_date_sub(&new_object, interval, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3290,7 +3289,8 @@ PHP_FUNCTION(date_timezone_set) php_date_timezone_set(object, timezone_object, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3308,7 +3308,7 @@ PHP_METHOD(DateTimeImmutable, setTimezone) date_clone_immutable(object, &new_object); php_date_timezone_set(&new_object, timezone_object, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3373,7 +3373,8 @@ PHP_FUNCTION(date_time_set) php_date_time_set(object, h, i, s, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3391,7 +3392,7 @@ PHP_METHOD(DateTimeImmutable, setTime) date_clone_immutable(object, &new_object); php_date_time_set(&new_object, h, i, s, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3421,7 +3422,8 @@ PHP_FUNCTION(date_date_set) php_date_date_set(object, y, m, d, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3439,7 +3441,7 @@ PHP_METHOD(DateTimeImmutable, setDate) date_clone_immutable(object, &new_object); php_date_date_set(&new_object, y, m, d, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3473,7 +3475,8 @@ PHP_FUNCTION(date_isodate_set) php_date_isodate_set(object, y, w, d, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3491,7 +3494,7 @@ PHP_METHOD(DateTimeImmutable, setISODate) date_clone_immutable(object, &new_object); php_date_isodate_set(&new_object, y, w, d, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ @@ -3519,7 +3522,8 @@ PHP_FUNCTION(date_timestamp_set) php_date_timestamp_set(object, timestamp, return_value); - RETURN_ZVAL(object, 1, 0); + Z_ADDREF_P(object); + ZVAL_COPY_VALUE(return_value, object); } /* }}} */ @@ -3537,7 +3541,7 @@ PHP_METHOD(DateTimeImmutable, setTimestamp) date_clone_immutable(object, &new_object); php_date_timestamp_set(&new_object, timestamp, return_value); - RETURN_ZVAL(&new_object, 0, 1); + ZVAL_COPY_VALUE(return_value, &new_object); } /* }}} */ diff --git a/ext/date/tests/009_win32.phpt b/ext/date/tests/009_win32.phpt index cbdc8b7c0a..3ad04f5849 100644 --- a/ext/date/tests/009_win32.phpt +++ b/ext/date/tests/009_win32.phpt @@ -4,11 +4,15 @@ strftime() and gmstrftime() tests <?php if (substr(PHP_OS, 0, 3) != 'WIN') die('skip only windows test.'); if (!function_exists('strftime')) die("skip, strftime not available"); +if (false === setlocale(LC_TIME, "en-us")) die("skip, couldn't set the locale to en-us"); ?> --FILE-- <?php date_default_timezone_set('Asia/Jerusalem'); +$loc = setlocale(LC_TIME, "0"); +setlocale(LC_TIME, "en-us"); + $t = mktime(0,0,0, 6, 27, 2006); var_dump(strftime()); @@ -32,19 +36,21 @@ var_dump(gmstrftime("%%q %%a", $t)); var_dump(gmstrftime("blah", $t)); echo "Done\n"; + +setlocale(LC_TIME, $loc); ?> --EXPECTF-- Warning: strftime() expects at least 1 parameter, 0 given in %s on line %d bool(false) bool(false) -string(%d) "Tue Tuesday Jun June 06/27/06 00:00:00 27 00 12 178 06 00 AM 00 26 26 2 06/27/06 00:00:00 06 2006 %s" +string(%d) "Tue Tuesday Jun June 6/27/2006 12:00:00 AM 27 00 12 178 06 00 AM 00 26 26 2 6/27/2006 12:00:00 AM 06 2006 %s" string(5) "%q %a" string(4) "blah" Warning: gmstrftime() expects at least 1 parameter, 0 given in %s on line %d bool(false) bool(false) -string(%d) "Mon Monday Jun June 06/26/06 21:00:00 26 21 09 177 06 00 PM 00 26 26 1 06/26/06 21:00:00 06 2006 %s" +string(%d) "Mon Monday Jun June 6/26/2006 9:00:00 PM 26 21 09 177 06 00 PM 00 26 26 1 6/26/2006 9:00:00 PM 06 2006 %s" string(5) "%q %a" string(4) "blah" Done diff --git a/ext/date/tests/bug55407.phpt b/ext/date/tests/bug55407.phpt index 00c3356461..925640554a 100644 --- a/ext/date/tests/bug55407.phpt +++ b/ext/date/tests/bug55407.phpt @@ -2,6 +2,8 @@ Bug #55407 (Impossible to prototype DateTime::createFromFormat) --INI-- error_reporting=-1 +--XFAIL-- +Bug #55407 --FILE-- <?php namespace melt\core; diff --git a/ext/date/tests/gmstrftime_variation11.phpt b/ext/date/tests/gmstrftime_variation11.phpt index 482ff6d2d7..f90e51ec16 100644 --- a/ext/date/tests/gmstrftime_variation11.phpt +++ b/ext/date/tests/gmstrftime_variation11.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking month related formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking month related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -32,6 +32,6 @@ var_dump( gmstrftime($format, $timestamp) ); *** Testing gmstrftime() : usage variation *** -- Testing gmstrftime() function with Abbreviated month name format %h -- -bool(false) -bool(false) +string(%d) "%s" +string(3) "Aug" ===DONE=== diff --git a/ext/date/tests/gmstrftime_variation13.phpt b/ext/date/tests/gmstrftime_variation13.phpt index 42f33f01ea..cccfa89d42 100644 --- a/ext/date/tests/gmstrftime_variation13.phpt +++ b/ext/date/tests/gmstrftime_variation13.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking date related formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking date related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); //array of values to iterate over @@ -43,18 +43,18 @@ foreach($inputs as $key =>$value) { *** Testing gmstrftime() : usage variation *** --Century number-- -bool(false) -bool(false) +string(2) "%d" +string(2) "20" --Month Date Year-- -bool(false) -bool(false) +string(%d) "%d/%d/%d" +string(8) "08/08/08" --Year with century-- -bool(false) -bool(false) +string(%d) "%d" +string(4) "2008" --Year without century-- -bool(false) -bool(false) +string(2) "%d" +string(2) "08" ===DONE=== diff --git a/ext/date/tests/gmstrftime_variation15.phpt b/ext/date/tests/gmstrftime_variation15.phpt index c0df364789..0a32b43322 100644 --- a/ext/date/tests/gmstrftime_variation15.phpt +++ b/ext/date/tests/gmstrftime_variation15.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking time related formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking time related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); //array of values to iterate over @@ -42,14 +42,14 @@ foreach($inputs as $key =>$value) { *** Testing gmstrftime() : usage variation *** --Time in a.m/p.m notation-- -bool(false) -bool(false) +string(%d) "%d:%d:%d %c%c" +string(11) "08:08:08 AM" --Time in 24 hour notation-- -bool(false) -bool(false) +string(%d) "%d:%d" +string(5) "08:08" --Current time %H:%M:%S format-- -bool(false) -bool(false) +string(%d) "%d:%d:%d" +string(8) "08:08:08" ===DONE=== diff --git a/ext/date/tests/gmstrftime_variation17.phpt b/ext/date/tests/gmstrftime_variation17.phpt index e3070a5144..5fa308f645 100644 --- a/ext/date/tests/gmstrftime_variation17.phpt +++ b/ext/date/tests/gmstrftime_variation17.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking day related formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking day related formats which was not supported on Windows before vc14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); echo "\n-- Testing gmstrftime() function with Day of the month as decimal single digit format --\n"; @@ -32,6 +32,6 @@ var_dump( gmstrftime($format, $timestamp) ); *** Testing gmstrftime() : usage variation *** -- Testing gmstrftime() function with Day of the month as decimal single digit format -- -bool(false) -bool(false) +string(2) "%A%d" +string(2) " 8" ===DONE=== diff --git a/ext/date/tests/gmstrftime_variation19.phpt b/ext/date/tests/gmstrftime_variation19.phpt index 3131e01ca1..9ba714750e 100644 --- a/ext/date/tests/gmstrftime_variation19.phpt +++ b/ext/date/tests/gmstrftime_variation19.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking newline and tab formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking newline and tab formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); //array of values to iterate over @@ -41,10 +41,12 @@ foreach($inputs as $key =>$value) { *** Testing gmstrftime() : usage variation *** --Newline character-- -bool(false) -bool(false) +string(1) " +" +string(1) " +" --Tab character-- -bool(false) -bool(false) +string(1) " " +string(1) " " ===DONE=== diff --git a/ext/date/tests/gmstrftime_variation21.phpt b/ext/date/tests/gmstrftime_variation21.phpt index 26ed157c3f..08ca2fc606 100644 --- a/ext/date/tests/gmstrftime_variation21.phpt +++ b/ext/date/tests/gmstrftime_variation21.phpt @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); //array of values to iterate over @@ -42,8 +42,8 @@ foreach($inputs as $key =>$value) { *** Testing gmstrftime() : usage variation *** --Preferred date and time representation-- -string(%d) "%d/%d/%d %d:%d:%d" -string(17) "08/08/08 08:08:08" +string(%d) "%s %s %d %d:%d:%d %d" +string(24) "Fri Aug 8 08:08:08 2008" --Preferred date representation-- string(%d) "%d/%d/%d" diff --git a/ext/date/tests/gmstrftime_variation9.phpt b/ext/date/tests/gmstrftime_variation9.phpt index 95b6c904f9..6ba33ac394 100644 --- a/ext/date/tests/gmstrftime_variation9.phpt +++ b/ext/date/tests/gmstrftime_variation9.phpt @@ -1,5 +1,5 @@ --TEST-- -Test gmstrftime() function : usage variation - Checking week related formats which are not supported on Windows. +Test gmstrftime() function : usage variation - Checking week related formats which was not supported on Windows before vc14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -18,7 +18,7 @@ echo "*** Testing gmstrftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) $timestamp = gmmktime(8, 8, 8, 8, 8, 2008); -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); //array of values to iterate over @@ -41,10 +41,10 @@ foreach($inputs as $key =>$value) { *** Testing gmstrftime() : usage variation *** --The ISO 8601:1988 week number-- -bool(false) -bool(false) +string(%d) "%d" +string(2) "32" --Weekday as decimal-- -bool(false) -bool(false) +string(1) "%d" +string(1) "5" ===DONE=== diff --git a/ext/date/tests/strftime_variation11.phpt b/ext/date/tests/strftime_variation11.phpt index a063f0ad20..08d5634daa 100644 --- a/ext/date/tests/strftime_variation11.phpt +++ b/ext/date/tests/strftime_variation11.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking month related formats which are not supported on Windows. +Test strftime() function : usage variation - Checking month related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -32,6 +32,6 @@ var_dump( strftime($format, $timestamp) ); *** Testing strftime() : usage variation *** -- Testing strftime() function with Abbreviated month name format %h -- -bool(false) -bool(false) +string(%d) "%s" +string(3) "Aug" ===DONE=== diff --git a/ext/date/tests/strftime_variation13.phpt b/ext/date/tests/strftime_variation13.phpt index 58ad283ffd..3218558184 100644 --- a/ext/date/tests/strftime_variation13.phpt +++ b/ext/date/tests/strftime_variation13.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking date related formats which are not supported on Windows. +Test strftime() function : usage variation - Checking date related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -43,18 +43,18 @@ foreach($inputs as $key =>$value) { *** Testing strftime() : usage variation *** --Century number-- -bool(false) -bool(false) +string(2) "20" +string(2) "20" --Month Date Year-- -bool(false) -bool(false) +string(%d) "%d/%d/%d" +string(8) "08/08/08" --Year with century-- -bool(false) -bool(false) +string(4) "%d" +string(4) "2008" --Year without century-- -bool(false) -bool(false) +string(2) "%d" +string(2) "08" ===DONE=== diff --git a/ext/date/tests/strftime_variation15.phpt b/ext/date/tests/strftime_variation15.phpt index 5b2946bf5d..962afd89c5 100644 --- a/ext/date/tests/strftime_variation15.phpt +++ b/ext/date/tests/strftime_variation15.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking time related formats which are not supported on Windows. +Test strftime() function : usage variation - Checking time related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -42,14 +42,14 @@ foreach($inputs as $key =>$value) { *** Testing strftime() : usage variation *** --Time in a.m/p.m notation-- -bool(false) -bool(false) +string(%d) "%d:%d:%d %s" +string(11) "08:08:08 AM" --Time in 24 hour notation-- -bool(false) -bool(false) +string(%d) "%d:%d" +string(5) "08:08" --Current time %H:%M:%S format-- -bool(false) -bool(false) +string(%d) "%d:%d:%d" +string(8) "08:08:08" ===DONE=== diff --git a/ext/date/tests/strftime_variation17.phpt b/ext/date/tests/strftime_variation17.phpt index 90b3269e83..66527e3436 100644 --- a/ext/date/tests/strftime_variation17.phpt +++ b/ext/date/tests/strftime_variation17.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking day related formats which are not supported on Windows. +Test strftime() function : usage variation - Checking day related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -31,6 +31,6 @@ var_dump( strftime($format, $timestamp) ); *** Testing strftime() : usage variation *** -- Testing strftime() function with Day of the month as decimal single digit format -- -bool(false) -bool(false) +string(%d) "%A%d" +string(2) " 8" ===DONE=== diff --git a/ext/date/tests/strftime_variation19.phpt b/ext/date/tests/strftime_variation19.phpt index b41607719c..f36d0a189e 100644 --- a/ext/date/tests/strftime_variation19.phpt +++ b/ext/date/tests/strftime_variation19.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking newline and tab formats which are not supported on Windows. +Test strftime() function : usage variation - Checking newline and tab formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -41,10 +41,12 @@ foreach($inputs as $key =>$value) { *** Testing strftime() : usage variation *** --Newline character-- -bool(false) -bool(false) +string(1) " +" +string(1) " +" --Tab character-- -bool(false) -bool(false) +string(1) " " +string(1) " " ===DONE=== diff --git a/ext/date/tests/strftime_variation21.phpt b/ext/date/tests/strftime_variation21.phpt index f6aed15eff..735659298a 100644 --- a/ext/date/tests/strftime_variation21.phpt +++ b/ext/date/tests/strftime_variation21.phpt @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -42,8 +42,8 @@ foreach($inputs as $key =>$value) { *** Testing strftime() : usage variation *** --Preferred date and time representation-- -string(%d) "%d/%d/%d %d:%d:%d" -string(17) "08/08/08 08:08:08" +string(%d) "%s %s %d %d:%d:%d %d" +string(24) "Fri Aug 8 08:08:08 2008" --Preferred date representation-- string(%d) "%d/%d/%d" diff --git a/ext/date/tests/strftime_variation9.phpt b/ext/date/tests/strftime_variation9.phpt index 23aa92d98d..eff030984f 100644 --- a/ext/date/tests/strftime_variation9.phpt +++ b/ext/date/tests/strftime_variation9.phpt @@ -1,5 +1,5 @@ --TEST-- -Test strftime() function : usage variation - Checking week related formats which are not supported on Windows. +Test strftime() function : usage variation - Checking week related formats which was not supported on Windows before VC14. --SKIPIF-- <?php if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { @@ -17,7 +17,7 @@ if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN') { echo "*** Testing strftime() : usage variation ***\n"; // Initialise function arguments not being substituted (if any) -setlocale(LC_ALL, "en_US"); +setlocale(LC_ALL, "C"); date_default_timezone_set("Asia/Calcutta"); $timestamp = mktime(8, 8, 8, 8, 8, 2008); @@ -41,10 +41,10 @@ foreach($inputs as $key =>$value) { *** Testing strftime() : usage variation *** --The ISO 8601:1988 week number-- -bool(false) -bool(false) +string(2) "%d" +string(2) "32" --Weekday as decimal-- -bool(false) -bool(false) +string(1) "%d" +string(1) "5" ===DONE=== diff --git a/ext/dba/config.w32 b/ext/dba/config.w32 index 4f3514e62f..4abba8dbf4 100644 --- a/ext/dba/config.w32 +++ b/ext/dba/config.w32 @@ -4,7 +4,7 @@ ARG_WITH("dba", "DBA support", "no"); if (PHP_DBA != "no") { - if (CHECK_LIB("libdb31s.lib", "dba", PHP_DBA) && + if (CHECK_LIB("libdb31s.lib;libdb61.lib", "dba", PHP_DBA) && CHECK_HEADER_ADD_INCLUDE("db.h", "CFLAGS_DBA")) { EXTENSION("dba", "dba.c dba_cdb.c dba_db1.c dba_db2.c dba_db3.c dba_dbm.c dba_flatfile.c dba_gdbm.c dba_ndbm.c dba_inifile.c"); ADD_SOURCES("ext/dba/libcdb", "cdb.c cdb_make.c uint32.c", "dba"); diff --git a/ext/dba/dba_db3.c b/ext/dba/dba_db3.c index 1b4629510b..a582ad5821 100644 --- a/ext/dba/dba_db3.c +++ b/ext/dba/dba_db3.c @@ -35,7 +35,11 @@ #include <db.h> #endif -static void php_dba_db3_errcall_fcn(const char *errpfx, char *msg) +static void php_dba_db3_errcall_fcn( +#if (DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)) + const DB_ENV *dbenv, +#endif + const char *errpfx, const char *msg) { php_error_docref(NULL, E_NOTICE, "%s%s", errpfx?errpfx:"", msg); @@ -90,7 +94,12 @@ DBA_OPEN_FUNC(db3) if ((err=db_create(&dbp, NULL, 0)) == 0) { dbp->set_errcall(dbp, php_dba_db3_errcall_fcn); - if ((err=dbp->open(dbp, info->path, NULL, type, gmode, filemode)) == 0) { + if( +#if (DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1)) + (err=dbp->open(dbp, 0, info->path, NULL, type, gmode, filemode)) == 0) { +#else + (err=dbp->open(dbp, info->path, NULL, type, gmode, filemode)) == 0) { +#endif dba_db3_data *data; data = pemalloc(sizeof(*data), info->flags&DBA_PERSISTENT); diff --git a/ext/dom/document.c b/ext/dom/document.c index d435b1c281..3e4e298654 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1562,7 +1562,7 @@ PHP_FUNCTION(dom_document_save) char *file; zend_long options = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &id, dom_document_class_entry, &file, &file_len, &options) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Op|l", &id, dom_document_class_entry, &file, &file_len, &options) == FAILURE) { return; } @@ -1793,7 +1793,7 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type int is_valid; char resolved_path[MAXPATHLEN + 1]; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Op|l", &id, dom_document_class_entry, &source, &source_len, &flags) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &id, dom_document_class_entry, &source, &source_len, &flags) == FAILURE) { return; } @@ -1806,7 +1806,11 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type switch (type) { case DOM_LOAD_FILE: - valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN ); + if (CHECK_NULL_PATH(source, source_len)) { + php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); + RETURN_FALSE; + } + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); if (!valid_file) { php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); RETURN_FALSE; @@ -1889,7 +1893,7 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ int is_valid; char resolved_path[MAXPATHLEN + 1]; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Op", &id, dom_document_class_entry, &source, &source_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &id, dom_document_class_entry, &source, &source_len) == FAILURE) { return; } @@ -1902,7 +1906,11 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ switch (type) { case DOM_LOAD_FILE: - valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN ); + if (CHECK_NULL_PATH(source, source_len)) { + php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source"); + RETURN_FALSE; + } + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC); if (!valid_file) { php_error_docref(NULL, E_WARNING, "Invalid RelaxNG file source"); RETURN_FALSE; @@ -1983,7 +1991,7 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ id = getThis(); - if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|l", &source, &source_len, &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &source, &source_len, &options) == FAILURE) { return; } @@ -1993,6 +2001,10 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ } if (mode == DOM_LOAD_FILE) { + if (CHECK_NULL_PATH(source, source_len)) { + php_error_docref(NULL, E_WARNING, "Invalid file source"); + RETURN_FALSE; + } ctxt = htmlCreateFileParserCtxt(source, NULL); } else { source_len = xmlStrlen((xmlChar *) source); @@ -2082,7 +2094,7 @@ PHP_FUNCTION(dom_document_save_html_file) char *file; const char *encoding; - if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &id, dom_document_class_entry, &file, &file_len) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Op", &id, dom_document_class_entry, &file, &file_len) == FAILURE) { return; } diff --git a/ext/dom/tests/DOMDocument_loadHTMLfile_error2.phpt b/ext/dom/tests/DOMDocument_loadHTMLfile_error2.phpt index 75004e2a74..e0d0923642 100644 --- a/ext/dom/tests/DOMDocument_loadHTMLfile_error2.phpt +++ b/ext/dom/tests/DOMDocument_loadHTMLfile_error2.phpt @@ -15,9 +15,9 @@ $result = $doc->loadHTMLFile(""); assert('$result === false'); $doc = new DOMDocument(); $result = $doc->loadHTMLFile("text.html\0something"); -assert('$result === null'); +assert('$result === false'); ?> --EXPECTF-- %r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): Empty string supplied as input %s -%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile() expects parameter 1 to be a valid path, string given %s +%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): Invalid file source %s diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 40df99df47..8663372de3 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -39,10 +39,6 @@ #include "fopen_wrappers.h" /* needed for is_url */ #include "Zend/zend_exceptions.h" -#ifndef _S_IFDIR -# define _S_IFDIR S_IFDIR -#endif - /* {{{ macros and type definitions */ typedef struct _php_fileinfo { zend_long options; diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 12828c676b..bb41b1a621 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -1837,8 +1837,6 @@ ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path) for (ptr = data->buf; rcvd; rcvd--, ptr++) { if (*ptr == '\n' && lastch == '\r') { lines++; - } else { - size++; } lastch = *ptr; } diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index e643e52736..36688e127a 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -35,9 +35,6 @@ PHP_ARG_WITH(xpm-dir, for the location of libXpm, PHP_ARG_WITH(freetype-dir, for FreeType 2, [ --with-freetype-dir[=DIR] GD: Set the path to FreeType 2 install prefix], no, no) -PHP_ARG_WITH(t1lib, for T1lib support, -[ --with-t1lib[=DIR] GD: Include T1lib support. T1lib version >= 5.0.0 required], no, no) - PHP_ARG_ENABLE(gd-native-ttf, whether to enable truetype string function in GD, [ --enable-gd-native-ttf GD: Enable TrueType string function], no, no) @@ -209,30 +206,6 @@ AC_DEFUN([PHP_GD_FREETYPE2],[ fi ]) -AC_DEFUN([PHP_GD_T1LIB],[ - if test "$PHP_T1LIB" != "no"; then - - for i in $PHP_T1LIB /usr/local /usr; do - test -f "$i/include/t1lib.h" && GD_T1_DIR=$i && break - done - - if test -z "$GD_T1_DIR"; then - AC_MSG_ERROR([Your t1lib distribution is not installed correctly. Please reinstall it.]) - fi - - PHP_CHECK_LIBRARY(t1, T1_StrError, - [ - AC_DEFINE(HAVE_LIBT1,1,[ ]) - PHP_ADD_INCLUDE($GD_T1_DIR/include) - PHP_ADD_LIBRARY_WITH_PATH(t1, $GD_T1_DIR/$PHP_LIBDIR, GD_SHARED_LIBADD) - ],[ - AC_MSG_ERROR([Problem with libt1.(a|so). Please check config.log for more information.]) - ],[ - -L$GD_T1_DIR/$PHP_LIBDIR - ]) - fi -]) - AC_DEFUN([PHP_GD_TTSTR],[ if test "$PHP_GD_NATIVE_TTF" = "yes"; then AC_DEFINE(USE_GD_IMGSTRTTF, 1, [ ]) @@ -274,7 +247,6 @@ dnl Various checks for GD features PHP_GD_PNG PHP_GD_XPM PHP_GD_FREETYPE2 - PHP_GD_T1LIB PHP_GD_JISX0208 fi @@ -343,7 +315,6 @@ dnl Various checks for GD features PHP_GD_PNG PHP_GD_XPM PHP_GD_FREETYPE2 - PHP_GD_T1LIB dnl Header path for i in include/gd include gd ""; do diff --git a/ext/gd/config.w32 b/ext/gd/config.w32 index da6d0d2ff5..a51f027d38 100644 --- a/ext/gd/config.w32 +++ b/ext/gd/config.w32 @@ -2,7 +2,6 @@ // vim:ft=javascript ARG_WITH("gd", "Bundled GD support", "yes,shared"); -ARG_WITH("t1lib", "t1lib support", "yes"); ARG_WITH("libvpx", "vpx support", "yes"); if (PHP_GD != "no") { @@ -21,15 +20,6 @@ if (PHP_GD != "no") { CHECK_LIB("libXpm_a.lib", "gd", PHP_GD) && CHECK_HEADER_ADD_INCLUDE("xpm.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\X11") ) { - if (PHP_T1LIB != "no") { - if (CHECK_LIB("T1_StaticMD.lib", "gd", PHP_GD) && - CHECK_HEADER_ADD_INCLUDE("t1lib.h", "CFLAGS_GD", PHP_GD + ";" + PHP_PHP_BUILD + "\\include\\t1lib") - ) { - ADD_FLAG("CFLAGS_GD", "/D HAVE_LIBT1"); - } else { - WARNING("t1lib not enabled; libraries and headers not found"); - } - } if (PHP_LIBVPX != "no") { if (CHECK_LIB("vpxmt.lib", "gd", PHP_GD) && diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 9105fe6609..70f98f6a50 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -61,12 +61,6 @@ static int le_gd, le_gd_font; -#if HAVE_LIBT1 -#include <t1lib.h> -static int le_ps_font, le_ps_enc; -static void php_free_ps_font(zend_resource *rsrc); -static void php_free_ps_enc(zend_resource *rsrc); -#endif #include <gd.h> #include <gdfontt.h> /* 1 Tiny font */ @@ -730,61 +724,6 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagettftext, 0) ZEND_END_ARG_INFO() #endif -#ifdef HAVE_LIBT1 -ZEND_BEGIN_ARG_INFO(arginfo_imagepsloadfont, 0) - ZEND_ARG_INFO(0, pathname) -ZEND_END_ARG_INFO() - -/* -ZEND_BEGIN_ARG_INFO(arginfo_imagepscopyfont, 0) - ZEND_ARG_INFO(0, font_index) -ZEND_END_ARG_INFO() -*/ - -ZEND_BEGIN_ARG_INFO(arginfo_imagepsfreefont, 0) - ZEND_ARG_INFO(0, font_index) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_imagepsencodefont, 0) - ZEND_ARG_INFO(0, font_index) - ZEND_ARG_INFO(0, filename) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_imagepsextendfont, 0) - ZEND_ARG_INFO(0, font_index) - ZEND_ARG_INFO(0, extend) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_imagepsslantfont, 0) - ZEND_ARG_INFO(0, font_index) - ZEND_ARG_INFO(0, slant) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepstext, 0, 0, 8) - ZEND_ARG_INFO(0, im) - ZEND_ARG_INFO(0, text) - ZEND_ARG_INFO(0, font) - ZEND_ARG_INFO(0, size) - ZEND_ARG_INFO(0, foreground) - ZEND_ARG_INFO(0, background) - ZEND_ARG_INFO(0, xcoord) - ZEND_ARG_INFO(0, ycoord) - ZEND_ARG_INFO(0, space) - ZEND_ARG_INFO(0, tightness) - ZEND_ARG_INFO(0, angle) - ZEND_ARG_INFO(0, antialias) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_imagepsbbox, 0, 0, 3) - ZEND_ARG_INFO(0, text) - ZEND_ARG_INFO(0, font) - ZEND_ARG_INFO(0, size) - ZEND_ARG_INFO(0, space) - ZEND_ARG_INFO(0, tightness) - ZEND_ARG_INFO(0, angle) -ZEND_END_ARG_INFO() -#endif - ZEND_BEGIN_ARG_INFO_EX(arginfo_image2wbmp, 0, 0, 1) ZEND_ARG_INFO(0, im) ZEND_ARG_INFO(0, filename) @@ -1005,18 +944,6 @@ const zend_function_entry gd_functions[] = { #endif #endif -#ifdef HAVE_LIBT1 - PHP_FE(imagepsloadfont, arginfo_imagepsloadfont) - /* - PHP_FE(imagepscopyfont, arginfo_imagepscopyfont) - */ - PHP_FE(imagepsfreefont, arginfo_imagepsfreefont) - PHP_FE(imagepsencodefont, arginfo_imagepsencodefont) - PHP_FE(imagepsextendfont, arginfo_imagepsextendfont) - PHP_FE(imagepsslantfont, arginfo_imagepsslantfont) - PHP_FE(imagepstext, arginfo_imagepstext) - PHP_FE(imagepsbbox, arginfo_imagepsbbox) -#endif PHP_FE(imagetypes, arginfo_imagetypes) #if defined(HAVE_GD_JPG) @@ -1044,11 +971,7 @@ zend_module_entry gd_module_entry = { "gd", gd_functions, PHP_MINIT(gd), -#if HAVE_LIBT1 - PHP_MSHUTDOWN(gd), -#else NULL, -#endif NULL, #if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_RSHUTDOWN(gd), @@ -1103,22 +1026,6 @@ void php_gd_error_method(int type, const char *format, va_list args) /* }}} */ #endif -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -#if HAVE_LIBT1 -PHP_MSHUTDOWN_FUNCTION(gd) -{ - T1_CloseLib(); -#if HAVE_GD_BUNDLED && HAVE_LIBFREETYPE - gdFontCacheMutexShutdown(); -#endif - UNREGISTER_INI_ENTRIES(); - return SUCCESS; -} -#endif -/* }}} */ - - /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(gd) @@ -1129,13 +1036,6 @@ PHP_MINIT_FUNCTION(gd) #if HAVE_GD_BUNDLED && HAVE_LIBFREETYPE gdFontCacheMutexSetup(); #endif -#if HAVE_LIBT1 - T1_SetBitmapPad(8); - T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE); - T1_SetLogLevel(T1LOG_DEBUG); - le_ps_font = zend_register_list_destructors_ex(php_free_ps_font, NULL, "gd PS font", module_number); - le_ps_enc = zend_register_list_destructors_ex(php_free_ps_enc, NULL, "gd PS encoding", module_number); -#endif #ifndef HAVE_GD_BUNDLED gdSetErrorMethod(php_gd_error_method); #endif @@ -1318,10 +1218,6 @@ PHP_MINFO_FUNCTION(gd) #endif #endif -#ifdef HAVE_LIBT1 - php_info_print_table_row(2, "T1Lib Support", "enabled"); -#endif - php_info_print_table_row(2, "GIF Read Support", "enabled"); php_info_print_table_row(2, "GIF Create Support", "enabled"); @@ -1379,12 +1275,6 @@ PHP_FUNCTION(gd_info) #else add_assoc_bool(return_value, "FreeType Support", 0); #endif - -#ifdef HAVE_LIBT1 - add_assoc_bool(return_value, "T1Lib Support", 1); -#else - add_assoc_bool(return_value, "T1Lib Support", 0); -#endif add_assoc_bool(return_value, "GIF Read Support", 1); add_assoc_bool(return_value, "GIF Create Support", 1); #ifdef HAVE_GD_JPG @@ -4011,475 +3901,6 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode, int /* }}} */ #endif /* ENABLE_GD_TTF */ -#if HAVE_LIBT1 - -/* {{{ php_free_ps_font - */ -static void php_free_ps_font(zend_resource *rsrc) -{ - int *font = (int *)rsrc->ptr; - - T1_DeleteFont(*font); - efree(font); -} -/* }}} */ - -/* {{{ php_free_ps_enc - */ -static void php_free_ps_enc(zend_resource *rsrc) -{ - char **enc = (char **)rsrc->ptr; - - T1_DeleteEncoding(enc); -} -/* }}} */ - -/* {{{ proto resource imagepsloadfont(string pathname) - Load a new font from specified file */ -PHP_FUNCTION(imagepsloadfont) -{ - zend_string *file; - int f_ind, *font; -#ifdef PHP_WIN32 - zend_stat_t st; -#endif - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "P", &file) == FAILURE) { - return; - } - -#ifdef PHP_WIN32 - if (VCWD_STAT(file->val, &st) < 0) { - php_error_docref(NULL, E_WARNING, "Font file not found (%s)", file); - RETURN_FALSE; - } -#endif - - f_ind = T1_AddFont(file->val); - - if (f_ind < 0) { - php_error_docref(NULL, E_WARNING, "T1Lib Error (%i): %s", f_ind, T1_StrError(f_ind)); - RETURN_FALSE; - } - - if (T1_LoadFont(f_ind)) { - php_error_docref(NULL, E_WARNING, "Couldn't load the font"); - RETURN_FALSE; - } - - font = (int *) emalloc(sizeof(int)); - *font = f_ind; - RETURN_RES(zend_register_resource(font, le_gd)); -} -/* }}} */ - -/* {{{ proto int imagepscopyfont(int font_index) - Make a copy of a font for purposes like extending or reenconding */ -/* The function in t1lib which this function uses seem to be buggy... -PHP_FUNCTION(imagepscopyfont) -{ - int l_ind, type; - gd_ps_font *nf_ind, *of_ind; - zend_long fnt; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fnt) == FAILURE) { - return; - } - - of_ind = zend_list_find(fnt, &type); - - if (type != le_ps_font) { - php_error_docref(NULL, E_WARNING, "%ld is not a Type 1 font index", fnt); - RETURN_FALSE; - } - - nf_ind = emalloc(sizeof(gd_ps_font)); - nf_ind->font_id = T1_CopyFont(of_ind->font_id); - - if (nf_ind->font_id < 0) { - l_ind = nf_ind->font_id; - efree(nf_ind); - switch (l_ind) { - case -1: - php_error_docref(NULL, E_WARNING, "FontID %d is not loaded in memory", l_ind); - RETURN_FALSE; - break; - case -2: - php_error_docref(NULL, E_WARNING, "Tried to copy a logical font"); - RETURN_FALSE; - break; - case -3: - php_error_docref(NULL, E_WARNING, "Memory allocation fault in t1lib"); - RETURN_FALSE; - break; - default: - php_error_docref(NULL, E_WARNING, "An unknown error occurred in t1lib"); - RETURN_FALSE; - break; - } - } - - nf_ind->extend = 1; - l_ind = zend_list_insert(nf_ind, le_ps_font); - RETURN_LONG(l_ind); -} -*/ -/* }}} */ - -/* {{{ proto bool imagepsfreefont(resource font_index) - Free memory used by a font */ -PHP_FUNCTION(imagepsfreefont) -{ - zval *fnt; - int *f_ind; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &fnt) == FAILURE) { - return; - } - - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - - zend_list_close(Z_RES_P(fnt)); - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool imagepsencodefont(resource font_index, string filename) - To change a fonts character encoding vector */ -PHP_FUNCTION(imagepsencodefont) -{ - zval *fnt; - char *enc, **enc_vector; - size_t enc_len; - int *f_ind; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp", &fnt, &enc, &enc_len) == FAILURE) { - return; - } - - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - - if ((enc_vector = T1_LoadEncoding(enc)) == NULL) { - php_error_docref(NULL, E_WARNING, "Couldn't load encoding vector from %s", enc); - RETURN_FALSE; - } - - T1_DeleteAllSizes(*f_ind); - if (T1_ReencodeFont(*f_ind, enc_vector)) { - T1_DeleteEncoding(enc_vector); - php_error_docref(NULL, E_WARNING, "Couldn't re-encode font"); - RETURN_FALSE; - } - - zend_list_insert(enc_vector, le_ps_enc); - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool imagepsextendfont(resource font_index, float extend) - Extend or or condense (if extend < 1) a font */ -PHP_FUNCTION(imagepsextendfont) -{ - zval *fnt; - double ext; - int *f_ind; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rd", &fnt, &ext) == FAILURE) { - return; - } - - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - - T1_DeleteAllSizes(*f_ind); - - if (ext <= 0) { - php_error_docref(NULL, E_WARNING, "Second parameter %F out of range (must be > 0)", ext); - RETURN_FALSE; - } - - if (T1_ExtendFont(*f_ind, ext) != 0) { - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto bool imagepsslantfont(resource font_index, float slant) - Slant a font */ -PHP_FUNCTION(imagepsslantfont) -{ - zval *fnt; - double slt; - int *f_ind; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rd", &fnt, &slt) == FAILURE) { - return; - } - - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - - if (T1_SlantFont(*f_ind, slt) != 0) { - RETURN_FALSE; - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto array imagepstext(resource image, string text, resource font, int size, int foreground, int background, int xcoord, int ycoord [, int space [, int tightness [, float angle [, int antialias]) - Rasterize a string over an image */ -PHP_FUNCTION(imagepstext) -{ - zval *img, *fnt; - int i, j; - zend_long _fg, _bg, x, y, size, space = 0, aa_steps = 4, width = 0; - int *f_ind; - int h_lines, v_lines, c_ind; - int rd, gr, bl, fg_rd, fg_gr, fg_bl, bg_rd, bg_gr, bg_bl; - int fg_al, bg_al, al; - int aa[16]; - int amount_kern, add_width; - double angle = 0.0, extend; - unsigned long aa_greys[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - gdImagePtr bg_img; - GLYPH *str_img; - T1_OUTLINE *char_path, *str_path; - T1_TMATRIX *transform = NULL; - zend_string *str; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSrlllll|lldl", &img, &str, &fnt, &size, &_fg, &_bg, &x, &y, &space, &width, &angle, &aa_steps) == FAILURE) { - return; - } - - if (aa_steps != 4 && aa_steps != 16) { - php_error_docref(NULL, E_WARNING, "Antialias steps must be 4 or 16"); - RETURN_FALSE; - } - - if ((bg_img = (gdImagePtr)zend_fetch_resource(Z_RES_P(img), "Image", le_gd)) == NULL) { - RETURN_FALSE; - } - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - - /* Ensure that the provided colors are valid */ - if (_fg < 0 || (!gdImageTrueColor(bg_img) && _fg > gdImageColorsTotal(bg_img))) { - php_error_docref(NULL, E_WARNING, "Foreground color index %pd out of range", _fg); - RETURN_FALSE; - } - - if (_bg < 0 || (!gdImageTrueColor(bg_img) && _fg > gdImageColorsTotal(bg_img))) { - php_error_docref(NULL, E_WARNING, "Background color index %pd out of range", _bg); - RETURN_FALSE; - } - - fg_rd = gdImageRed (bg_img, _fg); - fg_gr = gdImageGreen(bg_img, _fg); - fg_bl = gdImageBlue (bg_img, _fg); - fg_al = gdImageAlpha(bg_img, _fg); - - bg_rd = gdImageRed (bg_img, _bg); - bg_gr = gdImageGreen(bg_img, _bg); - bg_bl = gdImageBlue (bg_img, _bg); - bg_al = gdImageAlpha(bg_img, _bg); - - for (i = 0; i < aa_steps; i++) { - rd = bg_rd + (double) (fg_rd - bg_rd) / aa_steps * (i + 1); - gr = bg_gr + (double) (fg_gr - bg_gr) / aa_steps * (i + 1); - bl = bg_bl + (double) (fg_bl - bg_bl) / aa_steps * (i + 1); - al = bg_al + (double) (fg_al - bg_al) / aa_steps * (i + 1); - aa[i] = gdImageColorResolveAlpha(bg_img, rd, gr, bl, al); - } - - T1_AASetBitsPerPixel(8); - - switch (aa_steps) { - case 4: - T1_AASetGrayValues(0, 1, 2, 3, 4); - T1_AASetLevel(T1_AA_LOW); - break; - case 16: - T1_AAHSetGrayValues(aa_greys); - T1_AASetLevel(T1_AA_HIGH); - break; - default: - php_error_docref(NULL, E_WARNING, "Invalid value %pd as number of steps for antialiasing", aa_steps); - RETURN_FALSE; - } - - if (angle) { - transform = T1_RotateMatrix(NULL, angle); - } - - if (width) { - extend = T1_GetExtend(*f_ind); - str_path = T1_GetCharOutline(*f_ind, str->val[0], size, transform); - - if (!str_path) { - if (T1_errno) { - php_error_docref(NULL, E_WARNING, "T1Lib Error: %s", T1_StrError(T1_errno)); - } - RETURN_FALSE; - } - - for (i = 1; i < str->len; i++) { - amount_kern = (int) T1_GetKerning(*f_ind, str->val[i - 1], str->val[i]); - amount_kern += str->val[i - 1] == ' ' ? space : 0; - add_width = (int) (amount_kern + width) / extend; - - char_path = T1_GetMoveOutline(*f_ind, add_width, 0, 0, size, transform); - str_path = T1_ConcatOutlines(str_path, char_path); - - char_path = T1_GetCharOutline(*f_ind, str->val[i], size, transform); - str_path = T1_ConcatOutlines(str_path, char_path); - } - str_img = T1_AAFillOutline(str_path, 0); - } else { - str_img = T1_AASetString(*f_ind, str->val, str->len, space, T1_KERNING, size, transform); - } - if (T1_errno) { - php_error_docref(NULL, E_WARNING, "T1Lib Error: %s", T1_StrError(T1_errno)); - RETURN_FALSE; - } - - h_lines = str_img->metrics.ascent - str_img->metrics.descent; - v_lines = str_img->metrics.rightSideBearing - str_img->metrics.leftSideBearing; - - for (i = 0; i < v_lines; i++) { - for (j = 0; j < h_lines; j++) { - switch (str_img->bits[j * v_lines + i]) { - case 0: - break; - default: - c_ind = aa[str_img->bits[j * v_lines + i] - 1]; - gdImageSetPixel(bg_img, x + str_img->metrics.leftSideBearing + i, y - str_img->metrics.ascent + j, c_ind); - break; - } - } - } - - array_init(return_value); - - add_next_index_long(return_value, str_img->metrics.leftSideBearing); - add_next_index_long(return_value, str_img->metrics.descent); - add_next_index_long(return_value, str_img->metrics.rightSideBearing); - add_next_index_long(return_value, str_img->metrics.ascent); -} -/* }}} */ - -/* {{{ proto array imagepsbbox(string text, resource font, int size [, int space, int tightness, float angle]) - Return the bounding box needed by a string if rasterized */ -PHP_FUNCTION(imagepsbbox) -{ - zval *fnt; - zend_long sz = 0, sp = 0, wd = 0; - zend_string *str; - int i, space = 0, add_width = 0, char_width, amount_kern; - int cur_x, cur_y, dx, dy; - int x1, y1, x2, y2, x3, y3, x4, y4; - int *f_ind; - int per_char = 0; - int argc = ZEND_NUM_ARGS(); - double angle = 0, sin_a = 0, cos_a = 0; - BBox char_bbox, str_bbox = {0, 0, 0, 0}; - - if (argc != 3 && argc != 6) { - ZEND_WRONG_PARAM_COUNT(); - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "Srl|lld", &str, &fnt, &sz, &sp, &wd, &angle) == FAILURE) { - return; - } - - if (argc == 6) { - space = sp; - add_width = wd; - angle = angle * M_PI / 180; - sin_a = sin(angle); - cos_a = cos(angle); - per_char = add_width || angle ? 1 : 0; - } - - if ((f_ind = (int *)zend_fetch_resource(Z_RES_P(fnt), "Type 1 font", le_ps_font)) == NULL) { - RETURN_FALSE; - } - -#define max(a, b) (a > b ? a : b) -#define min(a, b) (a < b ? a : b) -#define new_x(a, b) (int) ((a) * cos_a - (b) * sin_a) -#define new_y(a, b) (int) ((a) * sin_a + (b) * cos_a) - - if (per_char) { - space += T1_GetCharWidth(*f_ind, ' '); - cur_x = cur_y = 0; - - for (i = 0; i < str->len; i++) { - if (str->val[i] == ' ') { - char_bbox.llx = char_bbox.lly = char_bbox.ury = 0; - char_bbox.urx = char_width = space; - } else { - char_bbox = T1_GetCharBBox(*f_ind, str->val[i]); - char_width = T1_GetCharWidth(*f_ind, str->val[i]); - } - amount_kern = i ? T1_GetKerning(*f_ind, str->val[i - 1], str->val[i]) : 0; - - /* Transfer character bounding box to right place */ - x1 = new_x(char_bbox.llx, char_bbox.lly) + cur_x; - y1 = new_y(char_bbox.llx, char_bbox.lly) + cur_y; - x2 = new_x(char_bbox.llx, char_bbox.ury) + cur_x; - y2 = new_y(char_bbox.llx, char_bbox.ury) + cur_y; - x3 = new_x(char_bbox.urx, char_bbox.ury) + cur_x; - y3 = new_y(char_bbox.urx, char_bbox.ury) + cur_y; - x4 = new_x(char_bbox.urx, char_bbox.lly) + cur_x; - y4 = new_y(char_bbox.urx, char_bbox.lly) + cur_y; - - /* Find min & max values and compare them with current bounding box */ - str_bbox.llx = min(str_bbox.llx, min(x1, min(x2, min(x3, x4)))); - str_bbox.lly = min(str_bbox.lly, min(y1, min(y2, min(y3, y4)))); - str_bbox.urx = max(str_bbox.urx, max(x1, max(x2, max(x3, x4)))); - str_bbox.ury = max(str_bbox.ury, max(y1, max(y2, max(y3, y4)))); - - /* Move to the next base point */ - dx = new_x(char_width + add_width + amount_kern, 0); - dy = new_y(char_width + add_width + amount_kern, 0); - cur_x += dx; - cur_y += dy; - /* - printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", x1, y1, x2, y2, x3, y3, x4, y4, char_bbox.llx, char_bbox.lly, char_bbox.urx, char_bbox.ury, char_width, amount_kern, cur_x, cur_y, dx, dy); - */ - } - - } else { - str_bbox = T1_GetStringBBox(*f_ind, str->val, str->len, space, T1_KERNING); - } - - if (T1_errno) { - RETURN_FALSE; - } - - array_init(return_value); - /* - printf("%d %d %d %d\n", str_bbox.llx, str_bbox.lly, str_bbox.urx, str_bbox.ury); - */ - add_next_index_long(return_value, (int) ceil(((double) str_bbox.llx)*sz/1000)); - add_next_index_long(return_value, (int) ceil(((double) str_bbox.lly)*sz/1000)); - add_next_index_long(return_value, (int) ceil(((double) str_bbox.urx)*sz/1000)); - add_next_index_long(return_value, (int) ceil(((double) str_bbox.ury)*sz/1000)); -} -/* }}} */ -#endif - /* {{{ proto bool image2wbmp(resource im [, string filename [, int threshold]]) Output WBMP image to browser or file */ PHP_FUNCTION(image2wbmp) diff --git a/ext/gd/libgd/webpimg.c b/ext/gd/libgd/webpimg.c index 1f1c02322e..9d801a29a3 100644 --- a/ext/gd/libgd/webpimg.c +++ b/ext/gd/libgd/webpimg.c @@ -706,14 +706,14 @@ static WebPResult VPXEncode(const uint8* Y, codec_ctl(&enc, VP8E_SET_STATIC_THRESHOLD, 0); codec_ctl(&enc, VP8E_SET_TOKEN_PARTITIONS, 2); - vpx_img_wrap(&img, IMG_FMT_I420, + vpx_img_wrap(&img, VPX_IMG_FMT_I420, y_width, y_height, 16, (uint8*)(Y)); - img.planes[PLANE_Y] = (uint8*)(Y); - img.planes[PLANE_U] = (uint8*)(U); - img.planes[PLANE_V] = (uint8*)(V); - img.stride[PLANE_Y] = y_stride; - img.stride[PLANE_U] = uv_stride; - img.stride[PLANE_V] = uv_stride; + img.planes[VPX_PLANE_Y] = (uint8*)(Y); + img.planes[VPX_PLANE_U] = (uint8*)(U); + img.planes[VPX_PLANE_V] = (uint8*)(V); + img.stride[VPX_PLANE_Y] = y_stride; + img.stride[VPX_PLANE_U] = uv_stride; + img.stride[VPX_PLANE_V] = uv_stride; res = vpx_codec_encode(&enc, &img, 0, 1, 0, VPX_DL_BEST_QUALITY); diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h index 2af7887ebc..f02c6aba34 100644 --- a/ext/gd/php_gd.h +++ b/ext/gd/php_gd.h @@ -70,9 +70,6 @@ extern zend_module_entry gd_module_entry; /* gd.c functions */ PHP_MINFO_FUNCTION(gd); PHP_MINIT_FUNCTION(gd); -#if HAVE_LIBT1 -PHP_MSHUTDOWN_FUNCTION(gd); -#endif #if HAVE_GD_FREETYPE && HAVE_LIBFREETYPE PHP_RSHUTDOWN_FUNCTION(gd); #endif diff --git a/ext/gd/tests/gd_info_variation1.phpt b/ext/gd/tests/gd_info_variation1.phpt index a725f6554d..1810a82f85 100644 --- a/ext/gd/tests/gd_info_variation1.phpt +++ b/ext/gd/tests/gd_info_variation1.phpt @@ -28,8 +28,6 @@ array(%d) { string(%d) %a ["FreeType Support"]=> bool%a - ["T1Lib Support"]=> - bool%a ["GIF Read Support"]=> bool%a ["GIF Create Support"]=> @@ -47,4 +45,4 @@ array(%d) { ["JIS-mapped Japanese Font Support"]=> bool%a } -===DONE===
\ No newline at end of file +===DONE=== diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 1994052686..663bbc96e4 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -1837,6 +1837,7 @@ ZEND_FUNCTION(gmp_random_range) { zval *min_arg, *max_arg; mpz_ptr gmpnum_min, gmpnum_max, gmpnum_result; + mpz_t gmpnum_range; gmp_temp_t temp_a, temp_b; if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &min_arg, &max_arg) == FAILURE) { @@ -1855,22 +1856,23 @@ ZEND_FUNCTION(gmp_random_range) } INIT_GMP_RETVAL(gmpnum_result); + mpz_init(gmpnum_range); - if (Z_LVAL_P(min_arg)) { - mpz_sub_ui(gmpnum_max, gmpnum_max, Z_LVAL_P(min_arg)); + if (Z_LVAL_P(min_arg) != 0) { + mpz_sub_ui(gmpnum_range, gmpnum_max, Z_LVAL_P(min_arg) - 1); + } else { + mpz_add_ui(gmpnum_range, gmpnum_max, 1); } - mpz_add_ui(gmpnum_max, gmpnum_max, 1); - mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_max); + mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range); - if (Z_LVAL_P(min_arg)) { + if (Z_LVAL_P(min_arg) != 0) { mpz_add_ui(gmpnum_result, gmpnum_result, Z_LVAL_P(min_arg)); } + mpz_clear(gmpnum_range); FREE_GMP_TEMP(temp_a); - - } - else { + } else { FETCH_GMP_ZVAL_DEP(gmpnum_min, min_arg, temp_b, temp_a); if (mpz_cmp(gmpnum_max, gmpnum_min) <= 0) { @@ -1881,12 +1883,14 @@ ZEND_FUNCTION(gmp_random_range) } INIT_GMP_RETVAL(gmpnum_result); + mpz_init(gmpnum_range); - mpz_sub(gmpnum_max, gmpnum_max, gmpnum_min); - mpz_add_ui(gmpnum_max, gmpnum_max, 1); - mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_max); + mpz_sub(gmpnum_range, gmpnum_max, gmpnum_min); + mpz_add_ui(gmpnum_range, gmpnum_range, 1); + mpz_urandomm(gmpnum_result, GMPG(rand_state), gmpnum_range); mpz_add(gmpnum_result, gmpnum_result, gmpnum_min); + mpz_clear(gmpnum_range); FREE_GMP_TEMP(temp_b); FREE_GMP_TEMP(temp_a); } diff --git a/ext/gmp/tests/bug69803.phpt b/ext/gmp/tests/bug69803.phpt new file mode 100644 index 0000000000..e158cc5c0c --- /dev/null +++ b/ext/gmp/tests/bug69803.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #69803: gmp_random_range() modifies second parameter if GMP number +--FILE-- +<?php + +$a = gmp_init(100); +$b = gmp_init(200); +echo $a . ", ", $b . "\n"; +gmp_random_range($a, $b); +echo $a . ", ", $b . "\n"; + +$b = gmp_init(200); +echo $a . ", ", $b . "\n"; +gmp_random_range(100, $b); +echo $a . ", ", $b . "\n"; + +?> +--EXPECT-- +100, 200 +100, 200 +100, 200 +100, 200 diff --git a/ext/gmp/tests/gmp_random_range.phpt b/ext/gmp/tests/gmp_random_range.phpt index db2ece61c5..654ffbefb3 100644 --- a/ext/gmp/tests/gmp_random_range.phpt +++ b/ext/gmp/tests/gmp_random_range.phpt @@ -5,8 +5,8 @@ gmp_random_range() basic tests --FILE-- <?php -$minusTen = gmp_init(-1); -$plusTen = gmp_init(1); +$minusTen = gmp_init(-10); +$plusTen = gmp_init(10); $zero = gmp_init(0); var_dump(gmp_random_range()); diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 0510d3749c..d72b88c879 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -794,6 +794,9 @@ static void php_hash_dtor(zend_resource *rsrc) /* {{{ */ #ifdef PHP_MHASH_BC +#if 0 +/* See #69823, we should not insert module into module_registry while doing startup */ + PHP_MINFO_FUNCTION(mhash) { php_info_print_table_start(); @@ -814,6 +817,7 @@ zend_module_entry mhash_module_entry = { PHP_MHASH_VERSION, STANDARD_MODULE_PROPERTIES, }; +#endif static void mhash_init(INIT_FUNC_ARGS) { @@ -830,7 +834,8 @@ static void mhash_init(INIT_FUNC_ARGS) len = slprintf(buf, 127, "MHASH_%s", algorithm.mhash_name, strlen(algorithm.mhash_name)); zend_register_long_constant(buf, len, algorithm.value, CONST_CS | CONST_PERSISTENT, module_number); } - zend_register_internal_module(&mhash_module_entry); + + /* TODO: this cause #69823 zend_register_internal_module(&mhash_module_entry); */ } /* {{{ proto string mhash(int hash, string data [, string key]) @@ -1090,6 +1095,14 @@ PHP_MINFO_FUNCTION(hash) php_info_print_table_row(2, "hash support", "enabled"); php_info_print_table_row(2, "Hashing Engines", buffer); php_info_print_table_end(); + +#ifdef PHP_MHASH_BC + php_info_print_table_start(); + php_info_print_table_row(2, "MHASH support", "Enabled"); + php_info_print_table_row(2, "MHASH API Version", "Emulated Support"); + php_info_print_table_end(); +#endif + } /* }}} */ diff --git a/ext/hash/tests/skip_mhash.inc b/ext/hash/tests/skip_mhash.inc index 17e5fe7f9e..608b557b28 100644 --- a/ext/hash/tests/skip_mhash.inc +++ b/ext/hash/tests/skip_mhash.inc @@ -1,5 +1,5 @@ <?php -if (!extension_loaded("mhash") || !function_exists("mhash")) { +if (!function_exists("mhash")) { die("skip mhash extension is not available"); } ?> diff --git a/ext/iconv/config.m4 b/ext/iconv/config.m4 index efde95e038..6a056979e7 100644 --- a/ext/iconv/config.m4 +++ b/ext/iconv/config.m4 @@ -186,8 +186,8 @@ int main() { AC_DEFINE([ICONV_BROKEN_IGNORE],1,[Whether iconv supports IGNORE]) ],[ AC_MSG_RESULT(no, cross-compiling) - PHP_DEFINE([ICONV_SUPPORTS_ERRNO],0,[ext/iconv]) - AC_DEFINE([ICONV_SUPPORTS_ERRNO],0,[Whether iconv supports IGNORE]) + PHP_DEFINE([ICONV_BROKEN_IGNORE],0,[ext/iconv]) + AC_DEFINE([ICONV_BROKEN_IGNORE],0,[Whether iconv supports IGNORE]) ]) AC_MSG_CHECKING([if your cpp allows macro usage in include lines]) diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index 8042916c0d..897185da8a 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -2209,8 +2209,8 @@ PHP_FUNCTION(iconv_mime_encode) { zend_string *field_name = NULL; zend_string *field_value = NULL; + zend_string *tmp_str = NULL; zval *pref = NULL; - zval tmp_zv, *tmp_zv_p = NULL; smart_str retval = {0}; php_iconv_err_t err; @@ -2273,12 +2273,8 @@ PHP_FUNCTION(iconv_mime_encode) if ((pzval = zend_hash_str_find(Z_ARRVAL_P(pref), "line-break-chars", sizeof("line-break-chars") - 1)) != NULL) { if (Z_TYPE_P(pzval) != IS_STRING) { - ZVAL_DUP(&tmp_zv, pzval); - convert_to_string(&tmp_zv); - - lfchars = Z_STRVAL(tmp_zv); - - tmp_zv_p = &tmp_zv; + tmp_str = zval_get_string(pzval); + lfchars = tmp_str->val; } else { lfchars = Z_STRVAL_P(pzval); } @@ -2301,8 +2297,8 @@ PHP_FUNCTION(iconv_mime_encode) RETVAL_FALSE; } - if (tmp_zv_p != NULL) { - zval_dtor(tmp_zv_p); + if (tmp_str) { + zend_string_release(tmp_str); } } /* }}} */ diff --git a/ext/intl/breakiterator/breakiterator_iterators.cpp b/ext/intl/breakiterator/breakiterator_iterators.cpp index 93a49d1290..5bd77a5f2d 100644 --- a/ext/intl/breakiterator/breakiterator_iterators.cpp +++ b/ext/intl/breakiterator/breakiterator_iterators.cpp @@ -289,7 +289,8 @@ U_CFUNC PHP_METHOD(IntlPartsIterator, getBreakIterator) INTLITERATOR_METHOD_FETCH_OBJECT; zval *biter_zval = &ii->iterator->data; - RETURN_ZVAL(biter_zval, 1, 0); + ZVAL_DEREF(biter_zval); + ZVAL_COPY(return_value, biter_zval); } ZEND_BEGIN_ARG_INFO_EX(ainfo_parts_it_void, 0, 0, 0) diff --git a/ext/intl/common/common_enum.cpp b/ext/intl/common/common_enum.cpp index 4160c30258..e76f3b830f 100644 --- a/ext/intl/common/common_enum.cpp +++ b/ext/intl/common/common_enum.cpp @@ -219,7 +219,8 @@ static PHP_METHOD(IntlIterator, current) INTLITERATOR_METHOD_FETCH_OBJECT; data = ii->iterator->funcs->get_current_data(ii->iterator); if (data) { - RETURN_ZVAL(data, 1, 0); + ZVAL_DEREF(data); + ZVAL_COPY(return_value, data); } } diff --git a/ext/intl/tests/collator_get_sort_key_variant4.phpt b/ext/intl/tests/collator_get_sort_key_variant4.phpt index 2c86f21111..ed2c9bc175 100644 --- a/ext/intl/tests/collator_get_sort_key_variant4.phpt +++ b/ext/intl/tests/collator_get_sort_key_variant4.phpt @@ -1,7 +1,8 @@ --TEST-- -collator_get_sort_key() icu >= 54.1 +collator_get_sort_key() icu >= 55.1 --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> <?php if (version_compare(INTL_ICU_VERSION, '54.1') < 0) die('skip for ICU >= 54.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/collator_get_sort_key_variant5.phpt b/ext/intl/tests/collator_get_sort_key_variant5.phpt new file mode 100644 index 0000000000..52a3af9766 --- /dev/null +++ b/ext/intl/tests/collator_get_sort_key_variant5.phpt @@ -0,0 +1,98 @@ +--TEST-- +collator_get_sort_key() icu >= 55.1 +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') < 0) die('skip for ICU >= 55.1'); ?> +--FILE-- +<?php + +/* + * Get sort keys using various locales + */ +function sort_arrays( $locale, $data ) +{ + $res_str = ''; + + $coll = ut_coll_create( $locale ); + + foreach($data as $value) { + $res_val = ut_coll_get_sort_key( $coll, $value ); + $res_str .= "source: ".$value."\n". + "key: ".bin2hex($res_val)."\n"; + } + + return $res_str; +} + + +function ut_main() +{ + $res_str = ''; + + // Regular strings keys + $test_params = array( + 'abc', 'abd', 'aaa', + 'аа', 'а', 'z', + '', null , '3', + 'y' , 'i' , 'k' + ); + + $res_str .= sort_arrays( 'en_US', $test_params ); + + // Sort a non-ASCII array using ru_RU locale. + $test_params = array( + 'абг', 'абв', 'жжж', 'эюя' + ); + + $res_str .= sort_arrays( 'ru_RU', $test_params ); + + // Sort an array using Lithuanian locale. + $res_str .= sort_arrays( 'lt_LT', $test_params ); + + return $res_str . "\n"; +} + +include_once( 'ut_common.inc' ); +ut_run(); +?> +--EXPECT-- +source: abc +key: 292b2d01070107 +source: abd +key: 292b2f01070107 +source: aaa +key: 29292901070107 +source: аа +key: 60060601060106 +source: а +key: 600601050105 +source: z +key: 5b01050105 +source: +key: 0101 +source: +key: 0101 +source: 3 +key: 1801050105 +source: y +key: 5901050105 +source: i +key: 3901050105 +source: k +key: 3d01050105 +source: абг +key: 26060c1001070107 +source: абв +key: 26060c0e01070107 +source: жжж +key: 2626262601070107 +source: эюя +key: 26b4b6ba01070107 +source: абг +key: 60060c1001070107 +source: абв +key: 60060c0e01070107 +source: жжж +key: 6026262601070107 +source: эюя +key: 60b4b6ba01070107 diff --git a/ext/intl/tests/dateformat_calendars_variant3.phpt b/ext/intl/tests/dateformat_calendars_variant3.phpt index 36a67e6f04..d5bdac7fcd 100644 --- a/ext/intl/tests/dateformat_calendars_variant3.phpt +++ b/ext/intl/tests/dateformat_calendars_variant3.phpt @@ -41,5 +41,8 @@ string(47) "Sunday, January 1, 2012 at 5:12:00 AM GMT+05:12" string(47) "Sunday, January 1, 2012 at 5:12:00 AM GMT+05:12" string(44) "Sunday, 6 Tevet 5772 at 5:12:00 AM GMT+05:12" -Warning: IntlDateFormatter::__construct(): datefmt_create: invalid value for calendar type; it must be one of IntlDateFormatter::TRADITIONAL (locale's default calendar) or IntlDateFormatter::GREGORIAN. Alternatively, it can be an IntlCalendar object in %sdateformat_calendars_variant%d.php on line %d -==DONE== +Fatal error: Uncaught IntlException: IntlDateFormatter::__construct(): datefmt_create: invalid value for calendar type; it must be one of IntlDateFormatter::TRADITIONAL (locale's default calendar) or IntlDateFormatter::GREGORIAN. Alternatively, it can be an IntlCalendar object in %sdateformat_calendars_variant3.php:27 +Stack trace: +#0 %sdateformat_calendars_variant3.php(%d): IntlDateFormatter->__construct('en_US@calendar=...', 0, 0, 'GMT+05:12', -1) +#1 {main} + thrown %sdateformat_calendars_variant3.php on line %d diff --git a/ext/intl/tests/dateformat_formatObject_calendar_variant4.phpt b/ext/intl/tests/dateformat_formatObject_calendar_variant4.phpt index 2ca57c245f..70a8adc913 100644 --- a/ext/intl/tests/dateformat_formatObject_calendar_variant4.phpt +++ b/ext/intl/tests/dateformat_formatObject_calendar_variant4.phpt @@ -4,6 +4,7 @@ IntlDateFormatter::formatObject(): IntlCalendar tests <?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> <?php if (version_compare(INTL_ICU_VERSION, '54.1') < 0) die('skip for ICU >= 54.1'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); diff --git a/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt b/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt new file mode 100644 index 0000000000..d68b1f5fcb --- /dev/null +++ b/ext/intl/tests/dateformat_formatObject_calendar_variant5.phpt @@ -0,0 +1,40 @@ +--TEST-- +IntlDateFormatter::formatObject(): IntlCalendar tests +--SKIPIF-- +<?php +if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') < 0) die('skip for ICU >= 55.1'); ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +ini_set("intl.default_locale", "pt_PT"); +ini_set("date.timezone", "Europe/Lisbon"); + +$cal = IntlCalendar::fromDateTime('2012-01-01 00:00:00'); //Europe/Lisbon +echo IntlDateFormatter::formatObject($cal), "\n"; +echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL), "\n"; +echo IntlDateFormatter::formatObject($cal, null, "en-US"), "\n"; +echo IntlDateFormatter::formatObject($cal, array(IntlDateFormatter::SHORT, IntlDateFormatter::FULL), "en-US"), "\n"; +echo IntlDateFormatter::formatObject($cal, 'E y-MM-d HH,mm,ss.SSS v', "en-US"), "\n"; + +$cal = IntlCalendar::fromDateTime('2012-01-01 05:00:00+03:00'); +echo datefmt_format_object($cal, IntlDateFormatter::FULL), "\n"; + +$cal = IntlCalendar::createInstance(null,'en-US@calendar=islamic-civil'); +$cal->setTime(strtotime('2012-01-01 00:00:00')*1000.); +echo IntlDateFormatter::formatObject($cal), "\n"; +echo IntlDateFormatter::formatObject($cal, IntlDateFormatter::FULL, "en-US"), "\n"; + +?> +==DONE== + +--EXPECTF-- +01/01/2012, 00:00:00 +domingo, 1 de janeiro de 2012 às 00:00:00 Hora padrão %Sda Europa Ocidental +Jan 1, 2012, 12:00:00 AM +1/1/12, 12:00:00 AM Western European Standard %STime +Sun 2012-01-1 00,00,00.000 Portugal Time +domingo, 1 de janeiro de 2012 às 05:00:00 GMT+03:00 +06/02/1433, 00:00:00 +Sunday, Safar 6, 1433 at 12:00:00 AM Western European Standard Time +==DONE== diff --git a/ext/intl/tests/dateformat_formatObject_datetime_variant4.phpt b/ext/intl/tests/dateformat_formatObject_datetime_variant4.phpt index c47e2b59bd..6ae6dfedd3 100644 --- a/ext/intl/tests/dateformat_formatObject_datetime_variant4.phpt +++ b/ext/intl/tests/dateformat_formatObject_datetime_variant4.phpt @@ -4,6 +4,7 @@ IntlDateFormatter::formatObject(): DateTime tests <?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> <?php if (version_compare(INTL_ICU_VERSION, '54.1') < 0) die('skip for ICU >= 54.1'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); diff --git a/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt b/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt new file mode 100644 index 0000000000..22b0f6adbe --- /dev/null +++ b/ext/intl/tests/dateformat_formatObject_datetime_variant5.phpt @@ -0,0 +1,33 @@ +--TEST-- +IntlDateFormatter::formatObject(): DateTime tests +--SKIPIF-- +<?php +if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') < 0) die('skip for ICU >= 55.1'); ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +ini_set("intl.default_locale", "pt_PT"); +ini_set("date.timezone", "Europe/Lisbon"); + +$dt = new DateTime('2012-01-01 00:00:00'); //Europe/Lisbon +echo IntlDateFormatter::formatObject($dt), "\n"; +echo IntlDateFormatter::formatObject($dt, IntlDateFormatter::FULL), "\n"; +echo IntlDateFormatter::formatObject($dt, null, "en-US"), "\n"; +echo IntlDateFormatter::formatObject($dt, array(IntlDateFormatter::SHORT, IntlDateFormatter::FULL), "en-US"), "\n"; +echo IntlDateFormatter::formatObject($dt, 'E y-MM-d HH,mm,ss.SSS v', "en-US"), "\n"; + +$dt = new DateTime('2012-01-01 05:00:00+03:00'); +echo IntlDateFormatter::formatObject($dt, IntlDateFormatter::FULL), "\n"; + +?> +==DONE== + +--EXPECTF-- +01/01/2012, 00:00:00 +domingo, 1 de janeiro de 2012 às 00:00:00 Hora padrão %Sda Europa Ocidental +Jan 1, 2012, 12:00:00 AM +1/1/12, 12:00:00 AM Western European Standard %STime +Sun 2012-01-1 00,00,00.000 Portugal Time +domingo, 1 de janeiro de 2012 às 05:00:00 GMT+03:00 +==DONE== diff --git a/ext/intl/tests/dateformat_get_set_timezone_variant4.phpt b/ext/intl/tests/dateformat_get_set_timezone_variant4.phpt index adedd74965..8a563d8a90 100644 --- a/ext/intl/tests/dateformat_get_set_timezone_variant4.phpt +++ b/ext/intl/tests/dateformat_get_set_timezone_variant4.phpt @@ -4,6 +4,7 @@ IntlDateFormatter: get/setTimeZone() <?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> <?php if (version_compare(INTL_ICU_VERSION, '54.1') < 0) die('skip for ICU >= 54.1'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); diff --git a/ext/intl/tests/dateformat_get_set_timezone_variant5.phpt b/ext/intl/tests/dateformat_get_set_timezone_variant5.phpt new file mode 100644 index 0000000000..1487751bd6 --- /dev/null +++ b/ext/intl/tests/dateformat_get_set_timezone_variant5.phpt @@ -0,0 +1,62 @@ +--TEST-- +IntlDateFormatter: get/setTimeZone() +--SKIPIF-- +<?php +if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') < 0) die('skip for ICU >= 55.1'); ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +ini_set("intl.default_locale", "pt_PT"); +ini_set("date.timezone", 'Atlantic/Azores'); + +$ts = strtotime('2012-01-01 00:00:00 UTC'); + +function d(IntlDateFormatter $df) { +global $ts; +echo $df->format($ts), "\n"; +var_dump( +$df->getTimeZoneID(), +$df->getTimeZone()->getID()); +echo "\n"; +} + +$df = new IntlDateFormatter('pt_PT', 0, 0, 'Europe/Minsk'); +d($df); + +$df->setTimeZone(NULL); +d($df); + +$df->setTimeZone('Europe/Madrid'); +d($df); + +$df->setTimeZone(IntlTimeZone::createTimeZone('Europe/Paris')); +d($df); + +$df->setTimeZone(new DateTimeZone('Europe/Amsterdam')); +d($df); + +?> +==DONE== +--EXPECTF-- +domingo, 1 de janeiro de 2012 às 03:00:00 Hor%s do Extremo Leste da Europa +string(12) "Europe/Minsk" +string(12) "Europe/Minsk" + +sábado, 31 de dezembro de 2011 às 23:00:00 Hor%s padrão %Sdos Açores +string(15) "Atlantic/Azores" +string(15) "Atlantic/Azores" + +domingo, 1 de janeiro de 2012 às 01:00:00 Hor%s padrão %Sda Europa Central +string(13) "Europe/Madrid" +string(13) "Europe/Madrid" + +domingo, 1 de janeiro de 2012 às 01:00:00 Hor%s padrão %Sda Europa Central +string(12) "Europe/Paris" +string(12) "Europe/Paris" + +domingo, 1 de janeiro de 2012 às 01:00:00 Hor%s padrão %Sda Europa Central +string(16) "Europe/Amsterdam" +string(16) "Europe/Amsterdam" + +==DONE== diff --git a/ext/intl/tests/spoofchecker_004.phpt b/ext/intl/tests/spoofchecker_004.phpt index b38c61d717..a26b39209f 100644 --- a/ext/intl/tests/spoofchecker_004.phpt +++ b/ext/intl/tests/spoofchecker_004.phpt @@ -2,6 +2,7 @@ spoofchecker with settings changed --SKIPIF-- <?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/spoofchecker_005.phpt b/ext/intl/tests/spoofchecker_005.phpt new file mode 100644 index 0000000000..0fc3d52a8c --- /dev/null +++ b/ext/intl/tests/spoofchecker_005.phpt @@ -0,0 +1,29 @@ +--TEST-- +spoofchecker with settings changed +--SKIPIF-- +<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') < 0) die('skip for ICU >= 55.1'); ?> +--FILE-- +<?php + +$korean = "\xED\x95\x9C" . "\xEA\xB5\xAD" . "\xEB\xA7\x90"; + +$x = new Spoofchecker(); +echo "Check with default settings\n"; +var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO")); +var_dump($x->areConfusable("hello", "h\xD0\xB5llo")); + +echo "Change confusable settings\n"; +$x->setChecks(Spoofchecker::MIXED_SCRIPT_CONFUSABLE | + Spoofchecker::WHOLE_SCRIPT_CONFUSABLE | + Spoofchecker::SINGLE_SCRIPT_CONFUSABLE); +var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO")); +var_dump($x->areConfusable("hello", "h\xD0\xB5llo")); +?> +--EXPECTF-- +Check with default settings +bool(true) +bool(true) +Change confusable settings +bool(true) +bool(true) diff --git a/ext/intl/tests/timezone_getDisplayName_variant3-49+.phpt b/ext/intl/tests/timezone_getDisplayName_variant3-49+.phpt index e90cc4748c..e67fcab491 100644 --- a/ext/intl/tests/timezone_getDisplayName_variant3-49+.phpt +++ b/ext/intl/tests/timezone_getDisplayName_variant3-49+.phpt @@ -1,11 +1,9 @@ --TEST-- IntlTimeZone::getDisplayName(): locale parameter --SKIPIF-- -<?php -if (!extension_loaded('intl')) - die('skip intl extension not enabled'); -if (version_compare(INTL_ICU_VERSION, '49') < 0) - die('skip for ICU 49+'); +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '49') < 0) die('skip for ICU >= 49'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '55.1') >= 0) die('skip for ICU < 55.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index 641727d7ed..728f9bde8b 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -614,7 +614,7 @@ U_CFUNC PHP_FUNCTION(intltz_to_date_time_zone) &TIMEZONE_ERROR(to), "intltz_to_date_time_zone", &tmp); if (ret) { - RETURN_ZVAL(ret, 1, 1); + ZVAL_COPY_VALUE(return_value, ret); } else { RETURN_FALSE; } diff --git a/ext/json/json_parser.tab.c b/ext/json/json_parser.tab.c index a5f5c6fb09..45a982bbcc 100644 --- a/ext/json/json_parser.tab.c +++ b/ext/json/json_parser.tab.c @@ -514,10 +514,10 @@ static const yytype_uint8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint8 yyrline[] = { - 0, 92, 92, 93, 97, 97, 101, 102, 106, 107, - 111, 112, 113, 117, 118, 122, 122, 126, 127, 131, - 132, 136, 137, 138, 142, 143, 147, 148, 149, 150, - 151, 152, 153, 154, 155, 156, 160 + 0, 92, 92, 98, 105, 105, 113, 114, 123, 126, + 130, 135, 140, 147, 152, 159, 159, 167, 168, 177, + 180, 184, 189, 194, 201, 202, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 219 }; #endif @@ -1447,13 +1447,19 @@ yyreduce: { case 2: - { ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value)); ZVAL_COPY_VALUE(parser->return_value, &(yyvsp[-1].value)); PHP_JSON_USE((yyvsp[0].value)); YYACCEPT; } + { + ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value)); + ZVAL_COPY_VALUE(parser->return_value, &(yyvsp[-1].value)); + PHP_JSON_USE((yyvsp[0].value)); YYACCEPT; + } break; case 3: - { PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); } + { + PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); + } break; @@ -1465,49 +1471,70 @@ yyreduce: case 5: - { PHP_JSON_DEPTH_DEC; (yyval.value) = (yyvsp[-1].value); } + { + PHP_JSON_DEPTH_DEC; + (yyval.value) = (yyvsp[-1].value); + } break; case 7: - { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } + { + parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; + YYERROR; + } break; case 8: - { php_json_parser_object_init(parser, &(yyval.value)); } + { + php_json_parser_object_init(parser, &(yyval.value)); + } break; case 10: - { php_json_parser_object_init(parser, &(yyval.value)); php_json_parser_object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val); } + { + php_json_parser_object_init(parser, &(yyval.value)); + php_json_parser_object_update(parser, &(yyval.value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val); + } break; case 11: - { php_json_parser_object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val); ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value)); } + { + php_json_parser_object_update(parser, &(yyvsp[-2].value), (yyvsp[0].pair).key, &(yyvsp[0].pair).val); + ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value)); + } break; case 12: - { PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); } + { + PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); + } break; case 13: - { (yyval.pair).key = Z_STR((yyvsp[-2].value)); ZVAL_COPY_VALUE(&(yyval.pair).val, &(yyvsp[0].value)); } + { + (yyval.pair).key = Z_STR((yyvsp[-2].value)); + ZVAL_COPY_VALUE(&(yyval.pair).val, &(yyvsp[0].value)); + } break; case 14: - { PHP_JSON_USE_2((yyval.pair), (yyvsp[-1].value), (yyvsp[0].value)); } + { + PHP_JSON_USE_2((yyval.pair), (yyvsp[-1].value), (yyvsp[0].value)); + } break; @@ -1519,43 +1546,62 @@ yyreduce: case 16: - { PHP_JSON_DEPTH_DEC; ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value)); } + { + PHP_JSON_DEPTH_DEC; + ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-1].value)); + } break; case 18: - { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } + { + parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; + YYERROR; + } break; case 19: - { php_json_parser_array_init(&(yyval.value)); } + { + php_json_parser_array_init(&(yyval.value)); + } break; case 21: - { php_json_parser_array_init(&(yyval.value)); php_json_parser_array_append(&(yyval.value), &(yyvsp[0].value)); } + { + php_json_parser_array_init(&(yyval.value)); + php_json_parser_array_append(&(yyval.value), &(yyvsp[0].value)); + } break; case 22: - { php_json_parser_array_append(&(yyvsp[-2].value), &(yyvsp[0].value)); ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value)); } + { + php_json_parser_array_append(&(yyvsp[-2].value), &(yyvsp[0].value)); + ZVAL_COPY_VALUE(&(yyval.value), &(yyvsp[-2].value)); + } break; case 23: - { PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); } + { + PHP_JSON_USE_2((yyval.value), (yyvsp[-1].value), (yyvsp[0].value)); + } break; case 36: - { PHP_JSON_USE_1((yyval.value), (yyvsp[0].value)); YYERROR; } + { + PHP_JSON_USE_1((yyval.value), (yyvsp[0].value)); + YYERROR; + } break; diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y index 044b82eb51..500a0ff11d 100644 --- a/ext/json/json_parser.y +++ b/ext/json/json_parser.y @@ -89,53 +89,112 @@ void php_json_parser_array_append(zval *array, zval *zvalue); %% /* Rules */ start: - value PHP_JSON_T_EOI { ZVAL_COPY_VALUE(&$$, &$1); ZVAL_COPY_VALUE(parser->return_value, &$1); PHP_JSON_USE($2); YYACCEPT; } - | value errlex { PHP_JSON_USE_2($$, $1, $2); } + value PHP_JSON_T_EOI + { + ZVAL_COPY_VALUE(&$$, &$1); + ZVAL_COPY_VALUE(parser->return_value, &$1); + PHP_JSON_USE($2); YYACCEPT; + } + | value errlex + { + PHP_JSON_USE_2($$, $1, $2); + } ; object: - '{' { PHP_JSON_DEPTH_INC; } members object_end { PHP_JSON_DEPTH_DEC; $$ = $3; } + '{' { PHP_JSON_DEPTH_INC; } members object_end + { + PHP_JSON_DEPTH_DEC; + $$ = $3; + } ; object_end: '}' - | ']' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } + | ']' + { + parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; + YYERROR; + } ; members: - /* empty */ { php_json_parser_object_init(parser, &$$); } + /* empty */ + { + php_json_parser_object_init(parser, &$$); + } | member ; member: - pair { php_json_parser_object_init(parser, &$$); php_json_parser_object_update(parser, &$$, $1.key, &$1.val); } - | member ',' pair { php_json_parser_object_update(parser, &$1, $3.key, &$3.val); ZVAL_COPY_VALUE(&$$, &$1); } - | member errlex { PHP_JSON_USE_2($$, $1, $2); } + pair + { + php_json_parser_object_init(parser, &$$); + php_json_parser_object_update(parser, &$$, $1.key, &$1.val); + } + | member ',' pair + { + php_json_parser_object_update(parser, &$1, $3.key, &$3.val); + ZVAL_COPY_VALUE(&$$, &$1); + } + | member errlex + { + PHP_JSON_USE_2($$, $1, $2); + } ; pair: - key ':' value { $$.key = Z_STR($1); ZVAL_COPY_VALUE(&$$.val, &$3); } - | key errlex { PHP_JSON_USE_2($$, $1, $2); } + key ':' value + { + $$.key = Z_STR($1); + ZVAL_COPY_VALUE(&$$.val, &$3); + } + | key errlex + { + PHP_JSON_USE_2($$, $1, $2); + } ; array: - '[' { PHP_JSON_DEPTH_INC; } elements array_end { PHP_JSON_DEPTH_DEC; ZVAL_COPY_VALUE(&$$, &$3); } + '[' { PHP_JSON_DEPTH_INC; } elements array_end + { + PHP_JSON_DEPTH_DEC; + ZVAL_COPY_VALUE(&$$, &$3); + } ; array_end: ']' - | '}' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } + | '}' + { + parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; + YYERROR; + } ; elements: - /* empty */ { php_json_parser_array_init(&$$); } + /* empty */ + { + php_json_parser_array_init(&$$); + } | element ; element: - value { php_json_parser_array_init(&$$); php_json_parser_array_append(&$$, &$1); } - | element ',' value { php_json_parser_array_append(&$1, &$3); ZVAL_COPY_VALUE(&$$, &$1); } - | element errlex { PHP_JSON_USE_2($$, $1, $2); } + value + { + php_json_parser_array_init(&$$); + php_json_parser_array_append(&$$, &$1); + } + | element ',' value + { + php_json_parser_array_append(&$1, &$3); + ZVAL_COPY_VALUE(&$$, &$1); + } + | element errlex + { + PHP_JSON_USE_2($$, $1, $2); + } ; key: @@ -157,7 +216,11 @@ value: ; errlex: - PHP_JSON_T_ERROR { PHP_JSON_USE_1($$, $1); YYERROR; } + PHP_JSON_T_ERROR + { + PHP_JSON_USE_1($$, $1); + YYERROR; + } ; %% /* Functions */ diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index b96d25aa80..bb87a389b4 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -815,6 +815,9 @@ static PHP_MINIT_FUNCTION(libxml) #if LIBXML_VERSION >= 20703 REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE", XML_PARSE_HUGE, CONST_CS | CONST_PERSISTENT); #endif +#if LIBXML_VERSION >= 20900 + REGISTER_LONG_CONSTANT("LIBXML_BIGLINES", XML_PARSE_BIG_LINES, CONST_CS | CONST_PERSISTENT); +#endif REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG", LIBXML_SAVE_NOEMPTYTAG, CONST_CS | CONST_PERSISTENT); /* Schema validation options */ @@ -1341,7 +1344,7 @@ PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *o } /* }}} */ -#ifdef PHP_WIN32 +#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML) PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { return xmlDllMain(hinstDLL, fdwReason, lpvReserved); diff --git a/ext/libxml/tests/bug54138.phpt b/ext/libxml/tests/bug54138.phpt new file mode 100644 index 0000000000..5a03f2ce07 --- /dev/null +++ b/ext/libxml/tests/bug54138.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #54138 - DOMNode::getLineNo() doesn't return line number higher than 65535 +--SKIPIF-- +<?php +if (!extension_loaded('dom')) die('skip dom extension not available'); +if (!defined('LIBXML_BIGLINES')) die('skip this test requires LIBXML_BIGLINES'); +?> +--FILE-- +<?php +$foos = str_repeat('<foo/>' . PHP_EOL, 65535); +$xml = <<<XML +<?xml version="1.0" encoding="UTF-8"?> +<root> +$foos +<bar/> +</root> +XML; +$dom = new DOMDocument(); +$dom->loadXML($xml, LIBXML_BIGLINES); +var_dump($dom->getElementsByTagName('bar')->item(0)->getLineNo()); +?> +--EXPECT-- +int(65540) diff --git a/ext/libxml/tests/bug54138_1.phpt b/ext/libxml/tests/bug54138_1.phpt new file mode 100644 index 0000000000..f0a8a04698 --- /dev/null +++ b/ext/libxml/tests/bug54138_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #54138 - DOMNode::getLineNo() doesn't return line number higher than 65535 +--SKIPIF-- +<?php +if (!extension_loaded('dom')) die('skip dom extension not available'); +if (LIBXML_VERSION >= 20900) die('skip this test is for libxml < 2.9.0 only'); +?> +--FILE-- +<?php +define('LIBXML_BIGLINES', 1<<22); +$foos = str_repeat('<foo/>' . PHP_EOL, 65535); +$xml = <<<XML +<?xml version="1.0" encoding="UTF-8"?> +<root> +$foos +<bar/> +</root> +XML; +$dom = new DOMDocument(); +$dom->loadXML($xml, LIBXML_BIGLINES); +var_dump($dom->getElementsByTagName('bar')->item(0)->getLineNo()); +?> +--EXPECT-- +int(65535) diff --git a/ext/libxml/tests/bug54138_2.phpt b/ext/libxml/tests/bug54138_2.phpt new file mode 100644 index 0000000000..b25e451527 --- /dev/null +++ b/ext/libxml/tests/bug54138_2.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #54138 - DOMNode::getLineNo() doesn't return line number higher than 65535 +--SKIPIF-- +<?php +if (!extension_loaded('dom')) die('skip dom extension not available'); +if (LIBXML_VERSION < 20900) die('skip this test is for libxml >= 2.9.0 only'); +if (defined('LIBXML_BIGLINES')) die('skip this test is for LIBXML_BIGLINES being undefined'); +?> +--FILE-- +<?php +define('LIBXML_BIGLINES', 1<<22); +$foos = str_repeat('<foo/>' . PHP_EOL, 65535); +$xml = <<<XML +<?xml version="1.0" encoding="UTF-8"?> +<root> +$foos +<bar/> +</root> +XML; +$dom = new DOMDocument(); +$dom->loadXML($xml, LIBXML_BIGLINES); +var_dump($dom->getElementsByTagName('bar')->item(0)->getLineNo()); +?> +--EXPECT-- +int(65540) diff --git a/ext/libxml/tests/bug69753.phpt b/ext/libxml/tests/bug69753.phpt new file mode 100644 index 0000000000..63d1295b6e --- /dev/null +++ b/ext/libxml/tests/bug69753.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #69753 - libXMLError::file contains invalid URI +--XFAIL-- +Awaiting upstream fix: https://bugzilla.gnome.org/show_bug.cgi?id=750365 +--SKIPIF-- +<?php +if (substr(PHP_OS, 0, 3) != 'WIN') die("skip this test is for Windows platforms only"); +if (!extension_loaded('dom')) die('skip dom extension not available'); +?> +--FILE-- +<?php +libxml_use_internal_errors(true); +$doc = new DomDocument(); +$doc->load(__DIR__ . DIRECTORY_SEPARATOR . 'bug69753.xml'); +$error = libxml_get_last_error(); +var_dump($error->file); +?> +--EXPECTF-- +string(%d) "file:///%s/ext/libxml/tests/bug69753.xml" diff --git a/ext/libxml/tests/bug69753.xml b/ext/libxml/tests/bug69753.xml new file mode 100644 index 0000000000..63b18d5c6d --- /dev/null +++ b/ext/libxml/tests/bug69753.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<root> + <sub> +</root> diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 9bc81769d5..d076614418 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -306,7 +306,7 @@ zval *mysqli_read_property(zval *object, zval *member, int type, void **cache_sl obj = Z_MYSQLI_P(object); if (Z_TYPE_P(member) != IS_STRING) { - ZVAL_DUP(&tmp_member, member); + ZVAL_COPY(&tmp_member, member); convert_to_string(&tmp_member); member = &tmp_member; } @@ -341,7 +341,7 @@ void mysqli_write_property(zval *object, zval *member, zval *value, void **cache mysqli_prop_handler *hnd = NULL; if (Z_TYPE_P(member) != IS_STRING) { - ZVAL_DUP(&tmp_member, member); + ZVAL_COPY(&tmp_member, member); convert_to_string(&tmp_member); member = &tmp_member; } diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 70ea4e4874..767ba34ab0 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -925,6 +925,8 @@ mysqlnd_stmt_fetch_row_unbuffered(MYSQLND_RES * result, void * param, unsigned i ZVAL_COPY_VALUE(result, data); /* copied data, thus also the ownership. Thus null data */ ZVAL_NULL(data); + } else { + ZVAL_NULL(result); } } } diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 275e3f5fa2..fe20248fee 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -123,12 +123,9 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz blocks[0].start_opline_no = 0; while (opline < end) { switch((unsigned)opline->opcode) { - case ZEND_BRK: - case ZEND_CONT: case ZEND_GOTO: - /* would not optimize non-optimized BRK/CONTs - we cannot - really know where it jumps, so these optimizations are - too dangerous */ + /* would not optimize GOTOs - we cannot really know where it jumps, + * so these optimizations are too dangerous */ return 0; case ZEND_FAST_CALL: START_BLOCK_OP(ZEND_OP1(opline).opline_num); diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index fd83e76461..c72e8f6772 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -301,13 +301,14 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx #if DEBUG_COMPACT_LITERALS { int i, use_copy; - fprintf(stderr, "File %s func %s\n", op_array->filename, - op_array->function_name? op_array->function_name : "main"); + fprintf(stderr, "File %s func %s\n", op_array->filename->val, + op_array->function_name ? op_array->function_name->val : "main"); fprintf(stderr, "Literlas table size %d\n", op_array->last_literal); for (i = 0; i < op_array->last_literal; i++) { - zval zv = op_array->literals[i].constant; - use_copy = zend_make_printable_zval(&op_array->literals[i].constant, &zv); + zval zv; + ZVAL_COPY_VALUE(&zv, op_array->literals + i); + use_copy = zend_make_printable_zval(op_array->literals + i, &zv); fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); if (use_copy) { zval_dtor(&zv); @@ -487,8 +488,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal); for (i = 0; i < op_array->last_literal; i++) { - zval zv = op_array->literals[i].constant; - use_copy = zend_make_printable_zval(&op_array->literals[i].constant, &zv); + zval zv; + ZVAL_COPY_VALUE(&zv, op_array->literals + i); + use_copy = zend_make_printable_zval(op_array->literals + i, &zv); fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); if (use_copy) { zval_dtor(&zv); diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index c042940b0e..611b39df24 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -613,8 +613,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_EXIT: case ZEND_THROW: case ZEND_CATCH: - case ZEND_BRK: - case ZEND_CONT: case ZEND_GOTO: case ZEND_FAST_CALL: case ZEND_FAST_RET: diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index c0832a1cbf..a9d85daa6f 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -181,59 +181,6 @@ void zend_optimizer_pass2(zend_op_array *op_array) opline->opcode = ZEND_JMP; } break; - - case ZEND_BRK: - case ZEND_CONT: - { - zend_brk_cont_element *jmp_to; - int array_offset; - int nest_levels; - int dont_optimize = 0; - - ZEND_ASSERT(ZEND_OP2_TYPE(opline) == IS_CONST); - ZEND_ASSERT(Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG); - - nest_levels = Z_LVAL(ZEND_OP2_LITERAL(opline)); - - array_offset = ZEND_OP1(opline).opline_num; - while (1) { - if (array_offset == -1) { - dont_optimize = 1; /* don't optimize this bogus break/continue, let the executor shout */ - break; - } - jmp_to = &op_array->brk_cont_array[array_offset]; - array_offset = jmp_to->parent; - if (--nest_levels > 0) { - if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || - op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE || - op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) { - dont_optimize = 1; - break; - } - } else { - break; - } - } - - if (dont_optimize) { - break; - } - - /* optimize - convert to a JMP */ - switch (opline->opcode) { - case ZEND_BRK: - MAKE_NOP(opline); - ZEND_OP1(opline).opline_num = jmp_to->brk; - break; - case ZEND_CONT: - MAKE_NOP(opline); - ZEND_OP1(opline).opline_num = jmp_to->cont; - break; - } - opline->opcode = ZEND_JMP; - /* MAKE_NOP() already set op1 and op2 to IS_UNUSED */ - } - break; } opline++; } diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index 3019b274e9..cb717998d6 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -322,8 +322,6 @@ continue_jmp_ex_optimization: op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPNZ_EX || op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_BRK || - op->opcode == ZEND_CONT || op->opcode == ZEND_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || @@ -358,8 +356,6 @@ continue_jmp_ex_optimization: op->opcode == ZEND_JMPNZ || op->opcode == ZEND_JMPNZ_EX || op->opcode == ZEND_JMPZNZ || - op->opcode == ZEND_BRK || - op->opcode == ZEND_CONT || op->opcode == ZEND_CASE || op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index c9f58963ac..36684218d8 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2146,7 +2146,6 @@ static inline void zend_accel_fast_del_bucket(HashTable *ht, uint32_t idx, Bucke uint32_t nIndex = p->h | ht->nTableMask; uint32_t i = HT_HASH(ht, nIndex); - ht->nNumUsed--; ht->nNumOfElements--; if (idx != i) { Bucket *prev = HT_HASH_TO_BUCKET(ht, i); @@ -2267,6 +2266,9 @@ static void zend_accel_fast_shutdown(void) zend_accel_fast_del_bucket(EG(zend_constants), HT_IDX_TO_HASH(_idx-1), _p); } } ZEND_HASH_FOREACH_END(); + EG(function_table)->nNumUsed = EG(function_table)->nNumOfElements; + EG(class_table)->nNumUsed = EG(class_table)->nNumOfElements; + EG(zend_constants)->nNumUsed = EG(zend_constants)->nNumOfElements; CG(unclean_shutdown) = 1; } diff --git a/ext/opcache/tests/bug69688.phpt b/ext/opcache/tests/bug69688.phpt new file mode 100644 index 0000000000..cc75960e73 --- /dev/null +++ b/ext/opcache/tests/bug69688.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #69688 (segfault with eval and opcache fast shutdown) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.fast_shutdown=1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +eval('function g() {} function g2() {} function g3() {}'); + +eval('class A{} class B{} class C{}'); + +?> +okey +--EXPECT-- +okey diff --git a/ext/opcache/tests/optimize_func_calls.phpt b/ext/opcache/tests/optimize_func_calls.phpt index b3bc8da6a9..3f795f5fc6 100644 --- a/ext/opcache/tests/optimize_func_calls.phpt +++ b/ext/opcache/tests/optimize_func_calls.phpt @@ -127,4 +127,8 @@ Array string(7) "changed" string(7) "changed" -Fatal error: Cannot pass parameter 1 by reference in %soptimize_func_calls.php on line %d +Fatal error: Uncaught EngineException: Cannot pass parameter 1 by reference in %soptimize_func_calls.php:%d +Stack trace: +#0 {main} + thrown in %soptimize_func_calls.php on line %d + diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 13118a0a4e..7a8ef8d3e8 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -243,10 +243,6 @@ static ZEND_INI_MH(OnEnable) #ifdef HAVE_OPCACHE_FILE_CACHE -#ifndef S_ISDIR -# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - static ZEND_INI_MH(OnUpdateFileCache) { if (new_value) { diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 9407b2ec39..9582b723a2 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -27,10 +27,10 @@ #if SIZEOF_SIZE_T <= SIZEOF_ZEND_LONG /* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */ -# define accel_xlat_set(old, new) zend_hash_index_update_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old), (new)) +# define accel_xlat_set(old, new) zend_hash_index_add_new_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old), (new)) # define accel_xlat_get(old) zend_hash_index_find_ptr(&ZCG(bind_hash), (zend_ulong)(zend_uintptr_t)(old)) #else -# define accel_xlat_set(old, new) (zend_hash_str_add_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (zend_ulong)(zend_uintptr_t)(old), (void**)&(new)) +# define accel_xlat_set(old, new) (zend_hash_str_add_new_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (zend_ulong)(zend_uintptr_t)(old), (void**)&(new)) # define accel_xlat_get(old, new) ((new) = zend_hash_str_find_ptr(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (zend_ulong)(zend_uintptr_t)(old), (void**)&(new))) #endif @@ -40,7 +40,6 @@ typedef int (*id_function_t)(void *, void *); typedef void (*unique_copy_ctor_func_t)(void *pElement); -static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind); static zend_ast *zend_ast_clone(zend_ast *ast); static void zend_accel_destroy_zend_function(zval *zv) @@ -124,11 +123,12 @@ void zend_accel_move_user_functions(HashTable *src, HashTable *dst) dtor_func_t orig_dtor = src->pDestructor; src->pDestructor = NULL; + zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) { zend_function *function = Z_PTR(p->val); if (EXPECTED(function->type == ZEND_USER_FUNCTION)) { - zend_hash_add_new_ptr(dst, p->key, function); + _zend_hash_append_ptr(dst, p->key, function); zend_hash_del_bucket(src, p); } else { break; @@ -152,80 +152,36 @@ void zend_accel_copy_internal_functions(void) ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table)); } -static zend_always_inline zend_string *zend_clone_str(zend_string *str) -{ - zend_string *ret; - - if (EXPECTED(IS_INTERNED(str))) { - ret = str; - } else if (GC_REFCOUNT(str) <= 1 || (ret = accel_xlat_get(str)) == NULL) { - ret = zend_string_dup(str, 0); - GC_FLAGS(ret) = GC_FLAGS(str); - if (GC_REFCOUNT(str) > 1) { - accel_xlat_set(str, ret); - } - } else { - GC_REFCOUNT(ret)++; - } - return ret; -} - -static inline void zend_clone_zval(zval *src, int bind) +static inline void zend_clone_zval(zval *src) { void *ptr; - if (Z_IMMUTABLE_P(src)) { - return; + if (Z_TYPE_P(src) == IS_REFERENCE) { + ptr = accel_xlat_get(Z_REF_P(src)); + if (ptr != NULL) { + Z_REF_P(src) = ptr; + return; + } else { + zend_reference *old = Z_REF_P(src); + ZVAL_NEW_REF(src, &old->val); + Z_REF_P(src)->gc = old->gc; + accel_xlat_set(old, Z_REF_P(src)); + src = Z_REFVAL_P(src); + } } + if (Z_TYPE_P(src) == IS_CONSTANT_AST) { + if (Z_REFCOUNT_P(src) > 1 && (ptr = accel_xlat_get(Z_AST_P(src))) != NULL) { + Z_AST_P(src) = ptr; + } else { + zend_ast_ref *old = Z_AST_P(src); - switch (Z_TYPE_P(src)) { - case IS_STRING: - case IS_CONSTANT: - Z_STR_P(src) = zend_clone_str(Z_STR_P(src)); - break; - case IS_ARRAY: - if (Z_ARR_P(src) != &EG(symbol_table)) { - if (bind && Z_REFCOUNT_P(src) > 1 && (ptr = accel_xlat_get(Z_ARR_P(src))) != NULL) { - Z_ARR_P(src) = ptr; - } else { - zend_array *old = Z_ARR_P(src); - - Z_ARR_P(src) = emalloc(sizeof(zend_array)); - Z_ARR_P(src)->gc = old->gc; - if (bind && Z_REFCOUNT_P(src) > 1) { - accel_xlat_set(old, Z_ARR_P(src)); - } - zend_hash_clone_zval(Z_ARRVAL_P(src), old, 0); - } - } - break; - case IS_REFERENCE: - if (bind && Z_REFCOUNT_P(src) > 1 && (ptr = accel_xlat_get(Z_REF_P(src))) != NULL) { - Z_REF_P(src) = ptr; - } else { - zend_reference *old = Z_REF_P(src); - ZVAL_NEW_REF(src, &old->val); - Z_REF_P(src)->gc = old->gc; - if (bind && Z_REFCOUNT_P(src) > 1) { - accel_xlat_set(old, Z_REF_P(src)); - } - zend_clone_zval(Z_REFVAL_P(src), bind); - } - break; - case IS_CONSTANT_AST: - if (bind && Z_REFCOUNT_P(src) > 1 && (ptr = accel_xlat_get(Z_AST_P(src))) != NULL) { - Z_AST_P(src) = ptr; - } else { - zend_ast_ref *old = Z_AST_P(src); - - ZVAL_NEW_AST(src, old->ast); - Z_AST_P(src)->gc = old->gc; - if (bind && Z_REFCOUNT_P(src) > 1) { - accel_xlat_set(old, Z_AST_P(src)); - } - Z_ASTVAL_P(src) = zend_ast_clone(Z_ASTVAL_P(src)); + ZVAL_NEW_AST(src, old->ast); + Z_AST_P(src)->gc = old->gc; + if (Z_REFCOUNT_P(src) > 1) { + accel_xlat_set(old, Z_AST_P(src)); } - break; + Z_ASTVAL_P(src) = zend_ast_clone(Z_ASTVAL_P(src)); + } } } @@ -238,7 +194,6 @@ static zend_ast *zend_ast_clone(zend_ast *ast) copy->kind = ZEND_AST_ZVAL; copy->attr = ast->attr; ZVAL_COPY_VALUE(©->val, zend_ast_get_zval(ast)); - zend_clone_zval(©->val, 0); return (zend_ast *) copy; } else if (zend_ast_is_list(ast)) { zend_ast_list *list = zend_ast_get_list(ast); @@ -271,10 +226,9 @@ static zend_ast *zend_ast_clone(zend_ast *ast) } } -static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) +static void zend_hash_clone_constants(HashTable *ht, HashTable *source) { - uint idx; - Bucket *p, *q, *r; + Bucket *p, *q, *end; zend_ulong nIndex; ht->nTableSize = source->nTableSize; @@ -291,65 +245,34 @@ static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) return; } - if (source->u.flags & HASH_FLAG_PACKED) { - ht->u.flags |= HASH_FLAG_PACKED; - HT_SET_DATA_ADDR(ht, (Bucket *) emalloc(HT_SIZE(ht))); - HT_HASH_RESET_PACKED(ht); - - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - nIndex = p->h | ht->nTableMask; + ZEND_ASSERT((source->u.flags & HASH_FLAG_PACKED) == 0); + HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); + HT_HASH_RESET(ht); - r = ht->arData + ht->nNumUsed; - q = ht->arData + p->h; - while (r != q) { - ZVAL_UNDEF(&r->val); - r++; - } - ht->nNumUsed = p->h + 1; + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; + nIndex = p->h | ht->nTableMask; - /* Initialize key */ - q->h = p->h; - q->key = NULL; + /* Insert into hash collision list */ + q = ht->arData + ht->nNumUsed; + Z_NEXT(q->val) = HT_HASH(ht, nIndex); + HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); - /* Copy data */ - ZVAL_COPY_VALUE(&q->val, &p->val); - zend_clone_zval(&q->val, bind); - } - } else { - HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); - HT_HASH_RESET(ht); - - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - nIndex = p->h | ht->nTableMask; - - /* Insert into hash collision list */ - q = ht->arData + ht->nNumUsed; - Z_NEXT(q->val) = HT_HASH(ht, nIndex); - HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(ht->nNumUsed++); - - /* Initialize key */ - q->h = p->h; - if (!p->key) { - q->key = NULL; - } else { - q->key = zend_clone_str(p->key); - } + /* Initialize key */ + q->h = p->h; + q->key = p->key; - /* Copy data */ - ZVAL_COPY_VALUE(&q->val, &p->val); - zend_clone_zval(&q->val, bind); - } + /* Copy data */ + ZVAL_COPY_VALUE(&q->val, &p->val); + zend_clone_zval(&q->val); } } static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce) { - uint idx; - Bucket *p, *q; + Bucket *p, *q, *end; zend_ulong nIndex; zend_op_array *new_entry; @@ -371,9 +294,10 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); HT_HASH_RESET(ht); - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; nIndex = p->h | ht->nTableMask; @@ -385,7 +309,7 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class /* Initialize key */ q->h = p->h; ZEND_ASSERT(p->key != NULL); - q->key = zend_clone_str(p->key); + q->key = p->key; /* Copy data */ ZVAL_PTR(&q->val, ARENA_REALLOC(Z_PTR(p->val))); @@ -406,8 +330,7 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce) { - uint idx; - Bucket *p, *q; + Bucket *p, *q, *end; zend_ulong nIndex; zend_property_info *prop_info; @@ -429,9 +352,10 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla HT_SET_DATA_ADDR(ht, emalloc(HT_SIZE(ht))); HT_HASH_RESET(ht); - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; nIndex = p->h | ht->nTableMask; @@ -443,7 +367,7 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla /* Initialize key */ q->h = p->h; ZEND_ASSERT(p->key != NULL); - q->key = zend_clone_str(p->key); + q->key = p->key; /* Copy data */ prop_info = ARENA_REALLOC(Z_PTR(p->val)); @@ -451,7 +375,6 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla if (prop_info->ce == old_ce || (prop_info->flags & ZEND_ACC_SHADOW)) { /* Copy constructor */ - prop_info->name = zend_clone_str(prop_info->name); if (prop_info->doc_comment) { if (ZCG(accel_directives).load_comments) { prop_info->doc_comment = zend_string_dup(prop_info->doc_comment, 0); @@ -479,17 +402,19 @@ static void zend_class_copy_ctor(zend_class_entry **pce) { zend_class_entry *ce = *pce; zend_class_entry *old_ce = ce; + zval *src, *dst, *end; *pce = ce = ARENA_REALLOC(old_ce); ce->refcount = 1; if (old_ce->default_properties_table) { - int i; - ce->default_properties_table = emalloc(sizeof(zval) * old_ce->default_properties_count); - for (i = 0; i < old_ce->default_properties_count; i++) { - ZVAL_COPY_VALUE(&ce->default_properties_table[i], &old_ce->default_properties_table[i]); - zend_clone_zval(&ce->default_properties_table[i], 1); + src = old_ce->default_properties_table; + end = src + old_ce->default_properties_count; + dst = ce->default_properties_table; + for (; src != end; src++, dst++) { + ZVAL_COPY_VALUE(dst, src); + zend_clone_zval(dst); } } @@ -497,12 +422,13 @@ static void zend_class_copy_ctor(zend_class_entry **pce) /* static members */ if (old_ce->default_static_members_table) { - int i; - ce->default_static_members_table = emalloc(sizeof(zval) * old_ce->default_static_members_count); - for (i = 0; i < old_ce->default_static_members_count; i++) { - ZVAL_COPY_VALUE(&ce->default_static_members_table[i], &old_ce->default_static_members_table[i]); - zend_clone_zval(&ce->default_static_members_table[i], 1); + src = old_ce->default_static_members_table; + end = src + old_ce->default_static_members_count; + dst = ce->default_static_members_table; + for (; src != end; src++, dst++) { + ZVAL_COPY_VALUE(dst, src); + zend_clone_zval(dst); } } ce->static_members_table = ce->default_static_members_table; @@ -511,11 +437,9 @@ static void zend_class_copy_ctor(zend_class_entry **pce) zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce); /* constants table */ - zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 1); + zend_hash_clone_constants(&ce->constants_table, &old_ce->constants_table); ce->constants_table.u.flags &= ~HASH_FLAG_APPLY_PROTECTION; - ce->name = zend_clone_str(ce->name); - /* interfaces aren't really implemented, so we create a new table */ if (ce->num_interfaces) { ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces); @@ -568,21 +492,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce) memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias)); trait_aliases[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); memcpy(trait_aliases[i]->trait_method, ce->trait_aliases[i]->trait_method, sizeof(zend_trait_method_reference)); - if (trait_aliases[i]->trait_method) { - if (trait_aliases[i]->trait_method->method_name) { - trait_aliases[i]->trait_method->method_name = - zend_clone_str(trait_aliases[i]->trait_method->method_name); - } - if (trait_aliases[i]->trait_method->class_name) { - trait_aliases[i]->trait_method->class_name = - zend_clone_str(trait_aliases[i]->trait_method->class_name); - } - } - - if (trait_aliases[i]->alias) { - trait_aliases[i]->alias = - zend_clone_str(trait_aliases[i]->alias); - } i++; } trait_aliases[i] = NULL; @@ -604,11 +513,6 @@ static void zend_class_copy_ctor(zend_class_entry **pce) trait_precedences[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); memcpy(trait_precedences[i]->trait_method, ce->trait_precedences[i]->trait_method, sizeof(zend_trait_method_reference)); - trait_precedences[i]->trait_method->method_name = - zend_clone_str(trait_precedences[i]->trait_method->method_name); - trait_precedences[i]->trait_method->class_name = - zend_clone_str(trait_precedences[i]->trait_method->class_name); - if (trait_precedences[i]->exclude_from_classes) { zend_string **exclude_from_classes; int j = 0; @@ -620,7 +524,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce) j = 0; while (trait_precedences[i]->exclude_from_classes[j].class_name) { exclude_from_classes[j] = - zend_clone_str(trait_precedences[i]->exclude_from_classes[j].class_name); + trait_precedences[i]->exclude_from_classes[j].class_name; j++; } exclude_from_classes[j] = NULL; @@ -636,23 +540,25 @@ static void zend_class_copy_ctor(zend_class_entry **pce) static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) { zend_function *function1, *function2; - uint idx; - Bucket *p; + Bucket *p, *end; zval *t; - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; + zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0); + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; ZEND_ASSERT(p->key); - t = zend_hash_add(target, p->key, &p->val); - if (UNEXPECTED(t == NULL)) { - if (p->key->len > 0 && p->key->val[0] == 0) { + t = zend_hash_find(target, p->key); + if (UNEXPECTED(t != NULL)) { + if (EXPECTED(p->key->len > 0) && EXPECTED(p->key->val[0] == 0)) { /* Mangled key */ t = zend_hash_update(target, p->key, &p->val); } else { - t = zend_hash_find(target, p->key); goto failure; } + } else { + _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); } } target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; @@ -678,25 +584,26 @@ failure: static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable *source) { zend_function *function1, *function2; - uint idx; - Bucket *p; + Bucket *p, *end; zval *t; - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; + zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0); + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; ZEND_ASSERT(p->key); - t = zend_hash_add(target, p->key, &p->val); - if (UNEXPECTED(t == NULL)) { - if (p->key->len > 0 && p->key->val[0] == 0) { + t = zend_hash_find(target, p->key); + if (UNEXPECTED(t != NULL)) { + if (EXPECTED(p->key->len > 0) && EXPECTED(p->key->val[0] == 0)) { /* Mangled key */ - t = zend_hash_update(target, p->key, &p->val); + zend_hash_update_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val))); } else { - t = zend_hash_find(target, p->key); goto failure; } + } else { + _zend_hash_append_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val))); } - Z_PTR_P(t) = ARENA_REALLOC(Z_PTR(p->val)); } target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; return; @@ -721,26 +628,28 @@ failure: static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor) { zend_class_entry *ce1; - uint idx; - Bucket *p; + Bucket *p, *end; zval *t; - for (idx = 0; idx < source->nNumUsed; idx++) { - p = source->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; + zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0); + p = source->arData; + end = p + source->nNumUsed; + for (; p != end; p++) { + if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; ZEND_ASSERT(p->key); - t = zend_hash_add(target, p->key, &p->val); - if (UNEXPECTED(t == NULL)) { - if (p->key->len > 0 && p->key->val[0] == 0) { + t = zend_hash_find(target, p->key); + if (UNEXPECTED(t != NULL)) { + if (EXPECTED(p->key->len > 0) && EXPECTED(p->key->val[0] == 0)) { /* Mangled key - ignore and wait for runtime */ continue; - } else if (!ZCG(accel_directives).ignore_dups) { - t = zend_hash_find(target, p->key); + } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { goto failure; } - } - if (pCopyConstructor) { - pCopyConstructor(&Z_PTR_P(t)); + } else { + t = _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); + if (pCopyConstructor) { + pCopyConstructor(&Z_PTR_P(t)); + } } } target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 52ee49d5e1..10a149c9f6 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -113,8 +113,6 @@ static int zend_file_cache_flock(int fd, int type) ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \ /* script->corrupted shows if the script in SHM or not */ \ if (EXPECTED(script->corrupted)) { \ - GC_FLAGS(ptr) |= IS_STR_INTERNED | IS_STR_PERMANENT; \ - } else { \ GC_FLAGS(ptr) |= IS_STR_INTERNED; \ GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \ } \ @@ -125,12 +123,12 @@ static int zend_file_cache_flock(int fd, int type) #define UNSERIALIZE_STR(ptr) do { \ if (ptr) { \ if (IS_SERIALIZED_INTERNED(ptr)) { \ - (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), script->corrupted); \ + (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \ } else { \ ZEND_ASSERT(IS_SERIALIZED(ptr)); \ (ptr) = (void*)((char*)buf + (size_t)(ptr)); \ /* script->corrupted shows if the script in SHM or not */ \ - if (EXPECTED(script->corrupted)) { \ + if (EXPECTED(!script->corrupted)) { \ GC_FLAGS(ptr) |= IS_STR_INTERNED | IS_STR_PERMANENT; \ } else { \ GC_FLAGS(ptr) |= IS_STR_INTERNED; \ @@ -440,6 +438,9 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra if (!IS_SERIALIZED(p->class_name)) { SERIALIZE_STR(p->class_name); } + if (p->class_name && !IS_SERIALIZED(p->lower_class_name)) { + SERIALIZE_STR(p->lower_class_name); + } p++; } } @@ -722,9 +723,13 @@ int zend_file_cache_script_store(zend_persistent_script *script, int in_shm) ZCG(mem) = zend_string_alloc(4096 - (_STR_HEADER_SIZE + 1), 0); zend_shared_alloc_init_xlat_table(); - script->corrupted = in_shm; /* used to check if script restored to SHM or process memory */ + if (!in_shm) { + script->corrupted = 1; /* used to check if script restored to SHM or process memory */ + } zend_file_cache_serialize(script, &info, buf); - script->corrupted = 0; + if (!in_shm) { + script->corrupted = 0; + } zend_shared_alloc_destroy_xlat_table(); info.checksum = zend_adler32(ADLER32_INIT, buf, script->size); @@ -960,6 +965,9 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr if (!IS_UNSERIALIZED(p->class_name)) { UNSERIALIZE_STR(p->class_name); } + if (p->class_name && !IS_UNSERIALIZED(p->lower_class_name)) { + UNSERIALIZE_STR(p->lower_class_name); + } p++; } } @@ -1307,7 +1315,7 @@ use_process_mem: ZCG(mem) = ((char*)mem + info.mem_size); script = (zend_persistent_script*)((char*)buf + info.script_offset); - script->corrupted = cache_it; /* used to check if script restored to SHM or process memory */ + script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */ zend_file_cache_unserialize(script, buf); script->corrupted = 0; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index d1c1bb279a..585c34f46e 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -342,6 +342,7 @@ static void zend_persist_zval_static(zval *z) new_ptr = zend_shared_alloc_get_xlat_entry(Z_AST_P(z)); if (new_ptr) { Z_AST_P(z) = new_ptr; + Z_TYPE_FLAGS_P(z) = IS_TYPE_CONSTANT | IS_TYPE_IMMUTABLE; } else { zend_accel_store(Z_AST_P(z), sizeof(zend_ast_ref)); Z_ASTVAL_P(z) = zend_persist_ast(Z_ASTVAL_P(z)); @@ -580,6 +581,9 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } if (arg_info[i].class_name) { zend_accel_store_interned_string(arg_info[i].class_name); + if (arg_info[i].lower_class_name) { + zend_accel_store_interned_string(arg_info[i].lower_class_name); + } } } } diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 98412109a5..d721db2feb 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -225,6 +225,9 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } if (arg_info[i].class_name) { ADD_INTERNED_STRING(arg_info[i].class_name, 1); + if (arg_info[i].lower_class_name) { + ADD_INTERNED_STRING(arg_info[i].lower_class_name, 1); + } } } } diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 19b51e7c59..ca50ab9219 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -435,7 +435,7 @@ void zend_shared_alloc_clear_xlat_table(void) void zend_shared_alloc_register_xlat_entry(const void *old, const void *new) { - zend_hash_index_update_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new); + zend_hash_index_add_new_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new); } void *zend_shared_alloc_get_xlat_entry(const void *old) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 1ae741136d..c79447097d 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -3217,7 +3217,7 @@ static EVP_PKEY * php_openssl_evp_from_zval(zval * val, int public_key, char * p if (Z_TYPE_P(zphrase) == IS_STRING) { passphrase = Z_STRVAL_P(zphrase); } else { - ZVAL_DUP(&tmp, zphrase); + ZVAL_COPY(&tmp, zphrase); convert_to_string(&tmp); passphrase = Z_STRVAL(tmp); } @@ -5253,7 +5253,7 @@ PHP_FUNCTION(openssl_encrypt) /* }}} */ /* {{{ proto string openssl_decrypt(string data, string method, string password [, long options=0 [, string $iv = '']]) - Takes raw or base64 encoded string and dectupt it using given method and key */ + Takes raw or base64 encoded string and decrypts it using given method and key */ PHP_FUNCTION(openssl_decrypt) { zend_long options = 0; diff --git a/ext/pcre/pcrelib/config.h b/ext/pcre/pcrelib/config.h index d0fde3b497..9714cf5bbd 100644 --- a/ext/pcre/pcrelib/config.h +++ b/ext/pcre/pcrelib/config.h @@ -262,7 +262,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_NAME "PCRE" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE 8.36" +#define PACKAGE_STRING "PCRE 8.37" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre" @@ -271,7 +271,7 @@ sure both macros are undefined; an emulation function will then be used. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "8.36" +#define PACKAGE_VERSION "8.37" /* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested parentheses (of any kind) in a pattern. This limits the amount of system @@ -364,7 +364,7 @@ sure both macros are undefined; an emulation function will then be used. */ /* Version number of package */ #ifndef VERSION -#define VERSION "8.36" +#define VERSION "8.37" #endif /* Define to empty if `const' does not conform to ANSI C. */ diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 8189473524..76a5eb4520 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1364,6 +1364,14 @@ static int dbh_compare(zval *object1, zval *object2) return -1; } +static HashTable *dbh_get_gc(zval *object, zval **gc_data, int *gc_count) +{ + pdo_dbh_t *dbh = Z_PDO_DBH_P(object); + *gc_data = &dbh->def_stmt_ctor_args; + *gc_count = 1; + return zend_std_get_properties(object); +} + static zend_object_handlers pdo_dbh_object_handlers; static void pdo_dbh_free_storage(zend_object *std); @@ -1381,6 +1389,7 @@ void pdo_dbh_init(void) pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage; pdo_dbh_object_handlers.get_method = dbh_method_get; pdo_dbh_object_handlers.compare_objects = dbh_compare; + pdo_dbh_object_handlers.get_gc = dbh_get_gc; REGISTER_PDO_CLASS_CONST_LONG("PARAM_BOOL", (zend_long)PDO_PARAM_BOOL); REGISTER_PDO_CLASS_CONST_LONG("PARAM_NULL", (zend_long)PDO_PARAM_NULL); diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 0a7ce6368d..f913eb7c31 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -27,6 +27,7 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_string.h" #include "main/php_network.h" #include "pdo/php_pdo.h" #include "pdo/php_pdo_driver.h" @@ -62,6 +63,17 @@ static char * _pdo_pgsql_trim_message(const char *message, int persistent) return tmp; } +static zend_string* _pdo_pgsql_escape_credentials(char *str) +{ + if (str) { + zend_string *tmp = zend_string_init(str, strlen(str), 0); + + return php_addcslashes(tmp, 1, "\\'", sizeof("\\'")); + } + + return NULL; +} + int _pdo_pgsql_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, int errcode, const char *sqlstate, const char *msg, const char *file, int line) /* {{{ */ { pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; @@ -1179,7 +1191,7 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ pdo_pgsql_db_handle *H; int ret = 0; char *conn_str, *p, *e; - char *tmp_pass; + zend_string *tmp_user, *tmp_pass; zend_long connect_timeout = 30; H = pecalloc(1, sizeof(pdo_pgsql_db_handle), dbh->is_persistent); @@ -1201,43 +1213,28 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30); } - if (dbh->password) { - if (dbh->password[0] != '\'' && dbh->password[strlen(dbh->password) - 1] != '\'') { - char *pwd = dbh->password; - int pos = 1; - - tmp_pass = safe_emalloc(2, strlen(dbh->password), 3); - tmp_pass[0] = '\''; - - while (*pwd != '\0') { - if (*pwd == '\\' || *pwd == '\'') { - tmp_pass[pos++] = '\\'; - } - - tmp_pass[pos++] = *pwd++; - } - - tmp_pass[pos++] = '\''; - tmp_pass[pos] = '\0'; - } else { - tmp_pass = dbh->password; - } - } + /* escape username and password, if provided */ + tmp_user = _pdo_pgsql_escape_credentials(dbh->username); + tmp_pass = _pdo_pgsql_escape_credentials(dbh->password); /* support both full connection string & connection string + login and/or password */ - if (dbh->username && dbh->password) { - spprintf(&conn_str, 0, "%s user=%s password=%s connect_timeout=%pd", dbh->data_source, dbh->username, tmp_pass, connect_timeout); - } else if (dbh->username) { - spprintf(&conn_str, 0, "%s user=%s connect_timeout=%pd", dbh->data_source, dbh->username, connect_timeout); - } else if (dbh->password) { - spprintf(&conn_str, 0, "%s password=%s connect_timeout=%pd", dbh->data_source, tmp_pass, connect_timeout); + if (tmp_user && tmp_pass) { + spprintf(&conn_str, 0, "%s user='%s' password='%s' connect_timeout=%pd", (char *) dbh->data_source, tmp_user->val, tmp_pass->val, connect_timeout); + } else if (tmp_user) { + spprintf(&conn_str, 0, "%s user='%s' connect_timeout=%pd", (char *) dbh->data_source, tmp_user->val, connect_timeout); + } else if (tmp_pass) { + spprintf(&conn_str, 0, "%s password='%s' connect_timeout=%pd", (char *) dbh->data_source, tmp_pass->val, connect_timeout); } else { spprintf(&conn_str, 0, "%s connect_timeout=%pd", (char *) dbh->data_source, connect_timeout); } H->server = PQconnectdb(conn_str); - if (dbh->password && tmp_pass != dbh->password) { - efree(tmp_pass); + + if (tmp_user) { + zend_string_release(tmp_user); + } + if (tmp_pass) { + zend_string_release(tmp_pass); } efree(conn_str); diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 60553b36ce..9c2a0e2efd 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -224,7 +224,7 @@ stmt_retry: return 0; } - if (!stmt->executed && !stmt->column_count) { + if (!stmt->executed && (!stmt->column_count || S->cols == NULL)) { stmt->column_count = (int) PQnfields(S->result); S->cols = ecalloc(stmt->column_count, sizeof(pdo_pgsql_column)); } @@ -301,8 +301,8 @@ static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data * if (param->paramno >= 0) { zval *parameter; - if (param->paramno >= zend_hash_num_elements(stmt->bound_param_map)) { - pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + if (param->paramno >= zend_hash_num_elements(stmt->bound_params)) { + pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined"); return 0; } @@ -618,6 +618,12 @@ done: static int pdo_pgsql_stmt_cursor_closer(pdo_stmt_t *stmt) { + pdo_pgsql_stmt *S = (pdo_pgsql_stmt*)stmt->driver_data; + + if (S->cols != NULL){ + efree(S->cols); + S->cols = NULL; + } return 1; } diff --git a/ext/pdo_pgsql/tests/bug69344.phpt b/ext/pdo_pgsql/tests/bug69344.phpt new file mode 100644 index 0000000000..d274e0608c --- /dev/null +++ b/ext/pdo_pgsql/tests/bug69344.phpt @@ -0,0 +1,44 @@ +--TEST-- +PDO PgSQL Bug #69344 (PDO PgSQL Incorrect binding numeric array with gaps) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded'); +require dirname(__FILE__) . '/config.inc'; +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php + +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +$pdo = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); +$pdo->setAttribute (\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute (\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC); + +$test = function () use ($pdo) { + $arr = [ + 0 => "a", + 2 => "b", + ]; + + $stmt = $pdo->prepare("SELECT (?)::text AS a, (?)::text AS b"); + + try { + $stmt->execute($arr); + var_dump($stmt->fetch()); + } catch (\Exception $e) { + echo $e->getMessage()."\n"; + } +}; + +$test(); + +$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, true); + +$test(); + +?> +--EXPECT-- +SQLSTATE[HY093]: Invalid parameter number: parameter was not defined +SQLSTATE[HY093]: Invalid parameter number: parameter was not defined + diff --git a/ext/pdo_pgsql/tests/bug69362.phpt b/ext/pdo_pgsql/tests/bug69362.phpt new file mode 100644 index 0000000000..33212a8863 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug69362.phpt @@ -0,0 +1,63 @@ +--TEST-- +PDO PgSQL Bug #69362 (PDO-pgsql fails to connect if password contains a leading single quote) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded'); +require dirname(__FILE__) . '/config.inc'; +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +PDOTest::skip(); + +$dsn = getenv('PDOTEST_DSN'); +if (empty($dsn)) die('skip no dsn found in env'); + +$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + +$user = 'pdo_test_'.rand(5, 400); +$pass = 'testpass'; + +// Assume that if we can't create or drop a user, this test needs to be skipped +try { + $db->exec("DROP USER IF EXISTS $user"); + $db->exec("CREATE USER $user WITH PASSWORD '$pass'"); +} catch (PDOException $e) { + die("skip You need CREATEUSER permissions to run the test"); +} + +// Peer authentication might prevent the test from properly running +try { + $testConn = new PDO($dsn, $user, $pass); +} catch (PDOException $e) { + echo "skip ".$e->getMessage(); +} + +$db->exec("DROP USER $user"); + +?> +--FILE-- +<?php +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +$pdo = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); +$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); +$rand = rand(5, 400); +$user = "pdo_test_$rand"; +$template = "CREATE USER $user WITH PASSWORD '%s'"; +$dropUser = "DROP USER $user"; +$testQuery = 'SELECT 1 as verification'; + +// Create temp user with leading single quote +$sql = sprintf($template, "''mypassword"); +$pdo->query($sql); +$testConn = new PDO($conf['ENV']['PDOTEST_DSN'], $user, "'mypassword"); +$result = $testConn->query($testQuery)->fetch(); +$check = $result[0]; +var_dump($check); + +// Remove the user +$pdo->query($dropUser); + +?> +--EXPECT-- +int(1) + diff --git a/ext/pdo_pgsql/tests/bug69752.phpt b/ext/pdo_pgsql/tests/bug69752.phpt new file mode 100644 index 0000000000..bb7e5e87e7 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug69752.phpt @@ -0,0 +1,55 @@ +--TEST-- +PDO PgSQL Bug #69752 (memory leak with closeCursor) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded'); +require dirname(__FILE__) . '/config.inc'; +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +$pdo = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); + +$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + +$pdo->beginTransaction(); + +$pdo->exec(" + create table foo ( + id bigserial not null primary key, + field1 text not null, + field2 text not null, + field3 text not null, + field4 int4 not null + ) +"); +$stmt = $pdo->prepare("insert into foo (field1, field2, field3, field4) values (:field1, :field2, :field3, :field4)"); +$max = 1000; +$first_time_usage = null; + +for($i = 0; $i < $max; $i++) { + $data = array( + 'field1' => "field1: $i", + 'field2' => "field2: $i", + 'field3' => "field3: $i", + 'field4' => $i + ); + $stmt->execute($data); + $stmt->closeCursor(); + $usage = intval(floor(memory_get_usage() / 1024)); + + if ($first_time_usage === null) $first_time_usage = $usage; + + /* Use delta instead of direct comparison here */ + if (abs($first_time_usage - $usage) > 3){ + printf("Memory Leak Detected: %d != %d\n", $usage, $first_time_usage); + break; + } +} +$pdo->rollBack(); +echo "done\n" +?> +--EXPECTF-- +done diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 6a86b8941b..6965589154 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -4246,7 +4246,7 @@ PHP_FUNCTION(pg_copy_from) #if HAVE_PQPUTCOPYDATA ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) { zval tmp; - ZVAL_DUP(&tmp, value); + ZVAL_COPY(&tmp, value); convert_to_string_ex(&tmp); query = (char *)emalloc(Z_STRLEN(tmp) + 2); strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2); @@ -4270,7 +4270,7 @@ PHP_FUNCTION(pg_copy_from) #else ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), value) { zval tmp; - ZVAL_DUP(&tmp, value); + ZVAL_COPY(&tmp, value); convert_to_string_ex(&tmp); query = (char *)emalloc(Z_STRLEN(tmp) + 2); strlcpy(query, Z_STRVAL(tmp), Z_STRLEN(tmp) + 2); @@ -5504,7 +5504,11 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z src = estrdup(table_name); tmp_name = php_strtok_r(src, ".", &tmp_name2); - + if (!tmp_name) { + efree(src); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The table name must be specified"); + return FAILURE; + } if (!tmp_name2 || !*tmp_name2) { /* Default schema */ tmp_name2 = tmp_name; @@ -6502,7 +6506,8 @@ static int do_exec(smart_str *querystr, int expect, PGconn *pg_link, zend_ulong static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */ { - char *table_copy, *escaped, *token, *tmp; + char *table_copy, *escaped, *tmp; + const char *token; size_t len; /* schame.table should be "schame"."table" */ diff --git a/ext/pgsql/tests/pg_insert_002.phpt b/ext/pgsql/tests/pg_insert_002.phpt new file mode 100644 index 0000000000..329f525b27 --- /dev/null +++ b/ext/pgsql/tests/pg_insert_002.phpt @@ -0,0 +1,27 @@ +--TEST-- +PostgreSQL pg_insert() - test for CVE-2015-1532 +--SKIPIF-- +<?php include("skipif.inc"); ?> +--FILE-- +<?php + +include('config.inc'); + +$conn = pg_connect($conn_str); + +foreach (array('', '.', '..') as $table) { + var_dump(pg_insert($conn, $table, array('id' => 1, 'id2' => 1))); +} +?> +Done +--EXPECTF-- + +Warning: pg_insert(): The table name must be specified in %s on line %d +bool(false) + +Warning: pg_insert(): The table name must be specified in %s on line %d +bool(false) + +Warning: pg_insert(): The table name must be specified in %s on line %d +bool(false) +Done
\ No newline at end of file diff --git a/ext/phar/Makefile.frag b/ext/phar/Makefile.frag index e5d756fa16..e58795deea 100644 --- a/ext/phar/Makefile.frag +++ b/ext/phar/Makefile.frag @@ -39,7 +39,7 @@ install-pharcmd: pharcmd -@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir) $(INSTALL) $(builddir)/phar.phar $(INSTALL_ROOT)$(bindir) -@rm -f $(INSTALL_ROOT)$(bindir)/phar - $(LN_S) -f $(INSTALL_ROOT)$(bindir)/phar.phar $(INSTALL_ROOT)$(bindir)/phar + $(LN_S) -f phar.phar $(INSTALL_ROOT)$(bindir)/phar @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1 @$(INSTALL_DATA) $(builddir)/phar.1 $(INSTALL_ROOT)$(mandir)/man1/phar.1 @$(INSTALL_DATA) $(builddir)/phar.phar.1 $(INSTALL_ROOT)$(mandir)/man1/phar.phar.1 diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 8cadeb5a4a..c4912649db 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -429,18 +429,6 @@ skip_phar: } /* }}} */ -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif -#ifndef S_ISLNK -#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) -#endif - -#define S_IXROOT ( S_IXUSR | S_IXGRP | S_IXOTH ) - #define IS_LINK_OPERATION(__t) ((__t) == FS_TYPE || (__t) == FS_IS_LINK || (__t) == FS_LSTAT) #define IS_EXISTS_CHECK(__t) ((__t) == FS_EXISTS || (__t) == FS_IS_W || (__t) == FS_IS_R || (__t) == FS_IS_X || (__t) == FS_IS_FILE || (__t) == FS_IS_DIR || (__t) == FS_IS_LINK) #define IS_ABLE_CHECK(__t) ((__t) == FS_IS_R || (__t) == FS_IS_W || (__t) == FS_IS_X) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 8ef5b0f7ed..11e5d5a7f1 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -3965,14 +3965,13 @@ PHP_METHOD(Phar, getMetadata) if (Z_TYPE(phar_obj->archive->metadata) != IS_UNDEF) { if (phar_obj->archive->is_persistent) { - zval ret; char *buf = estrndup((char *) Z_PTR(phar_obj->archive->metadata), phar_obj->archive->metadata_len); /* assume success, we would have failed before */ - phar_parse_metadata(&buf, &ret, phar_obj->archive->metadata_len); + phar_parse_metadata(&buf, return_value, phar_obj->archive->metadata_len); efree(buf); - RETURN_ZVAL(&ret, 0, 1); + } else { + ZVAL_COPY(return_value, &phar_obj->archive->metadata); } - RETURN_ZVAL(&phar_obj->archive->metadata, 1, 0); } } /* }}} */ @@ -4005,7 +4004,7 @@ PHP_METHOD(Phar, setMetadata) ZVAL_UNDEF(&phar_obj->archive->metadata); } - ZVAL_ZVAL(&phar_obj->archive->metadata, metadata, 1, 0); + ZVAL_COPY(&phar_obj->archive->metadata, metadata); phar_obj->archive->is_modified = 1; phar_flush(phar_obj->archive, 0, 0, 0, &error); @@ -4622,14 +4621,13 @@ PHP_METHOD(PharFileInfo, getMetadata) if (Z_TYPE(entry_obj->entry->metadata) != IS_UNDEF) { if (entry_obj->entry->is_persistent) { - zval ret; char *buf = estrndup((char *) Z_PTR(entry_obj->entry->metadata), entry_obj->entry->metadata_len); /* assume success, we would have failed before */ - phar_parse_metadata(&buf, &ret, entry_obj->entry->metadata_len); + phar_parse_metadata(&buf, return_value, entry_obj->entry->metadata_len); efree(buf); - RETURN_ZVAL(&ret, 0, 1); + } else { + ZVAL_COPY(return_value, &entry_obj->entry->metadata); } - RETURN_ZVAL(&entry_obj->entry->metadata, 1, 0); } } /* }}} */ @@ -4674,7 +4672,7 @@ PHP_METHOD(PharFileInfo, setMetadata) ZVAL_UNDEF(&entry_obj->entry->metadata); } - ZVAL_ZVAL(&entry_obj->entry->metadata, metadata, 1, 0); + ZVAL_COPY(&entry_obj->entry->metadata, metadata); entry_obj->entry->is_modified = 1; entry_obj->entry->phar->is_modified = 1; diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 81b08de36a..52df81971b 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -460,9 +460,7 @@ bail: entry.flags = phar_tar_number(hdr->mode, sizeof(hdr->mode)) & PHAR_ENT_PERM_MASK; entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime)); entry.is_persistent = myphar->is_persistent; -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif + if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry.flags)) { entry.tar_type = TAR_DIR; } diff --git a/ext/phar/tests/bug69441.phpt b/ext/phar/tests/bug69441.phpt index 3e995e03c0..3536c3a835 100644 --- a/ext/phar/tests/bug69441.phpt +++ b/ext/phar/tests/bug69441.phpt @@ -14,7 +14,7 @@ $r = new Phar($fname, 0); ==DONE== --EXPECTF-- -UnexpectedValueException: phar error: corrupted central directory entry, no magic signature in zip-based phar "%s%ebug69441.phar" in %s%ebug69441.php:%d +UnexpectedValueException: phar error: corrupted central directory entry, no magic signature in zip-based phar "%sbug69441.phar" in %sbug69441.php:%d Stack trace: #0 %s%ebug69441.php(%d): Phar->__construct('%s', 0) #1 {main} diff --git a/ext/phar/tests/bug69453.phpt b/ext/phar/tests/bug69453.phpt index 12b437639a..6f280a5351 100644 --- a/ext/phar/tests/bug69453.phpt +++ b/ext/phar/tests/bug69453.phpt @@ -14,8 +14,8 @@ $r = new Phar($fname, 0); ==DONE== --EXPECTF-- -UnexpectedValueException: phar error: "%s/bug69453.tar.phar" is a corrupted tar file (checksum mismatch of file "") in %s:%d +UnexpectedValueException: phar error: "%s%ebug69453.tar.phar" is a corrupted tar file (checksum mismatch of file "") in %s:%d Stack trace: -#0 %s/bug69453.php(%d): Phar->__construct('%s', 0) +#0 %s%ebug69453.php(%d): Phar->__construct('%s', 0) #1 {main} -==DONE==
\ No newline at end of file +==DONE== diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 92f67972d2..4c570d7573 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -59,6 +59,7 @@ PHPAPI zend_class_entry *reflection_function_abstract_ptr; PHPAPI zend_class_entry *reflection_function_ptr; PHPAPI zend_class_entry *reflection_generator_ptr; PHPAPI zend_class_entry *reflection_parameter_ptr; +PHPAPI zend_class_entry *reflection_type_ptr; PHPAPI zend_class_entry *reflection_class_ptr; PHPAPI zend_class_entry *reflection_object_ptr; PHPAPI zend_class_entry *reflection_method_ptr; @@ -201,11 +202,18 @@ typedef struct _parameter_reference { zend_function *fptr; } parameter_reference; +/* Struct for type hints */ +typedef struct _type_reference { + struct _zend_arg_info *arg_info; + zend_function *fptr; +} type_reference; + typedef enum { REF_TYPE_OTHER, /* Must be 0 */ REF_TYPE_FUNCTION, REF_TYPE_GENERATOR, REF_TYPE_PARAMETER, + REF_TYPE_TYPE, REF_TYPE_PROPERTY, REF_TYPE_DYNAMIC_PROPERTY } reflection_type_t; @@ -299,6 +307,7 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ reflection_object *intern = reflection_object_from_obj(object); parameter_reference *reference; property_reference *prop_reference; + type_reference *typ_reference; if (intern->ptr) { switch (intern->ref_type) { @@ -307,6 +316,11 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ _free_function(reference->fptr); efree(intern->ptr); break; + case REF_TYPE_TYPE: + typ_reference = (type_reference*)intern->ptr; + _free_function(typ_reference->fptr); + efree(intern->ptr); + break; case REF_TYPE_FUNCTION: _free_function(intern->ptr); break; @@ -1236,6 +1250,27 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje } /* }}} */ +/* {{{ reflection_type_factory */ +static void reflection_type_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, zval *object) +{ + reflection_object *intern; + type_reference *reference; + + reflection_instantiate(reflection_type_ptr, object); + intern = Z_REFLECTION_P(object); + reference = (type_reference*) emalloc(sizeof(type_reference)); + reference->arg_info = arg_info; + reference->fptr = fptr; + intern->ptr = reference; + intern->ref_type = REF_TYPE_TYPE; + intern->ce = fptr->common.scope; + if (closure_object) { + Z_ADDREF_P(closure_object); + ZVAL_COPY_VALUE(&intern->obj, closure_object); + } +} +/* }}} */ + /* {{{ reflection_function_factory */ static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) { @@ -1685,7 +1720,7 @@ ZEND_METHOD(reflection_function, getClosureThis) if (!Z_ISUNDEF(intern->obj)) { closure_this = zend_get_closure_this_ptr(&intern->obj); if (!Z_ISUNDEF_P(closure_this)) { - RETURN_ZVAL(closure_this, 1, 0); + ZVAL_COPY(return_value, closure_this); } } } @@ -2504,6 +2539,7 @@ ZEND_METHOD(reflection_parameter, __toString) _parameter_string(&str, param->fptr, param->arg_info, param->offset, param->required, ""); RETURN_NEW_STR(str.buf); } + /* }}} */ /* {{{ proto public string ReflectionParameter::getName() @@ -2538,7 +2574,7 @@ ZEND_METHOD(reflection_parameter, getDeclaringFunction) /* }}} */ /* {{{ proto public ReflectionClass|NULL ReflectionParameter::getDeclaringClass() - Returns in which class this parameter is defined (not the typehint of the parameter) */ + Returns in which class this parameter is defined (not the type of the parameter) */ ZEND_METHOD(reflection_parameter, getDeclaringClass) { reflection_object *intern; @@ -2630,6 +2666,44 @@ ZEND_METHOD(reflection_parameter, getClass) } /* }}} */ +/* {{{ proto public bool ReflectionParameter::hasType() + Returns whether parameter has a type */ +ZEND_METHOD(reflection_parameter, hasType) +{ + reflection_object *intern; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint != 0); +} +/* }}} */ + +/* {{{ proto public ReflectionType ReflectionParameter::getType() + Returns the type associated with the parameter */ +ZEND_METHOD(reflection_parameter, getType) +{ + reflection_object *intern; + parameter_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(param); + + if ((param->fptr->type == ZEND_INTERNAL_FUNCTION ? + ((zend_internal_arg_info*)param->arg_info)->type_hint : + param->arg_info->type_hint) == 0) + { + RETURN_NULL(); + } + reflection_type_factory(_copy_function(param->fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, param->arg_info, return_value); +} +/* }}} */ + /* {{{ proto public bool ReflectionParameter::isArray() Returns whether parameter MUST be an array */ ZEND_METHOD(reflection_parameter, isArray) @@ -2867,6 +2941,69 @@ ZEND_METHOD(reflection_parameter, isVariadic) } /* }}} */ +/* {{{ proto public bool ReflectionType::allowsNull() + Returns whether parameter MAY be null */ +ZEND_METHOD(reflection_type, allowsNull) +{ + reflection_object *intern; + type_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->allow_null); +} +/* }}} */ + +/* {{{ proto public bool ReflectionType::isBuiltin() + Returns whether parameter is a builtin type */ +ZEND_METHOD(reflection_type, isBuiltin) +{ + reflection_object *intern; + type_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(param); + + RETVAL_BOOL(param->arg_info->type_hint != IS_OBJECT); +} +/* }}} */ + +/* {{{ proto public string ReflectionType::__toString() + Return the text of the type hint */ +ZEND_METHOD(reflection_type, __toString) +{ + reflection_object *intern; + type_reference *param; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + GET_REFLECTION_OBJECT_PTR(param); + + switch (param->arg_info->type_hint) { + case IS_ARRAY: RETURN_STRINGL("array", sizeof("array") - 1); + case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1); + case IS_OBJECT: + if (param->fptr->type == ZEND_INTERNAL_FUNCTION) { + if (!(param->fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + RETURN_STRING(((zend_internal_arg_info*)param->arg_info)->class_name); + } + } + RETURN_STR_COPY(param->arg_info->class_name); + case IS_STRING: RETURN_STRINGL("string", sizeof("string") - 1); + case _IS_BOOL: RETURN_STRINGL("bool", sizeof("bool") - 1); + case IS_LONG: RETURN_STRINGL("int", sizeof("int") - 1); + case IS_DOUBLE: RETURN_STRINGL("float", sizeof("float") - 1); + EMPTY_SWITCH_DEFAULT_CASE() + } +} +/* }}} */ + /* {{{ proto public static mixed ReflectionMethod::export(mixed class, string name [, bool return]) throws ReflectionException Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ ZEND_METHOD(reflection_method, export) @@ -3016,7 +3153,7 @@ ZEND_METHOD(reflection_method, getClosure) if (Z_OBJCE_P(obj) == zend_ce_closure && (mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - RETURN_ZVAL(obj, 1, 0); + ZVAL_COPY(return_value, obj); } else { zend_create_closure(return_value, mptr, mptr->common.scope, Z_OBJCE_P(obj), obj); } @@ -3375,7 +3512,46 @@ ZEND_METHOD(reflection_function, getShortName) { RETURN_STRINGL(backslash + 1, Z_STRLEN_P(name) - (backslash - Z_STRVAL_P(name) + 1)); } - RETURN_ZVAL(name, 1, 0); + ZVAL_DEREF(name); + ZVAL_COPY(return_value, name); +} +/* }}} */ + +/* {{{ proto public bool ReflectionFunctionAbstract:hasReturnType() + Return whether the function has a return type */ +ZEND_METHOD(reflection_function, hasReturnType) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + GET_REFLECTION_OBJECT_PTR(fptr); + + RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE); +} +/* }}} */ + +/* {{{ proto public string ReflectionFunctionAbstract::getReturnType() + Returns the return type associated with the function */ +ZEND_METHOD(reflection_function, getReturnType) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + GET_REFLECTION_OBJECT_PTR(fptr); + + if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { + RETURN_NULL(); + } + + reflection_type_factory(_copy_function(fptr), Z_ISUNDEF(intern->obj)? NULL : &intern->obj, &fptr->common.arg_info[-1], return_value); } /* }}} */ @@ -3644,14 +3820,15 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue) prop = zend_std_get_static_property(ce, name, 1); if (!prop) { if (def_value) { - RETURN_ZVAL(def_value, 1, 0); + ZVAL_COPY(return_value, def_value); } else { zend_throw_exception_ex(reflection_exception_ptr, 0, "Class %s does not have a property named %s", ce->name->val, name->val); } return; } else { - RETURN_ZVAL(prop, 1, 0); + ZVAL_DEREF(prop); + ZVAL_COPY(return_value, prop); } } /* }}} */ @@ -4963,7 +5140,8 @@ ZEND_METHOD(reflection_class, getShortName) { RETURN_STRINGL(backslash + 1, Z_STRLEN_P(name) - (backslash - Z_STRVAL_P(name) + 1)); } - RETURN_ZVAL(name, 1, 0); + ZVAL_DEREF(name); + ZVAL_COPY(return_value, name); } /* }}} */ @@ -5984,6 +6162,8 @@ static const zend_function_entry reflection_function_abstract_functions[] = { ZEND_ME(reflection_function, getStartLine, arginfo_reflection__void, 0) ZEND_ME(reflection_function, getStaticVariables, arginfo_reflection__void, 0) ZEND_ME(reflection_function, returnsReference, arginfo_reflection__void, 0) + ZEND_ME(reflection_function, hasReturnType, arginfo_reflection__void, 0) + ZEND_ME(reflection_function, getReturnType, arginfo_reflection__void, 0) PHP_FE_END }; @@ -6064,7 +6244,7 @@ static const zend_function_entry reflection_method_functions[] = { ZEND_ME(reflection_method, invokeArgs, arginfo_reflection_method_invokeArgs, 0) ZEND_ME(reflection_method, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_method, getPrototype, arginfo_reflection__void, 0) - ZEND_ME(reflection_property, setAccessible, arginfo_reflection_method_setAccessible, 0) + ZEND_ME(reflection_method, setAccessible, arginfo_reflection_method_setAccessible, 0) PHP_FE_END }; @@ -6281,6 +6461,8 @@ static const zend_function_entry reflection_parameter_functions[] = { ZEND_ME(reflection_parameter, getDeclaringFunction, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getClass, arginfo_reflection__void, 0) + ZEND_ME(reflection_parameter, hasType, arginfo_reflection__void, 0) + ZEND_ME(reflection_parameter, getType, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isArray, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isCallable, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, allowsNull, arginfo_reflection__void, 0) @@ -6294,6 +6476,14 @@ static const zend_function_entry reflection_parameter_functions[] = { PHP_FE_END }; +static const zend_function_entry reflection_type_functions[] = { + ZEND_ME(reflection, __clone, arginfo_reflection__void, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL) + ZEND_ME(reflection_type, allowsNull, arginfo_reflection__void, 0) + ZEND_ME(reflection_type, isBuiltin, arginfo_reflection__void, 0) + ZEND_ME(reflection_type, __toString, arginfo_reflection__void, 0) + PHP_FE_END +}; + ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_extension_export, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, return) @@ -6407,6 +6597,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ zend_class_implements(reflection_parameter_ptr, 1, reflector_ptr); zend_declare_property_string(reflection_parameter_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionType", reflection_type_functions); + _reflection_entry.create_object = reflection_objects_new; + reflection_type_ptr = zend_register_internal_class(&_reflection_entry); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionMethod", reflection_method_functions); _reflection_entry.create_object = reflection_objects_new; reflection_method_ptr = zend_register_internal_class_ex(&_reflection_entry, reflection_function_abstract_ptr); diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index 1bb47b16c8..b50309d9ff 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -37,6 +37,7 @@ extern PHPAPI zend_class_entry *reflection_ptr; extern PHPAPI zend_class_entry *reflection_function_abstract_ptr; extern PHPAPI zend_class_entry *reflection_function_ptr; extern PHPAPI zend_class_entry *reflection_parameter_ptr; +extern PHPAPI zend_class_entry *reflection_type_ptr; extern PHPAPI zend_class_entry *reflection_class_ptr; extern PHPAPI zend_class_entry *reflection_object_ptr; extern PHPAPI zend_class_entry *reflection_method_ptr; diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 508f73ee55..4eda22a3f9 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -9,7 +9,7 @@ var_dump($ext->getClasses()); ?> ==DONE== --EXPECT-- -array(13) { +array(14) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -45,33 +45,38 @@ array(13) { ["name"]=> string(19) "ReflectionParameter" } - ["ReflectionMethod"]=> + ["ReflectionType"]=> object(ReflectionClass)#9 (1) { ["name"]=> + string(14) "ReflectionType" + } + ["ReflectionMethod"]=> + object(ReflectionClass)#10 (1) { + ["name"]=> string(16) "ReflectionMethod" } ["ReflectionClass"]=> - object(ReflectionClass)#10 (1) { + object(ReflectionClass)#11 (1) { ["name"]=> string(15) "ReflectionClass" } ["ReflectionObject"]=> - object(ReflectionClass)#11 (1) { + object(ReflectionClass)#12 (1) { ["name"]=> string(16) "ReflectionObject" } ["ReflectionProperty"]=> - object(ReflectionClass)#12 (1) { + object(ReflectionClass)#13 (1) { ["name"]=> string(18) "ReflectionProperty" } ["ReflectionExtension"]=> - object(ReflectionClass)#13 (1) { + object(ReflectionClass)#14 (1) { ["name"]=> string(19) "ReflectionExtension" } ["ReflectionZendExtension"]=> - object(ReflectionClass)#14 (1) { + object(ReflectionClass)#15 (1) { ["name"]=> string(23) "ReflectionZendExtension" } diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt new file mode 100644 index 0000000000..f764cf1519 --- /dev/null +++ b/ext/reflection/tests/ReflectionType_001.phpt @@ -0,0 +1,185 @@ +--TEST-- +ReflectionParameter::get/hasType and ReflectionType tests +--FILE-- +<?php +function foo(stdClass $a, array $b, callable $c, stdClass $d = null, $e = null, string $f, bool $g, int $h, float $i, NotExisting $j) { } + +function bar(): stdClass { return new stdClass; } + +class c extends stdClass { + function bar(self $x): int { return 1; } + function pbar(parent $x): int { return 1; } + function factory(): self { return new c; } + function pfactory(): parent { return new stdClass; } +} + +$closure = function (Test $a): Test { return $a; }; + +echo "*** functions\n"; + +foreach ([ + new ReflectionFunction('foo'), + new ReflectionFunction($closure), +] as $idx => $rf) { + foreach ($rf->getParameters() as $idx2 => $rp) { + echo "** Function $idx - Parameter $idx2\n"; + var_dump($rp->hasType()); + $ra = $rp->getType(); + if ($ra) { + var_dump($ra->allowsNull()); + var_dump($ra->isBuiltin()); + var_dump((string)$ra); + } + } +} + +echo "\n*** methods\n"; + +foreach ([ + new ReflectionMethod('SplObserver', 'update'), + new ReflectionMethod('c', 'bar'), + new ReflectionMethod('c', 'pbar'), + new ReflectionMethod($closure, '__invoke'), +] as $idx => $rm) { + foreach ($rm->getParameters() as $idx2 => $rp) { + echo "** Method $idx - parameter $idx2\n"; + var_dump($rp->hasType()); + $ra = $rp->getType(); + if ($ra) { + var_dump($ra->allowsNull()); + var_dump($ra->isBuiltin()); + var_dump((string)$ra); + } + } +} + +echo "\n*** return types\n"; + +foreach ([ + new ReflectionMethod('SplObserver', 'update'), + new ReflectionFunction('bar'), + new ReflectionMethod('c', 'bar'), + new ReflectionMethod('c', 'factory'), + new ReflectionMethod('c', 'pfactory'), + new ReflectionFunction($closure), + new ReflectionMethod($closure, '__invoke'), +] as $idx => $rf) { + echo "** Function/method return type $idx\n"; + var_dump($rf->hasReturnType()); + $ra = $rf->getReturnType(); + if ($ra) { + var_dump($ra->allowsNull()); + var_dump($ra->isBuiltin()); + var_dump((string)$ra); + } +} +--EXPECT-- +*** functions +** Function 0 - Parameter 0 +bool(true) +bool(false) +bool(false) +string(8) "stdClass" +** Function 0 - Parameter 1 +bool(true) +bool(false) +bool(true) +string(5) "array" +** Function 0 - Parameter 2 +bool(true) +bool(false) +bool(true) +string(8) "callable" +** Function 0 - Parameter 3 +bool(true) +bool(true) +bool(false) +string(8) "stdClass" +** Function 0 - Parameter 4 +bool(false) +** Function 0 - Parameter 5 +bool(true) +bool(false) +bool(true) +string(6) "string" +** Function 0 - Parameter 6 +bool(true) +bool(false) +bool(true) +string(4) "bool" +** Function 0 - Parameter 7 +bool(true) +bool(false) +bool(true) +string(3) "int" +** Function 0 - Parameter 8 +bool(true) +bool(false) +bool(true) +string(5) "float" +** Function 0 - Parameter 9 +bool(true) +bool(false) +bool(false) +string(11) "NotExisting" +** Function 1 - Parameter 0 +bool(true) +bool(false) +bool(false) +string(4) "Test" + +*** methods +** Method 0 - parameter 0 +bool(true) +bool(false) +bool(false) +string(10) "SplSubject" +** Method 1 - parameter 0 +bool(true) +bool(false) +bool(false) +string(4) "self" +** Method 2 - parameter 0 +bool(true) +bool(false) +bool(false) +string(6) "parent" +** Method 3 - parameter 0 +bool(true) +bool(false) +bool(false) +string(4) "Test" + +*** return types +** Function/method return type 0 +bool(false) +** Function/method return type 1 +bool(true) +bool(false) +bool(false) +string(8) "stdClass" +** Function/method return type 2 +bool(true) +bool(false) +bool(true) +string(3) "int" +** Function/method return type 3 +bool(true) +bool(false) +bool(false) +string(4) "self" +** Function/method return type 4 +bool(true) +bool(false) +bool(false) +string(6) "parent" +** Function/method return type 5 +bool(true) +bool(false) +bool(false) +string(4) "Test" +** Function/method return type 6 +bool(true) +bool(false) +bool(false) +string(4) "Test" diff --git a/ext/reflection/tests/ReflectionType_002.phpt b/ext/reflection/tests/ReflectionType_002.phpt new file mode 100644 index 0000000000..8313862ec5 --- /dev/null +++ b/ext/reflection/tests/ReflectionType_002.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionType leak +--FILE-- +<?php + +$closure = function(Test $x): Test2 { return new Test2($x); }; +$rm = new ReflectionMethod($closure, '__invoke'); +$rp = $rm->getParameters()[0]; +$rt = $rp->getType(); +$rrt = $rm->getReturnType(); +unset($rm, $rp); +var_dump((string) $rt, (string) $rrt); + +--EXPECT-- +string(4) "Test" +string(5) "Test2" + diff --git a/ext/session/session.c b/ext/session/session.c index 70adf8e1ac..66b1a8628b 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -2743,7 +2743,8 @@ static zend_bool early_find_sid_in(zval *dest, int where, php_session_rfc1867_pr if ((ppid = zend_hash_str_find(Z_ARRVAL(PG(http_globals)[where]), PS(session_name), progress->sname_len)) && Z_TYPE_P(ppid) == IS_STRING) { zval_dtor(dest); - ZVAL_ZVAL(dest, ppid, 1, 0); + ZVAL_DEREF(ppid); + ZVAL_COPY(dest, ppid); return 1; } diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 7a9775b752..9beb946aea 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -517,7 +517,7 @@ static int sxe_prop_dim_write(zval *object, zval *member, zval *value, zend_bool case IS_DOUBLE: case IS_NULL: if (Z_TYPE_P(value) != IS_STRING) { - ZVAL_DUP(&zval_copy, value); + ZVAL_COPY(&zval_copy, value); value = &zval_copy; convert_to_string(value); new_value = 1; @@ -1066,7 +1066,7 @@ static int sxe_prop_is_empty(zval *object) /* {{{ */ if (node->type == XML_ATTRIBUTE_NODE) { return 0; } else if (sxe->iter.type != SXE_ITER_CHILD) { - if (!node->children || !node->parent || !node->next || node->children->next || node->children->children || node->parent->children == node->parent->last) { + if (sxe->iter.type == SXE_ITER_NONE || !node->children || !node->parent || node->children->next || node->children->children || node->parent->children == node->parent->last) { node = node->children; } else { ZVAL_COPY_VALUE(&iter_data, &sxe->iter.data); @@ -1186,7 +1186,7 @@ static HashTable *sxe_get_prop_hash(zval *object, int is_debug) /* {{{ */ node = NULL; } else if (sxe->iter.type != SXE_ITER_CHILD) { - if ( !node->children || !node->parent || !node->next || node->children->next || node->children->children || node->parent->children == node->parent->last ) { + if ( sxe->iter.type == SXE_ITER_NONE || !node->children || !node->parent || node->children->next || node->children->children || node->parent->children == node->parent->last ) { node = node->children; } else { ZVAL_COPY_VALUE(&iter_data, &sxe->iter.data); @@ -1930,12 +1930,8 @@ static int sxe_object_cast(zval *readobj, zval *writeobj, int type) Returns the string content */ SXE_METHOD(__toString) { - zval result; - - if (sxe_object_cast_ex(getThis(), &result, IS_STRING) == SUCCESS) { - RETURN_ZVAL(&result, 0, 0); - } else { - zval_ptr_dtor(&result); + if (sxe_object_cast_ex(getThis(), return_value, IS_STRING) != SUCCESS) { + zval_ptr_dtor(return_value); RETURN_EMPTY_STRING(); } } @@ -1979,9 +1975,9 @@ static int sxe_count_elements(zval *object, zend_long *count) /* {{{ */ if (!Z_ISUNDEF(intern->tmp)) { zval_ptr_dtor(&intern->tmp); } - ZVAL_ZVAL(&intern->tmp, &rv, 0, 0); - convert_to_long(&intern->tmp); - *count = (zend_long)Z_LVAL(intern->tmp); + ZVAL_LONG(&intern->tmp, zval_get_long(&rv)); + zval_ptr_dtor(&rv); + *count = Z_LVAL(intern->tmp); return SUCCESS; } return FAILURE; diff --git a/ext/simplexml/sxe.c b/ext/simplexml/sxe.c index c5241a804a..fc3f28d6d3 100644 --- a/ext/simplexml/sxe.c +++ b/ext/simplexml/sxe.c @@ -71,6 +71,7 @@ PHP_METHOD(ce_SimpleXMLIterator, valid) PHP_METHOD(ce_SimpleXMLIterator, current) { php_sxe_object *sxe = Z_SXEOBJ_P(getThis()); + zval *data; if (zend_parse_parameters_none() == FAILURE) { return; @@ -80,7 +81,9 @@ PHP_METHOD(ce_SimpleXMLIterator, current) return; /* return NULL */ } - RETURN_ZVAL(&sxe->iter.data, 1, 0); + data = &sxe->iter.data; + ZVAL_DEREF(data); + ZVAL_COPY(return_value, data); } /* }}} */ @@ -158,6 +161,7 @@ PHP_METHOD(ce_SimpleXMLIterator, hasChildren) PHP_METHOD(ce_SimpleXMLIterator, getChildren) { php_sxe_object *sxe = Z_SXEOBJ_P(getThis()); + zval *data; if (zend_parse_parameters_none() == FAILURE) { return; @@ -166,7 +170,10 @@ PHP_METHOD(ce_SimpleXMLIterator, getChildren) if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) { return; /* return NULL */ } - RETURN_ZVAL(&sxe->iter.data, 1, 0); + + data = &sxe->iter.data; + ZVAL_DEREF(data); + ZVAL_COPY(return_value, data); } /* {{{ arginfo */ diff --git a/ext/simplexml/tests/SimpleXMLElement_xpath.phpt b/ext/simplexml/tests/SimpleXMLElement_xpath.phpt index 8e84c51d67..fb7e6e90f0 100644 --- a/ext/simplexml/tests/SimpleXMLElement_xpath.phpt +++ b/ext/simplexml/tests/SimpleXMLElement_xpath.phpt @@ -1,17 +1,15 @@ --TEST-- Testing xpath() with invalid XML ---SKIPIF-- -<?php PHP_INT_SIZE == 4 or die("skip - 32-bit only"); --FILE-- <?php -$xml = simplexml_load_string("XXXXXXX^",$x,0x6000000000000001); -var_dump($xml->xpath("BBBB")); ---EXPECTF-- -Notice: Undefined variable: x in %s on line %d - -Warning: simplexml_load_string() expects parameter 3 to be integer, float given in %s on line %d +// gracefully recover from parsing of invalid XML; not available in PHP +const XML_PARSE_RECOVER = 1; -Fatal error: Uncaught Error: Call to a member function xpath() on null in %s:%d -Stack trace: -#0 {main} - thrown in %s on line %d +// we're not interested in checking concrete warnings regarding invalid XML +$xml = @simplexml_load_string("XXXXXXX^", 'SimpleXMLElement', XML_PARSE_RECOVER); + +// $xml is supposed to hold a SimpleXMLElement, but not FALSE/NULL +var_dump($xml->xpath("BBBB")); +?> +--EXPECT-- +bool(false) diff --git a/ext/simplexml/tests/SimpleXMLElement_xpath_3.phpt b/ext/simplexml/tests/SimpleXMLElement_xpath_3.phpt new file mode 100644 index 0000000000..e2997f19d1 --- /dev/null +++ b/ext/simplexml/tests/SimpleXMLElement_xpath_3.phpt @@ -0,0 +1,21 @@ +--TEST-- +Testing xpath() with invalid XML +--SKIPIF-- +<?php +if (PHP_MAJOR_VERSION < 7) die("skip this test is for PHP 7+ only"); +if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platforms only"); +?> +--FILE-- +<?php +$xml = simplexml_load_string("XXXXXXX^",$x,0x6000000000000001); +var_dump($xml->xpath("BBBB")); +?> +--EXPECTF-- +Notice: Undefined variable: x in %s on line %d + +Warning: simplexml_load_string() expects parameter 3 to be integer, float given in %s on line %d + +Fatal error: Uncaught EngineException: Call to a member function xpath() on null in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/simplexml/tests/SimpleXMLElement_xpath_4.phpt b/ext/simplexml/tests/SimpleXMLElement_xpath_4.phpt new file mode 100644 index 0000000000..d4d709753f --- /dev/null +++ b/ext/simplexml/tests/SimpleXMLElement_xpath_4.phpt @@ -0,0 +1,22 @@ +--TEST-- +Testing xpath() with invalid XML +--SKIPIF-- +<?php +if (PHP_MAJOR_VERSION < 7) die("skip this test is for PHP 7+ only"); +if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platforms only"); +?> +--FILE-- +<?php +$xml = simplexml_load_string("XXXXXXX^",$x,0x6000000000000001); +var_dump($xml->xpath("BBBB")); +?> +--EXPECTF-- +Notice: Undefined variable: x in %s on line %d + +Warning: simplexml_load_string(): Entity: line 1: parser error : Start tag expected, '<' not found in %s on line %d + +Warning: simplexml_load_string(): XXXXXXX^ in %s on line %d + +Warning: simplexml_load_string(): ^ in %s on line %d +bool(false) + diff --git a/ext/simplexml/tests/bug61335.phpt b/ext/simplexml/tests/bug61335.phpt new file mode 100644 index 0000000000..d2b9477ceb --- /dev/null +++ b/ext/simplexml/tests/bug61335.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #61335 - Access to array node returns wrong truth value +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php +$rec1 = simplexml_load_string("<foo><bar>aa</bar>\n</foo>"); +$rec2 = simplexml_load_string("<foo><bar>aa</bar></foo>"); + +if ($rec1->bar[0]) echo "NONEMPTY1\n"; +if ($rec1->bar[0] . "") echo "NONEMPTY2\n"; +if ($rec2->bar[0]) echo "NONEMPTY3\n"; +?> +--EXPECT-- +NONEMPTY1 +NONEMPTY2 +NONEMPTY3 diff --git a/ext/simplexml/tests/bug62639.phpt b/ext/simplexml/tests/bug62639.phpt new file mode 100644 index 0000000000..4a4e157b76 --- /dev/null +++ b/ext/simplexml/tests/bug62639.phpt @@ -0,0 +1,63 @@ +--TEST-- +Bug #62639 (XML structure broken) +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php + +class A extends SimpleXMLElement +{ +} + +$xml1 = <<<XML +<?xml version="1.0"?> +<a> + <b> + <c> + <value attr="Some Attr">Some Value</value> + </c> + </b> +</a> +XML; + +$a1 = new A($xml1); + +foreach ($a1->b->c->children() as $key => $value) { + var_dump($value); +} + +$xml2 = <<<XML +<?xml version="1.0"?> +<a> + <b> + <c><value attr="Some Attr">Some Value</value></c> + </b> +</a> +XML; + +$a2 = new A($xml2); + +foreach ($a2->b->c->children() as $key => $value) { + var_dump($value); +}?> +--EXPECT-- +object(A)#2 (2) { + ["@attributes"]=> + array(1) { + ["attr"]=> + string(9) "Some Attr" + } + [0]=> + string(10) "Some Value" +} +object(A)#3 (2) { + ["@attributes"]=> + array(1) { + ["attr"]=> + string(9) "Some Attr" + } + [0]=> + string(10) "Some Value" +}
\ No newline at end of file diff --git a/ext/simplexml/tests/bug67116.phpt b/ext/simplexml/tests/bug67116.phpt new file mode 100644 index 0000000000..774e43c99b --- /dev/null +++ b/ext/simplexml/tests/bug67116.phpt @@ -0,0 +1,93 @@ +--TEST-- +Bug #67116 (Inconsistent parsing of Nodes w/o linefeed) +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php + +$xml = <<<XML +<?xml version="1.0" encoding="UTF-8"?> +<aa> + <bs> + <b>b</b> + </bs> + <cs><c>b</c></cs> + <ds><d id="d"></d></ds> + <es> + <e id="e"></e> + </es> + <fs><f id="f"></f><f id="f"></f></fs> +</aa> +XML; +$sxe = simplexml_load_string($xml); +print_r($sxe); + +?> +--EXPECT-- +SimpleXMLElement Object +( + [bs] => SimpleXMLElement Object + ( + [b] => b + ) + + [cs] => SimpleXMLElement Object + ( + [c] => b + ) + + [ds] => SimpleXMLElement Object + ( + [d] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [id] => d + ) + + ) + + ) + + [es] => SimpleXMLElement Object + ( + [e] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [id] => e + ) + + ) + + ) + + [fs] => SimpleXMLElement Object + ( + [f] => Array + ( + [0] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [id] => f + ) + + ) + + [1] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [id] => f + ) + + ) + + ) + + ) + +) diff --git a/ext/simplexml/tests/bug67572.phpt b/ext/simplexml/tests/bug67572.phpt new file mode 100644 index 0000000000..4631f16142 --- /dev/null +++ b/ext/simplexml/tests/bug67572.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #67572 - SimpleXMLElement not parsing \n correctly +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php +$foo = 'bar'; +print "regular string ... "; +var_dump(empty($foo)); + +$xml = simplexml_load_string("<xml><something>somevalue</something></xml>"); +$xml2 = simplexml_load_string("<xml>\n<something>somevalue</something>\n</xml>"); + +foreach($xml as $key => $value) { + print "$key = $value ... "; + var_dump(empty($value)); + var_dump($value == false); +} + +foreach($xml2 as $key => $value) { + print "$key = $value ... "; + var_dump(empty($value)); + var_dump($value == false); +} +?> +--EXPECT-- +regular string ... bool(false) +something = somevalue ... bool(false) +bool(false) +something = somevalue ... bool(false) +bool(false) diff --git a/ext/simplexml/tests/bug69169.phpt b/ext/simplexml/tests/bug69169.phpt new file mode 100644 index 0000000000..08cf299290 --- /dev/null +++ b/ext/simplexml/tests/bug69169.phpt @@ -0,0 +1,70 @@ +--TEST-- +Bug #69169 (simplexml_load_string parse wrongly when xml given in one row) +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php +$a = '<?xml version="1.0" encoding="UTF-8"?> +<root a="b"> + <row b="y"> + <item s="t" /> + </row> + <row p="c"> + <item y="n" /> + </row> +</root>'; +$b = str_replace(array("\n", "\r", "\t"), "", $a); +$simple_xml = simplexml_load_string($b); +print_r($simple_xml); +?> +--EXPECT-- +SimpleXMLElement Object +( + [@attributes] => Array + ( + [a] => b + ) + + [row] => Array + ( + [0] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [b] => y + ) + + [item] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [s] => t + ) + + ) + + ) + + [1] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [p] => c + ) + + [item] => SimpleXMLElement Object + ( + [@attributes] => Array + ( + [y] => n + ) + + ) + + ) + + ) + +) diff --git a/ext/simplexml/tests/bug69491.phpt b/ext/simplexml/tests/bug69491.phpt new file mode 100644 index 0000000000..b48a40bf95 --- /dev/null +++ b/ext/simplexml/tests/bug69491.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #69491 (simplexml doesn't correctly parse empty nodes on same line as another node) +--SKIPIF-- +<?php +if (!extension_loaded("simplexml")) die("skip SimpleXML not available"); +?> +--FILE-- +<?php +var_dump(simplexml_load_string('<a> + <b><c/></b> +</a>'));?> +--EXPECT-- +object(SimpleXMLElement)#1 (1) { + ["b"]=> + object(SimpleXMLElement)#2 (1) { + ["c"]=> + object(SimpleXMLElement)#3 (0) { + } + } +} diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 96207f7975..7bff2aeab6 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -923,21 +923,16 @@ static xmlNodePtr to_xml_base64(encodeTypePtr type, zval *data, int style, xmlNo if (Z_TYPE_P(data) == IS_STRING) { str = php_base64_encode((unsigned char*)Z_STRVAL_P(data), Z_STRLEN_P(data)); - text = xmlNewTextLen(BAD_CAST(str->val), str->len); - xmlAddChild(ret, text); - zend_string_release(str); } else { - zval tmp; - - ZVAL_DUP(&tmp, data); - convert_to_string(&tmp); - str = php_base64_encode((unsigned char*)Z_STRVAL(tmp), Z_STRLEN(tmp)); - text = xmlNewTextLen(BAD_CAST(str->val), str->len); - xmlAddChild(ret, text); - zend_string_release(str); - zval_dtor(&tmp); + zend_string *tmp = zval_get_string(data); + str = php_base64_encode((unsigned char*) tmp->val, tmp->len); + zend_string_release(tmp); } + text = xmlNewTextLen(BAD_CAST(str->val), str->len); + xmlAddChild(ret, text); + zend_string_release(str); + if (style == SOAP_ENCODED) { set_ns_and_type(ret, type); } diff --git a/ext/sockets/tests/socket_import_stream-4-win.phpt b/ext/sockets/tests/socket_import_stream-4-win.phpt index 0effd04d34..1c9a7789c6 100644 --- a/ext/sockets/tests/socket_import_stream-4-win.phpt +++ b/ext/sockets/tests/socket_import_stream-4-win.phpt @@ -80,11 +80,11 @@ stream_set_blocking Warning: stream_set_blocking(): supplied resource is not a valid stream resource in %s on line %d socket_set_block -Warning: socket_set_block(): unable to set blocking mode [%d]: An operation was attempted on something that is not a socket. +Warning: socket_set_block(): unable to set blocking mode [10038]: %s in %ssocket_import_stream-4-win.php on line %d socket_get_option -Warning: socket_get_option(): unable to retrieve socket option [%d]: An operation was attempted on something that is not a socket. +Warning: socket_get_option(): unable to retrieve socket option [10038]: %s in %ssocket_import_stream-4-win.php on line %d diff --git a/ext/sockets/tests/socket_sentto_recvfrom_ipv6_udp-win32.phpt b/ext/sockets/tests/socket_sentto_recvfrom_ipv6_udp-win32.phpt index ec965094bc..8754028f84 100644 --- a/ext/sockets/tests/socket_sentto_recvfrom_ipv6_udp-win32.phpt +++ b/ext/sockets/tests/socket_sentto_recvfrom_ipv6_udp-win32.phpt @@ -48,7 +48,7 @@ require 'ipv6_skipif.inc'; socket_close($socket); --EXPECTF-- -Warning: socket_recvfrom(): unable to recvfrom [10022]: An invalid argument was supplied. +Warning: socket_recvfrom(): unable to recvfrom [10022]: %s in %s on line %d Warning: Wrong parameter count for socket_sendto() in %s on line %d diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index c56e2d5f22..4ade06a5d8 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -831,6 +831,15 @@ static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp) /* {{{ */ } /* }}} */ +static HashTable *spl_array_get_gc(zval *obj, zval **gc_data, int *gc_data_count) /* {{{ */ +{ + spl_array_object *intern = Z_SPLARRAY_P(obj); + *gc_data = &intern->array; + *gc_data_count = 1; + return zend_std_get_properties(obj); +} +/* }}} */ + static zval *spl_array_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */ { spl_array_object *intern = Z_SPLARRAY_P(object); @@ -1913,6 +1922,7 @@ PHP_MINIT_FUNCTION(spl_array) spl_handler_ArrayObject.get_properties = spl_array_get_properties; spl_handler_ArrayObject.get_debug_info = spl_array_get_debug_info; + spl_handler_ArrayObject.get_gc = spl_array_get_gc; spl_handler_ArrayObject.read_property = spl_array_read_property; spl_handler_ArrayObject.write_property = spl_array_write_property; spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr; diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 12c50c0b46..3fed81949a 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -1854,8 +1854,8 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type) zval *retval_ptr = &retval; ZVAL_STRINGL(retval_ptr, intern->file_name, intern->file_name_len); - zval_dtor(readobj); - ZVAL_ZVAL(writeobj, retval_ptr, 0, 0); + zval_ptr_dtor(readobj); + ZVAL_COPY_VALUE(writeobj, retval_ptr); } else { ZVAL_STRINGL(writeobj, intern->file_name, intern->file_name_len); } @@ -1866,8 +1866,8 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type) zval *retval_ptr = &retval; ZVAL_STRING(retval_ptr, intern->u.dir.entry.d_name); - zval_dtor(readobj); - ZVAL_ZVAL(writeobj, retval_ptr, 0, 0); + zval_ptr_dtor(readobj); + ZVAL_COPY_VALUE(writeobj, retval_ptr); } else { ZVAL_STRING(writeobj, intern->u.dir.entry.d_name); } @@ -1878,7 +1878,7 @@ static int spl_filesystem_object_cast(zval *readobj, zval *writeobj, int type) return SUCCESS; } if (readobj == writeobj) { - zval_dtor(readobj); + zval_ptr_dtor(readobj); } ZVAL_NULL(writeobj); return FAILURE; @@ -2031,7 +2031,7 @@ static int spl_filesystem_file_read(spl_filesystem_object *intern, int silent) / if (intern->u.file.max_line_len > 0) { buf = safe_emalloc((intern->u.file.max_line_len + 1), sizeof(char), 0); - if (php_stream_get_line(intern->u.file.stream, buf, intern->u.file.max_line_len, &line_len) == NULL) { + if (php_stream_get_line(intern->u.file.stream, buf, intern->u.file.max_line_len + 1, &line_len) == NULL) { efree(buf); buf = NULL; } else { @@ -2140,7 +2140,7 @@ static int spl_filesystem_file_read_csv(spl_filesystem_object *intern, char deli php_fgetcsv(intern->u.file.stream, delimiter, enclosure, escape, buf_len, buf, &intern->u.file.current_zval); if (return_value) { if (Z_TYPE_P(return_value) != IS_NULL) { - zval_dtor(return_value); + zval_ptr_dtor(return_value); ZVAL_NULL(return_value); } ZVAL_ZVAL(return_value, &intern->u.file.current_zval, 1, 0); diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index a92707bd93..c4857663e7 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -249,9 +249,10 @@ static void spl_ptr_heap_insert(spl_ptr_heap *heap, zval *elem, void *cmp_userda } /* sifting up */ - for (i = heap->count++; i > 0 && heap->cmp(&heap->elements[(i-1)/2], elem, cmp_userdata) < 0; i = (i-1)/2) { + for (i = heap->count; i > 0 && heap->cmp(&heap->elements[(i-1)/2], elem, cmp_userdata) < 0; i = (i-1)/2) { heap->elements[i] = heap->elements[(i-1)/2]; } + heap->count++; if (EG(exception)) { /* exception thrown during comparison */ diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 37f97866c7..aa0854ab78 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -494,7 +494,7 @@ static void spl_recursive_it_it_construct(INTERNAL_FUNCTION_PARAMETERS, zend_cla } if (user_caching_it_flags) { - ZVAL_ZVAL(&caching_it_flags, user_caching_it_flags, 1, 0); + ZVAL_COPY(&caching_it_flags, user_caching_it_flags); } else { ZVAL_LONG(&caching_it_flags, CIT_CATCH_GET_CHILD); } @@ -1067,10 +1067,10 @@ static void spl_recursive_tree_iterator_get_entry(spl_recursive_it_object *objec if (data) { RETVAL_ZVAL(data, 1, 0); if (Z_TYPE_P(return_value) == IS_ARRAY) { - zval_dtor(return_value); + zval_ptr_dtor(return_value); ZVAL_STRINGL(return_value, "Array", sizeof("Array")-1); } else { - convert_to_string(return_value); + convert_to_string_ex(return_value); } } zend_restore_error_handling(&error_handling); @@ -1268,9 +1268,7 @@ SPL_METHOD(RecursiveTreeIterator, key) } if (object->flags & RTIT_BYPASS_KEY) { - zval *key_ptr = &key; - RETVAL_ZVAL(key_ptr, 1, 0); - zval_dtor(&key); + RETVAL_ZVAL(&key, 1, 1); return; } @@ -2074,8 +2072,7 @@ SPL_METHOD(RegexIterator, accept) case REGIT_MODE_REPLACE: replacement = zend_read_property(intern->std.ce, getThis(), "replacement", sizeof("replacement")-1, 1, &rv); if (Z_TYPE_P(replacement) != IS_STRING) { - tmp_replacement = *replacement; - zval_copy_ctor(&tmp_replacement); + ZVAL_COPY(&tmp_replacement, replacement); convert_to_string(&tmp_replacement); replacement = &tmp_replacement; } @@ -2090,7 +2087,7 @@ SPL_METHOD(RegexIterator, accept) } if (replacement == &tmp_replacement) { - zval_dtor(replacement); + zval_ptr_dtor(replacement); } RETVAL_BOOL(count > 0); } @@ -2787,11 +2784,11 @@ SPL_METHOD(CachingIterator, __toString) return; } if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) { - ZVAL_DUP(return_value, &intern->current.key); + ZVAL_COPY(return_value, &intern->current.key); convert_to_string(return_value); return; } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) { - ZVAL_DUP(return_value, &intern->current.data); + ZVAL_COPY(return_value, &intern->current.data); convert_to_string(return_value); return; } @@ -3529,7 +3526,7 @@ static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser) return ZEND_HASH_APPLY_STOP; } array_set_zval_key(Z_ARRVAL_P(return_value), &key, data); - zval_dtor(&key); + zval_ptr_dtor(&key); } else { Z_TRY_ADDREF_P(data); add_next_index_zval(return_value, data); @@ -3571,7 +3568,7 @@ PHP_FUNCTION(iterator_to_array) array_init(return_value); if (spl_iterator_apply(obj, use_keys ? spl_iterator_to_array_apply : spl_iterator_to_values_apply, (void*)return_value) != SUCCESS) { - zval_dtor(return_value); + zval_ptr_dtor(return_value); RETURN_NULL(); } } /* }}} */ diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index ddff20ea4f..e2a55735a3 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -657,7 +657,7 @@ SPL_METHOD(SplObjectStorage, current) if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) { return; } - RETVAL_ZVAL(&element->obj, 1, 0); + ZVAL_COPY(return_value, &element->obj); } /* }}} */ /* {{{ proto mixed SplObjectStorage::getInfo() @@ -674,7 +674,7 @@ SPL_METHOD(SplObjectStorage, getInfo) if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) { return; } - RETVAL_ZVAL(&element->inf, 1, 0); + ZVAL_COPY(return_value, &element->inf); } /* }}} */ /* {{{ proto mixed SplObjectStorage::setInfo(mixed $inf) @@ -693,7 +693,7 @@ SPL_METHOD(SplObjectStorage, setInfo) return; } zval_ptr_dtor(&element->inf); - ZVAL_ZVAL(&element->inf, inf, 1, 0); + ZVAL_COPY(&element->inf, inf); } /* }}} */ /* {{{ proto void SplObjectStorage::next() diff --git a/ext/spl/tests/bug67805.phpt b/ext/spl/tests/bug67805.phpt new file mode 100644 index 0000000000..ba07b3da92 --- /dev/null +++ b/ext/spl/tests/bug67805.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #67805 SplFileObject setMaxLineLength +--CREDITS-- +Willian Gustavo Veiga <contact@willianveiga.com> +--FILE-- +<?php +$splFileObject = new SplFileObject(__FILE__); +$splFileObject->setMaxLineLen(3); +$line = $splFileObject->getCurrentLine(); +var_dump($line === '<?p'); +var_dump(strlen($line) === 3); +?> +--EXPECTF-- +bool(true) +bool(true) diff --git a/ext/spl/tests/bug69737.phpt b/ext/spl/tests/bug69737.phpt new file mode 100644 index 0000000000..02815c296a --- /dev/null +++ b/ext/spl/tests/bug69737.phpt @@ -0,0 +1,21 @@ +--TEST-- +Bug #69737 (Segfault when SplMinHeap::compare produces fatal error) +--FILE-- +<?php +class SplMinHeap1 extends SplMinHeap { + public function compare($a, $b) { + return -parent::notexist($a, $b); + } +} +$h = new SplMinHeap1(); +$h->insert(1); +$h->insert(6); +?> +===DONE=== +--EXPECTF-- +Fatal error: Uncaught Error: Call to undefined method SplMinHeap::notexist() in %s%ebug69737.php:%d +Stack trace: +#0 [internal function]: SplMinHeap1->compare(1, 6) +#1 %s%ebug69737.php(%d): SplHeap->insert(6) +#2 {main} + thrown in %s%ebug69737.php on line %d diff --git a/ext/spl/tests/fileobject_setmaxlinelen_basic.phpt b/ext/spl/tests/fileobject_setmaxlinelen_basic.phpt index c2307663cb..1d81a6f651 100644 --- a/ext/spl/tests/fileobject_setmaxlinelen_basic.phpt +++ b/ext/spl/tests/fileobject_setmaxlinelen_basic.phpt @@ -12,4 +12,4 @@ $s->setMaxLineLen( 3); echo $s->getCurrentLine(); ?> --EXPECT-- -<? +<?p diff --git a/ext/sqlite3/libsqlite/sqlite3.c b/ext/sqlite3/libsqlite/sqlite3.c index c9ed48f4bf..dff4538dd3 100644 --- a/ext/sqlite3/libsqlite/sqlite3.c +++ b/ext/sqlite3/libsqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.8.3. By combining all the individual C code files into this +** version 3.8.10.2. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -22,9 +22,6 @@ #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif -#ifndef SQLITE_API -# define SQLITE_API -#endif /************** Begin file sqliteInt.h ***************************************/ /* ** 2001 September 15 @@ -73,6 +70,7 @@ #pragma warning(disable : 4055) #pragma warning(disable : 4100) #pragma warning(disable : 4127) +#pragma warning(disable : 4130) #pragma warning(disable : 4152) #pragma warning(disable : 4189) #pragma warning(disable : 4206) @@ -91,6 +89,44 @@ /************** Continuing where we left off in sqliteInt.h ******************/ /* +** Special setup for VxWorks +*/ +/************** Include vxworks.h in the middle of sqliteInt.h ***************/ +/************** Begin file vxworks.h *****************************************/ +/* +** 2015-03-02 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to Wind River's VxWorks +*/ +#if defined(__RTP__) || defined(_WRS_KERNEL) +/* This is VxWorks. Set up things specially for that OS +*/ +#include <vxWorks.h> +#include <pthread.h> /* amalgamator: dontcache */ +#define OS_VXWORKS 1 +#define SQLITE_OS_OTHER 0 +#define SQLITE_HOMEGROWN_RECURSIVE_MUTEX 1 +#define SQLITE_OMIT_LOAD_EXTENSION 1 +#define SQLITE_ENABLE_LOCKING_STYLE 0 +#define HAVE_UTIME 1 +#else +/* This is not VxWorks. */ +#define OS_VXWORKS 0 +#endif /* defined(_WRS_KERNEL) */ + +/************** End of vxworks.h *********************************************/ +/************** Continuing where we left off in sqliteInt.h ******************/ + +/* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks ** large file support, or if the OS is windows, these should be no-ops. @@ -214,16 +250,20 @@ extern "C" { /* -** Add the ability to override 'extern' +** Provide the ability to override linkage features of the interface. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif - #ifndef SQLITE_API # define SQLITE_API #endif - +#ifndef SQLITE_CDECL +# define SQLITE_CDECL +#endif +#ifndef SQLITE_STDCALL +# define SQLITE_STDCALL +#endif /* ** These no-op macros are used in front of interfaces to mark those @@ -278,9 +318,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.3" -#define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-02-25 13:29:11 9d6c1880fb75660bbabd693175579529785f8a6b" +#define SQLITE_VERSION "3.8.10.2" +#define SQLITE_VERSION_NUMBER 3008010 +#define SQLITE_SOURCE_ID "2015-05-20 18:17:19 2ef4f3a5b1d1d0c4338f8243d40a2452cc1f7fe4" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -313,9 +353,9 @@ extern "C" { ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void); +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics @@ -340,8 +380,8 @@ SQLITE_API int sqlite3_libversion_number(void); ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N); #endif /* @@ -380,7 +420,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N); ** ** See the [threading mode] documentation for additional information. */ -SQLITE_API int sqlite3_threadsafe(void); +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle @@ -437,6 +477,7 @@ typedef sqlite_uint64 sqlite3_uint64; /* ** CAPI3REF: Closing A Database Connection +** DESTRUCTOR: sqlite3 ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. @@ -476,8 +517,8 @@ typedef sqlite_uint64 sqlite3_uint64; ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer ** argument is a harmless no-op. */ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. @@ -488,6 +529,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* ** CAPI3REF: One-Step Query Execution Interface +** METHOD: sqlite3 ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], @@ -547,7 +589,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** </ul> */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ @@ -927,14 +969,16 @@ struct sqlite3_io_methods { ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** +** <ul> +** <li>[[SQLITE_FCNTL_LOCKSTATE]] ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability -** is used during testing and only needs to be supported when SQLITE_TEST -** is defined. -** <ul> +** is used during testing and is only available when the SQLITE_TEST +** compile-time option is used. +** ** <li>[[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the @@ -1059,7 +1103,9 @@ struct sqlite3_io_methods { ** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] ** file control returns [SQLITE_OK], then the parser assumes that the ** VFS has handled the PRAGMA itself and the parser generates a no-op -** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns +** prepared statement if result string is NULL, or that returns a copy +** of the result string if the string is non-NULL. +** ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] @@ -1117,12 +1163,19 @@ struct sqlite3_io_methods { ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** +** <li>[[SQLITE_FCNTL_WAL_BLOCK]] +** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might +** be advantageous to block on the next WAL lock if the lock is not immediately +** available. The WAL subsystem issues this signal during rare +** circumstances in order to fix a problem with priority inversion. +** Applications should <em>not</em> use this file-control. +** ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 +#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 +#define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 @@ -1141,6 +1194,13 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_SYNC 21 #define SQLITE_FCNTL_COMMIT_PHASETWO 22 #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 +#define SQLITE_FCNTL_WAL_BLOCK 24 + +/* deprecated names */ +#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE +#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO + /* ** CAPI3REF: Mutex Handle @@ -1489,10 +1549,10 @@ struct sqlite3_vfs { ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); +SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void); +SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library @@ -1523,10 +1583,11 @@ SQLITE_API int sqlite3_os_end(void); ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ -SQLITE_API int sqlite3_config(int, ...); +SQLITE_API int SQLITE_CDECL sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections +** METHOD: sqlite3 ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to @@ -1541,7 +1602,7 @@ SQLITE_API int sqlite3_config(int, ...); ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines @@ -1701,7 +1762,7 @@ struct sqlite3_mem_methods { ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] -** <li> [sqlite3_status()] +** <li> [sqlite3_status64()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory @@ -1912,7 +1973,6 @@ struct sqlite3_mem_methods { ** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro ** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value ** that specifies the maximum size of the created heap. -** </dl> ** ** [[SQLITE_CONFIG_PCACHE_HDRSZ]] ** <dt>SQLITE_CONFIG_PCACHE_HDRSZ @@ -2025,15 +2085,17 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Enable Or Disable Extended Result Codes +** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid +** METHOD: sqlite3 ** ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) ** has a unique 64-bit signed @@ -2081,10 +2143,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE @@ -2133,10 +2196,11 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed @@ -2156,10 +2220,11 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_total_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query +** METHOD: sqlite3 ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically @@ -2195,7 +2260,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ -SQLITE_API void sqlite3_interrupt(sqlite3*); +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -2230,12 +2295,13 @@ SQLITE_API void sqlite3_interrupt(sqlite3*); ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** KEYWORDS: {busy-handler callback} {busy handler} +** METHOD: sqlite3 ** ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X ** that might be invoked with argument P whenever @@ -2291,10 +2357,11 @@ SQLITE_API int sqlite3_complete16(const void *sql); ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout +** METHOD: sqlite3 ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler @@ -2313,10 +2380,11 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); ** ** See also: [PRAGMA busy_timeout] */ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries +** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. @@ -2387,7 +2455,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ @@ -2395,13 +2463,17 @@ SQLITE_API int sqlite3_get_table( int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); -SQLITE_API void sqlite3_free_table(char **result); +SQLITE_API void SQLITE_STDCALL sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. +** These routines understand most of the common K&R formatting options, +** plus some additional non-standard formats, detailed below. +** Note that some of the more obscure formatting options from recent +** C-library standards are omitted from this implementation. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. @@ -2434,7 +2506,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. +** is are "%q", "%Q", "%w" and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. @@ -2487,14 +2559,20 @@ SQLITE_API void sqlite3_free_table(char **result); ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** +** ^(The "%w" formatting option is like "%q" except that it expects to +** be contained within double-quotes instead of single quotes, and it +** escapes the double-quote character instead of the single-quote +** character.)^ The "%w" formatting option is intended for safely inserting +** table and column names into a constructed SQL statement. +** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char*,...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2584,12 +2662,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** a block of memory after it has been released using ** [sqlite3_free()] or [sqlite3_realloc()]. */ -SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); -SQLITE_API void *sqlite3_realloc(void*, int); -SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64); -SQLITE_API void sqlite3_free(void*); -SQLITE_API sqlite3_uint64 sqlite3_msize(void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void*, int); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void*, sqlite3_uint64); +SQLITE_API void SQLITE_STDCALL sqlite3_free(void*); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void*); /* ** CAPI3REF: Memory Allocator Statistics @@ -2614,8 +2692,8 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void*); ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator @@ -2638,10 +2716,11 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ -SQLITE_API void sqlite3_randomness(int N, void *P); +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks +** METHOD: sqlite3 ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. @@ -2720,7 +2799,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData @@ -2798,6 +2877,7 @@ SQLITE_API int sqlite3_set_authorizer( /* ** CAPI3REF: Tracing And Profiling Functions +** METHOD: sqlite3 ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. @@ -2824,12 +2904,13 @@ SQLITE_API int sqlite3_set_authorizer( ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to @@ -2859,10 +2940,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, ** database connections for the meaning of "modify" in this paragraph. ** */ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection +** CONSTRUCTOR: sqlite3 ** ** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for @@ -3087,15 +3169,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** See also: [sqlite3_temp_directory] */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -3141,19 +3223,22 @@ SQLITE_API int sqlite3_open_v2( ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages -** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** METHOD: sqlite3 +** +** ^If the most recent sqlite3_* API call associated with +** [database connection] D failed, then the sqlite3_errcode(D) interface +** returns the numeric [result code] or [extended result code] for that +** API call. +** If the most recent API call was successful, +** then the return value from sqlite3_errcode() is undefined. +** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. @@ -3184,40 +3269,41 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int); /* -** CAPI3REF: SQL Statement Object +** CAPI3REF: Prepared Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". +** An instance of this object represents a single SQL statement that +** has been compiled into binary form and is ready to be evaluated. ** -** The life of a statement object goes something like this: +** Think of each SQL statement as a separate computer program. The +** original SQL text is source code. A prepared statement object +** is the compiled object code. All SQL must be converted into a +** prepared statement before it can be run. +** +** The life-cycle of a prepared statement object usually goes like this: ** ** <ol> -** <li> Create the object using [sqlite3_prepare_v2()] or a related -** function. -** <li> Bind values to [host parameters] using the sqlite3_bind_*() +** <li> Create the prepared statement object using [sqlite3_prepare_v2()]. +** <li> Bind values to [parameters] using the sqlite3_bind_*() ** interfaces. ** <li> Run the SQL by calling [sqlite3_step()] one or more times. -** <li> Reset the statement using [sqlite3_reset()] then go back +** <li> Reset the prepared statement using [sqlite3_reset()] then go back ** to step 2. Do this zero or more times. ** <li> Destroy the object using [sqlite3_finalize()]. ** </ol> -** -** Refer to documentation on individual methods above for additional -** information. */ typedef struct sqlite3_stmt sqlite3_stmt; /* ** CAPI3REF: Run-time Limits +** METHOD: sqlite3 ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the @@ -3255,7 +3341,7 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** ** New run-time limit categories may be added in future releases. */ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories @@ -3329,6 +3415,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_stmt ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. @@ -3342,16 +3430,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string <i>including</i> -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. +** ^If the nByte argument is negative, then zSql is read up to the +** first zero terminator. ^If nByte is positive, then it is the +** number of bytes read from zSql. ^If nByte is zero, then no prepared +** statement is generated. +** If the caller knows that the supplied string is nul-terminated, then +** there is a small performance advantage to passing an nByte parameter that +** is the number of bytes in the input string <i>including</i> +** the nul-terminator. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -3407,28 +3493,28 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** </li> ** </ol> */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ @@ -3438,15 +3524,17 @@ SQLITE_API int sqlite3_prepare16_v2( /* ** CAPI3REF: Retrieving Statement SQL +** METHOD: sqlite3_stmt ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to @@ -3474,10 +3562,11 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using @@ -3493,7 +3582,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** for example, in diagnostic routines to search for prepared ** statements that are holding a transaction open. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*); /* ** CAPI3REF: Dynamically Typed Value Object @@ -3552,6 +3641,7 @@ typedef struct sqlite3_context sqlite3_context; ** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** METHOD: sqlite3_stmt ** ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, ** literals may be replaced by a [parameter] that matches one of following @@ -3654,22 +3744,23 @@ typedef struct sqlite3_context sqlite3_context; ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, void(*)(void*)); -SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); -SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void(*)(void*), unsigned char encoding); -SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters +** METHOD: sqlite3_stmt ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the @@ -3686,10 +3777,11 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter +** METHOD: sqlite3_stmt ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. @@ -3713,10 +3805,11 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name +** METHOD: sqlite3_stmt ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second @@ -3729,19 +3822,21 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set +** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL @@ -3749,10 +3844,11 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); ** ** See also: [sqlite3_data_count()] */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set +** METHOD: sqlite3_stmt ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() @@ -3777,11 +3873,12 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result +** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in @@ -3825,15 +3922,16 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result +** METHOD: sqlite3_stmt ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the @@ -3861,11 +3959,12 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); ** is associated with individual values, not with the containers ** used to hold those values. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement +** METHOD: sqlite3_stmt ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -3941,10 +4040,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ -SQLITE_API int sqlite3_step(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set +** METHOD: sqlite3_stmt ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. @@ -3961,7 +4061,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); ** ** See also: [sqlite3_column_count()] */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes @@ -3998,6 +4098,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} +** METHOD: sqlite3_stmt ** ** These routines form the "result set" interface. ** @@ -4157,19 +4258,20 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object +** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors @@ -4193,10 +4295,11 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object +** METHOD: sqlite3_stmt ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. @@ -4219,13 +4322,14 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} +** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior @@ -4318,7 +4422,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4328,7 +4432,7 @@ SQLITE_API int sqlite3_create_function( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -4338,7 +4442,7 @@ SQLITE_API int sqlite3_create_function16( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4380,21 +4484,22 @@ SQLITE_API int sqlite3_create_function_v2( ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. +** the use of these functions. To encourage programmers to avoid +** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values +** METHOD: sqlite3_value ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -4438,21 +4543,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context +** METHOD: sqlite3_context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. @@ -4493,10 +4599,11 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) @@ -4507,10 +4614,11 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) @@ -4518,10 +4626,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*); ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data +** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -4570,8 +4679,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -4594,6 +4703,7 @@ typedef void (*sqlite3_destructor_type)(void*); /* ** CAPI3REF: Setting The Result Of An SQL Function +** METHOD: sqlite3_context ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -4706,29 +4816,30 @@ typedef void (*sqlite3_destructor_type)(void*); ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ -SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*, +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); -SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); -SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -SQLITE_API void sqlite3_result_null(sqlite3_context*); -SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences +** METHOD: sqlite3 ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. @@ -4806,14 +4917,14 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -4821,7 +4932,7 @@ SQLITE_API int sqlite3_create_collation_v2( int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, @@ -4831,6 +4942,7 @@ SQLITE_API int sqlite3_create_collation16( /* ** CAPI3REF: Collation Needed Callbacks +** METHOD: sqlite3 ** ** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the @@ -4855,12 +4967,12 @@ SQLITE_API int sqlite3_create_collation16( ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) @@ -4874,11 +4986,11 @@ SQLITE_API int sqlite3_collation_needed16( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_key( +SQLITE_API int SQLITE_STDCALL sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); -SQLITE_API int sqlite3_key_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_key_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The key */ @@ -4892,11 +5004,11 @@ SQLITE_API int sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_rekey( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); -SQLITE_API int sqlite3_rekey_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The new key */ @@ -4906,7 +5018,7 @@ SQLITE_API int sqlite3_rekey_v2( ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ -SQLITE_API void sqlite3_activate_see( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4916,7 +5028,7 @@ SQLITE_API void sqlite3_activate_see( ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ -SQLITE_API void sqlite3_activate_cerod( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4938,7 +5050,7 @@ SQLITE_API void sqlite3_activate_cerod( ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ -SQLITE_API int sqlite3_sleep(int); +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files @@ -5038,6 +5150,7 @@ SQLITE_API char *sqlite3_data_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} +** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, @@ -5056,10 +5169,11 @@ SQLITE_API char *sqlite3_data_directory; ** connection while this routine is running, then the return value ** is undefined. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] @@ -5068,10 +5182,11 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file @@ -5084,19 +5199,21 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only +** METHOD: sqlite3 ** ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N ** of connection D is read-only, 0 if it is read/write, or -1 if N is not ** the name of a database on connection D. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Find the next prepared statement +** METHOD: sqlite3 ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -5108,10 +5225,11 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. @@ -5156,11 +5274,12 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** ** See also the [sqlite3_update_hook()] interface. */ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument @@ -5207,7 +5326,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* @@ -5237,12 +5356,17 @@ SQLITE_API void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 +** and will always return SQLITE_MISUSE. On those systems, +** shared cache mode should be enabled per-database connection via +** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. +** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ -SQLITE_API int sqlite3_enable_shared_cache(int); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory @@ -5258,10 +5382,11 @@ SQLITE_API int sqlite3_enable_shared_cache(int); ** ** See also: [sqlite3_db_release_memory()] */ -SQLITE_API int sqlite3_release_memory(int); +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int); /* ** CAPI3REF: Free Memory Used By A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the @@ -5271,7 +5396,7 @@ SQLITE_API int sqlite3_release_memory(int); ** ** See also: [sqlite3_release_memory()] */ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size @@ -5323,7 +5448,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface @@ -5334,11 +5459,12 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table +** METHOD: sqlite3 ** ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns ** information about column C of table T in database D @@ -5403,7 +5529,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** parsed, if that has not already been done, and returns an error if ** any errors are encountered while loading the schema. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -5417,6 +5543,7 @@ SQLITE_API int sqlite3_table_column_metadata( /* ** CAPI3REF: Load An Extension +** METHOD: sqlite3 ** ** ^This interface loads an SQLite extension library from the named file. ** @@ -5449,7 +5576,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ** See also the [load_extension() SQL function]. */ -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ @@ -5458,6 +5585,7 @@ SQLITE_API int sqlite3_load_extension( /* ** CAPI3REF: Enable Or Disable Extension Loading +** METHOD: sqlite3 ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with [extension loading], and as a means of disabling @@ -5469,7 +5597,7 @@ SQLITE_API int sqlite3_load_extension( ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions @@ -5507,7 +5635,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Cancel Automatic Extension Loading @@ -5519,7 +5647,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); ** unregistered and it returns 0 if X was not on the list of initialization ** routines. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading @@ -5527,7 +5655,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ -SQLITE_API void sqlite3_reset_auto_extension(void); +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered @@ -5707,6 +5835,7 @@ struct sqlite3_index_info { /* ** CAPI3REF: Register A Virtual Table Implementation +** METHOD: sqlite3 ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before @@ -5730,13 +5859,13 @@ struct sqlite3_index_info { ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -5764,7 +5893,7 @@ SQLITE_API int sqlite3_create_module_v2( */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ + int nRef; /* Number of open cursors */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; @@ -5799,10 +5928,11 @@ struct sqlite3_vtab_cursor { ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table +** METHOD: sqlite3 ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. @@ -5817,7 +5947,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -5845,6 +5975,8 @@ typedef struct sqlite3_blob sqlite3_blob; /* ** CAPI3REF: Open A BLOB For Incremental I/O +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_blob ** ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; @@ -5914,7 +6046,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, @@ -5926,6 +6058,7 @@ SQLITE_API int sqlite3_blob_open( /* ** CAPI3REF: Move a BLOB Handle to a New Row +** METHOD: sqlite3_blob ** ** ^This function is used to move an existing blob handle so that it points ** to a different row of the same database table. ^The new row is identified @@ -5946,10 +6079,11 @@ SQLITE_API int sqlite3_blob_open( ** ** ^This function sets the database handle error code and message. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle +** DESTRUCTOR: sqlite3_blob ** ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed ** unconditionally. Even if this routine returns an error code, the @@ -5968,10 +6102,11 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_i ** is passed a valid open blob handle, the values returned by the ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB +** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The @@ -5983,10 +6118,11 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z @@ -6011,10 +6147,11 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); ** ** See also: [sqlite3_blob_write()]. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. N bytes of data are copied from the buffer Z @@ -6052,7 +6189,7 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); ** ** See also: [sqlite3_blob_read()]. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects @@ -6083,9 +6220,9 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOff ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes @@ -6198,11 +6335,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object @@ -6312,8 +6449,8 @@ struct sqlite3_mutex_methods { ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* @@ -6342,6 +6479,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); /* ** CAPI3REF: Retrieve the mutex for a database connection +** METHOD: sqlite3 ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument @@ -6349,10 +6487,11 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files +** METHOD: sqlite3 ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated @@ -6383,7 +6522,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface @@ -6402,7 +6541,7 @@ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void* ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ -SQLITE_API int sqlite3_test_control(int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes @@ -6436,12 +6575,13 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_LAST 24 +#define SQLITE_TESTCTRL_IMPOSTER 25 +#define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** -** ^This interface is used to retrieve runtime status information +** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes @@ -6455,19 +6595,22 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. +** ^The sqlite3_status() and sqlite3_status64() routines return +** SQLITE_OK on success and a non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. +** If either the current value or the highwater mark is too large to +** be represented by a 32-bit integer, then the values returned by +** sqlite3_status() are undefined. ** ** See also: [sqlite3_db_status()] */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +); /* @@ -6565,6 +6708,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Database Connection Status +** METHOD: sqlite3 ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the @@ -6585,7 +6729,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections @@ -6693,6 +6837,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Prepared Statement Status +** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS counters] that measure the number @@ -6714,7 +6859,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements @@ -7137,20 +7282,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] ** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b> ** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. +** ^The sqlite3_backup_remaining() routine returns the number of pages still +** to be backed up at the conclusion of the most recent sqlite3_backup_step(). +** ^The sqlite3_backup_pagecount() routine returns the total number of pages +** in the source database at the conclusion of the most recent +** sqlite3_backup_step(). +** ^(The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified in a way that +** changes the size of the source database or the number of pages remaining, +** those changes are not reflected in the output of sqlite3_backup_pagecount() +** and sqlite3_backup_remaining() until after the next +** sqlite3_backup_step().)^ ** ** <b>Concurrent Usage of Database Handles</b> ** @@ -7183,19 +7328,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification +** METHOD: sqlite3 ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or @@ -7308,7 +7454,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ @@ -7323,8 +7469,8 @@ SQLITE_API int sqlite3_unlock_notify( ** strings in a case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *, const char *); +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing @@ -7339,7 +7485,7 @@ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. */ -SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr); /* ** CAPI3REF: Error Logging Interface @@ -7362,10 +7508,11 @@ SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); ** a few hundred characters, it will be truncated to the length of the ** buffer. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** is invoked each time data is committed to a database in wal mode. @@ -7397,7 +7544,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* @@ -7405,6 +7552,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** CAPI3REF: Configure an auto-checkpoint +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D @@ -7431,10 +7579,11 @@ SQLITE_API void *sqlite3_wal_hook( ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ @@ -7452,10 +7601,11 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** start a callback but which do not need the full power (and corresponding ** complication) of [sqlite3_wal_checkpoint_v2()]. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint ** operation on database X of [database connection] D in mode M. Status @@ -7545,7 +7695,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface ** from SQL. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -7581,7 +7731,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options ** may be added in the future. */ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options @@ -7634,7 +7784,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Conflict resolution modes @@ -7710,6 +7860,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Prepared Statement Scan Status +** METHOD: sqlite3_stmt ** ** This interface returns information about the predicted and measured ** performance for pStmt. Advanced applications can use this @@ -7738,7 +7889,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ @@ -7747,13 +7898,14 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( /* ** CAPI3REF: Zero Scan-Status Counters +** METHOD: sqlite3_stmt ** ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. ** ** This API is only available if the library is built with pre-processor ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. */ -SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); +SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* @@ -7808,7 +7960,7 @@ typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), @@ -7834,7 +7986,7 @@ struct sqlite3_rtree_geometry { ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...) */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), @@ -7998,15 +8150,17 @@ struct sqlite3_rtree_query_info { #endif /* -** The maximum number of in-memory pages to use for the main database -** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE +** The suggested maximum number of in-memory pages to use for +** the main database table and for temporary tables. +** +** IMPLEMENTATION-OF: R-31093-59126 The default suggested cache size +** is 2000 pages. +** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be +** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options. */ #ifndef SQLITE_DEFAULT_CACHE_SIZE # define SQLITE_DEFAULT_CACHE_SIZE 2000 #endif -#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE -# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 -#endif /* ** The default number of frames to accumulate in the log file before @@ -8356,6 +8510,32 @@ SQLITE_PRIVATE void sqlite3Coverage(int); #endif /* +** Declarations used for tracing the operating system interfaces. +*/ +#if defined(SQLITE_FORCE_OS_TRACE) || defined(SQLITE_TEST) || \ + (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) + extern int sqlite3OSTrace; +# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X +# define SQLITE_HAVE_OS_TRACE +#else +# define OSTRACE(X) +# undef SQLITE_HAVE_OS_TRACE +#endif + +/* +** Is the sqlite3ErrName() function needed in the build? Currently, +** it is needed by "mutex_w32.c" (when debugging), "os_win.c" (when +** OSTRACE is enabled), and by several "test*.c" files (which are +** compiled using SQLITE_TEST). +*/ +#if defined(SQLITE_HAVE_OS_TRACE) || defined(SQLITE_TEST) || \ + (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) +# define SQLITE_NEED_ERR_NAME +#else +# undef SQLITE_NEED_ERR_NAME +#endif + +/* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() ** macros to verify that we have tested SQLite for large-file support. @@ -8851,6 +9031,20 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ typedef INT16_TYPE LogEst; /* +** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer +*/ +#ifndef SQLITE_PTRSIZE +# if defined(__SIZEOF_POINTER__) +# define SQLITE_PTRSIZE __SIZEOF_POINTER__ +# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(_M_ARM) || defined(__arm__) || defined(__x86) +# define SQLITE_PTRSIZE 4 +# else +# define SQLITE_PTRSIZE 8 +# endif +#endif + +/* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** @@ -9062,8 +9256,8 @@ struct BusyHandler { #define SQLITE_WSD const #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) -SQLITE_API int sqlite3_wsd_init(int N, int J); -SQLITE_API void *sqlite3_wsd_find(void *K, int L); +SQLITE_API int SQLITE_STDCALL sqlite3_wsd_init(int N, int J); +SQLITE_API void *SQLITE_STDCALL sqlite3_wsd_find(void *K, int L); #else #define SQLITE_WSD #define GLOBAL(t,v) v @@ -9221,10 +9415,8 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*); SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); -#endif SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); @@ -9302,8 +9494,18 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); /* ** Values that may be OR'd together to form the second argument of an ** sqlite3BtreeCursorHints() call. +** +** The BTREE_BULKLOAD flag is set on index cursors when the index is going +** to be filled with content that is already in sorted order. +** +** The BTREE_SEEK_EQ flag is set on cursors that will get OP_SeekGE or +** OP_SeekLE opcodes for a range search, but where the range of entries +** selected will all have the same key. In other words, the cursor will +** be used only for equality key searches. +** */ -#define BTREE_BULKLOAD 0x00000001 +#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */ +#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */ SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ @@ -9349,6 +9551,9 @@ SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *); SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); +#ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask); +#endif SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt); SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void); @@ -9715,23 +9920,25 @@ typedef struct VdbeOpList VdbeOpList; #define OP_MemMax 136 /* synopsis: r[P1]=max(r[P1],r[P2]) */ #define OP_IfPos 137 /* synopsis: if r[P1]>0 goto P2 */ #define OP_IfNeg 138 /* synopsis: r[P1]+=P3, if r[P1]<0 goto P2 */ -#define OP_IfZero 139 /* synopsis: r[P1]+=P3, if r[P1]==0 goto P2 */ -#define OP_AggFinal 140 /* synopsis: accum=r[P1] N=P2 */ -#define OP_IncrVacuum 141 -#define OP_Expire 142 -#define OP_TableLock 143 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 144 -#define OP_VCreate 145 -#define OP_VDestroy 146 -#define OP_VOpen 147 -#define OP_VColumn 148 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VNext 149 -#define OP_VRename 150 -#define OP_Pagecount 151 -#define OP_MaxPgcnt 152 -#define OP_Init 153 /* synopsis: Start at P2 */ -#define OP_Noop 154 -#define OP_Explain 155 +#define OP_IfNotZero 139 /* synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2 */ +#define OP_DecrJumpZero 140 /* synopsis: if (--r[P1])==0 goto P2 */ +#define OP_JumpZeroIncr 141 /* synopsis: if (r[P1]++)==0 ) goto P2 */ +#define OP_AggFinal 142 /* synopsis: accum=r[P1] N=P2 */ +#define OP_IncrVacuum 143 +#define OP_Expire 144 +#define OP_TableLock 145 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 146 +#define OP_VCreate 147 +#define OP_VDestroy 148 +#define OP_VOpen 149 +#define OP_VColumn 150 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VNext 151 +#define OP_VRename 152 +#define OP_Pagecount 153 +#define OP_MaxPgcnt 154 +#define OP_Init 155 /* synopsis: Start at P2 */ +#define OP_Noop 156 +#define OP_Explain 157 /* Properties such as "out2" or "jump" that are specified in @@ -9739,33 +9946,32 @@ typedef struct VdbeOpList VdbeOpList; ** are encoded into bitvectors as follows: */ #define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ -#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ -#define OPFLG_IN1 0x0004 /* in1: P1 is an input */ -#define OPFLG_IN2 0x0008 /* in2: P2 is an input */ -#define OPFLG_IN3 0x0010 /* in3: P3 is an input */ -#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ -#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ +#define OPFLG_IN1 0x0002 /* in1: P1 is an input */ +#define OPFLG_IN2 0x0004 /* in2: P2 is an input */ +#define OPFLG_IN3 0x0008 /* in3: P3 is an input */ +#define OPFLG_OUT2 0x0010 /* out2: P2 is an output */ +#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\ -/* 8 */ 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,\ -/* 16 */ 0x01, 0x01, 0x04, 0x24, 0x01, 0x04, 0x05, 0x10,\ -/* 24 */ 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02,\ -/* 32 */ 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x05, 0x04,\ -/* 40 */ 0x04, 0x00, 0x00, 0x01, 0x01, 0x05, 0x05, 0x00,\ -/* 48 */ 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x00, 0x00,\ -/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ -/* 64 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x4c,\ -/* 72 */ 0x4c, 0x02, 0x02, 0x00, 0x05, 0x05, 0x15, 0x15,\ -/* 80 */ 0x15, 0x15, 0x15, 0x15, 0x00, 0x4c, 0x4c, 0x4c,\ -/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x00,\ -/* 96 */ 0x24, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,\ -/* 104 */ 0x00, 0x01, 0x01, 0x01, 0x01, 0x08, 0x08, 0x00,\ -/* 112 */ 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00,\ -/* 120 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x02, 0x00, 0x01,\ -/* 136 */ 0x08, 0x05, 0x05, 0x05, 0x00, 0x01, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,\ -/* 152 */ 0x02, 0x01, 0x00, 0x00,} +/* 8 */ 0x01, 0x01, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,\ +/* 16 */ 0x01, 0x01, 0x02, 0x12, 0x01, 0x02, 0x03, 0x08,\ +/* 24 */ 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10,\ +/* 32 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x03, 0x02,\ +/* 40 */ 0x02, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x00,\ +/* 48 */ 0x00, 0x00, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00,\ +/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09,\ +/* 64 */ 0x09, 0x09, 0x04, 0x09, 0x09, 0x09, 0x09, 0x26,\ +/* 72 */ 0x26, 0x10, 0x10, 0x00, 0x03, 0x03, 0x0b, 0x0b,\ +/* 80 */ 0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x26, 0x26, 0x26,\ +/* 88 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\ +/* 96 */ 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ +/* 104 */ 0x00, 0x01, 0x01, 0x01, 0x01, 0x04, 0x04, 0x00,\ +/* 112 */ 0x10, 0x01, 0x01, 0x01, 0x01, 0x10, 0x00, 0x00,\ +/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 128 */ 0x06, 0x23, 0x0b, 0x01, 0x10, 0x10, 0x00, 0x01,\ +/* 136 */ 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x01,\ +/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,\ +/* 152 */ 0x00, 0x10, 0x10, 0x01, 0x00, 0x00,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -9824,6 +10030,7 @@ SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int); SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); @@ -10841,11 +11048,13 @@ struct sqlite3 { u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ + u8 imposterTable; /* Building an imposter table */ } init; int nVdbeActive; /* Number of VDBEs currently running */ int nVdbeRead; /* Number of active VDBEs that read or write */ int nVdbeWrite; /* Number of active VDBEs that read and write */ int nVdbeExec; /* Number of nested calls to VdbeExec() */ + int nVDestroy; /* Number of active OP_VDestroy operations */ int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ void (*xTrace)(void*,const char*); /* Trace function */ @@ -10959,6 +11168,7 @@ struct sqlite3 { #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ #define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ +#define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */ /* @@ -11289,34 +11499,8 @@ struct VTable { }; /* -** Each SQL table is represented in memory by an instance of the -** following structure. -** -** Table.zName is the name of the table. The case of the original -** CREATE TABLE statement is stored, but case is not significant for -** comparisons. -** -** Table.nCol is the number of columns in this table. Table.aCol is a -** pointer to an array of Column structures, one for each column. -** -** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of -** the column that is that key. Otherwise Table.iPKey is negative. Note -** that the datatype of the PRIMARY KEY must be INTEGER for this field to -** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of -** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid -** is generated for each row of the table. TF_HasPrimaryKey is set if -** the table has any PRIMARY KEY, INTEGER or otherwise. -** -** Table.tnum is the page number for the root BTree page of the table in the -** database file. If Table.iDb is the index of the database table backend -** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that -** holds temporary tables and indices. If TF_Ephemeral is set -** then the table is stored in a file that is automatically deleted -** when the VDBE cursor to the table is closed. In this case Table.tnum -** refers VDBE cursor number that holds the table open, not to the root -** page number. Transient tables are used to hold the results of a -** sub-query that appears instead of a real table name in the FROM clause -** of a SELECT statement. +** The schema for each SQL table and view is represented in memory +** by an instance of the following structure. */ struct Table { char *zName; /* Name of the table or view */ @@ -11328,11 +11512,11 @@ struct Table { #ifndef SQLITE_OMIT_CHECK ExprList *pCheck; /* All CHECK constraints */ #endif - LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ - int tnum; /* Root BTree node for this table (see note above) */ - i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + int tnum; /* Root BTree page for this table */ + i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */ i16 nCol; /* Number of columns in this table */ u16 nRef; /* Number of pointers to this Table */ + LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */ LogEst szTabRow; /* Estimated size of each table row in bytes */ #ifdef SQLITE_ENABLE_COSTMULT LogEst costMult; /* Cost multiplier for using this table */ @@ -11354,6 +11538,12 @@ struct Table { /* ** Allowed values for Table.tabFlags. +** +** TF_OOOHidden applies to virtual tables that have hidden columns that are +** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING +** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden, +** the TF_OOOHidden attribute would apply in this case. Such tables require +** special handling during INSERT processing. */ #define TF_Readonly 0x01 /* Read-only system table */ #define TF_Ephemeral 0x02 /* An ephemeral table */ @@ -11361,6 +11551,7 @@ struct Table { #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ #define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */ +#define TF_OOOHidden 0x40 /* Out-of-Order hidden columns */ /* @@ -11797,8 +11988,14 @@ struct Expr { #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ -#define EP_Constant 0x080000 /* Node is a constant */ +#define EP_ConstFunc 0x080000 /* Node is a SQLITE_FUNC_CONSTANT function */ #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ +#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */ + +/* +** Combinations of two or more EP_* flags +*/ +#define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */ /* ** These macros can be used to test, set, or clear bits in the @@ -11997,7 +12194,7 @@ struct SrcList { #define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */ #define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */ #define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */ - /* 0x0080 // not currently used */ +#define WHERE_NO_AUTOINDEX 0x0080 /* Disallow automatic indexes */ #define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */ #define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */ #define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */ @@ -12111,11 +12308,12 @@ struct Select { #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0040 /* Part of a compound query */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ -#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ +#define SF_MultiValue 0x0100 /* Single VALUES term with multiple rows */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ +#define SF_Converted 0x2000 /* By convertCompoundSelectToSubquery() */ /* @@ -12434,7 +12632,8 @@ struct AuthContext { #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ -#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */ +#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ +#define OPFLAG_P2ISREG 0x04 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ /* @@ -12493,7 +12692,7 @@ struct Trigger { * orconf -> stores the ON CONFLICT algorithm * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then * this stores a pointer to the SELECT statement. Otherwise NULL. - * target -> A token holding the quoted name of the table to insert into. + * zTarget -> Dequoted name of the table to insert into. * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then * this stores values to be inserted. Otherwise NULL. * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ... @@ -12501,12 +12700,12 @@ struct Trigger { * inserted into. * * (op == TK_DELETE) - * target -> A token holding the quoted name of the table to delete from. + * zTarget -> Dequoted name of the table to delete from. * pWhere -> The WHERE clause of the DELETE statement if one is specified. * Otherwise NULL. * * (op == TK_UPDATE) - * target -> A token holding the quoted name of the table to update rows of. + * zTarget -> Dequoted name of the table to update. * pWhere -> The WHERE clause of the UPDATE statement if one is specified. * Otherwise NULL. * pExprList -> A list of the columns to update and the expressions to update @@ -12518,8 +12717,8 @@ struct TriggerStep { u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ - Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ - Token target; /* Target table for DELETE, UPDATE, INSERT */ + Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ + char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ ExprList *pExprList; /* SET clause for UPDATE. */ IdList *pIdList; /* Column names for INSERT */ @@ -12552,8 +12751,7 @@ struct StrAccum { char *zText; /* The string collected so far */ int nChar; /* Length of the string so far */ int nAlloc; /* Amount of space allocated in zText */ - int mxAlloc; /* Maximum allowed string length */ - u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */ + int mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */ }; #define STRACCUM_NOMEM 1 @@ -12838,10 +13036,15 @@ SQLITE_PRIVATE int sqlite3MutexInit(void); SQLITE_PRIVATE int sqlite3MutexEnd(void); #endif -SQLITE_PRIVATE int sqlite3StatusValue(int); -SQLITE_PRIVATE void sqlite3StatusAdd(int, int); +SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int); +SQLITE_PRIVATE void sqlite3StatusUp(int, int); +SQLITE_PRIVATE void sqlite3StatusDown(int, int); SQLITE_PRIVATE void sqlite3StatusSet(int, int); +/* Access to mutexes used by sqlite3_status() */ +SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void); +SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void); + #ifndef SQLITE_OMIT_FLOATING_POINT SQLITE_PRIVATE int sqlite3IsNaN(double); #else @@ -12865,7 +13068,7 @@ SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, u32, const char*, ...); SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...); -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...); #endif #if defined(SQLITE_TEST) @@ -12906,6 +13109,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); +SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); @@ -13211,7 +13415,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); -#if defined(SQLITE_TEST) +#if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int); #endif @@ -13220,7 +13424,7 @@ SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); -SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*); +SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int); SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*); SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*); SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); @@ -13305,7 +13509,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int); +SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int); SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*); SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char); @@ -13489,12 +13693,11 @@ SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *); SQLITE_PRIVATE int sqlite3MemJournalSize(void); SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *); +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p); #if SQLITE_MAX_EXPR_DEPTH>0 -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p); SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *); SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); #else - #define sqlite3ExprSetHeight(x,y) #define sqlite3SelectExprHeight(x) 0 #define sqlite3ExprCheckHeight(x,y) #endif @@ -13524,7 +13727,7 @@ SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *); #ifdef SQLITE_ENABLE_IOTRACE # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); -void (*sqlite3IoTrace)(const char*,...); +SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...); #else # define IOTRACE(A) # define sqlite3VdbeIOTraceSql(X) @@ -13631,16 +13834,16 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ - 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */ - 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */ + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */ + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ - 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */ + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */ 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ - 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */ - 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */ + 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */ + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */ #endif }; @@ -13923,6 +14126,9 @@ static const char * const azCompileOpt[] = { #if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif +#if SQLITE_ENABLE_DBSTAT_VTAB + "ENABLE_DBSTAT_VTAB", +#endif #if SQLITE_ENABLE_EXPENSIVE_ASSERT "ENABLE_EXPENSIVE_ASSERT", #endif @@ -14237,7 +14443,7 @@ static const char * const azCompileOpt[] = { ** The name can optionally begin with "SQLITE_" but the "SQLITE_" prefix ** is not required for a match. */ -SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName){ int i, n; #if SQLITE_ENABLE_API_ARMOR @@ -14265,7 +14471,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ ** Return the N-th compile-time option string. If N is out of range, ** return a NULL pointer. */ -SQLITE_API const char *sqlite3_compileoption_get(int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N){ if( N>=0 && N<ArraySize(azCompileOpt) ){ return azCompileOpt[N]; } @@ -14608,14 +14814,6 @@ struct ScanStatus { ** ** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() ** is really a pointer to an instance of this structure. -** -** The Vdbe.inVtabMethod variable is set to non-zero for the duration of -** any virtual table method invocations made by the vdbe program. It is -** set to 2 for xDestroy method calls and 1 for all other methods. This -** variable is used for two purposes: to allow xDestroy methods to execute -** "DROP TABLE" statements and to prevent some nasty side effects of -** malloc failure when SQLite is invoked recursively by a virtual table -** method function. */ struct Vdbe { sqlite3 *db; /* The database connection that owns this statement */ @@ -14639,11 +14837,13 @@ struct Vdbe { u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ +#ifdef SQLITE_DEBUG + int rcApp; /* errcode set by sqlite3_result_error_code() */ +#endif u16 nResColumn; /* Number of columns in one row of the result set */ u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ bft explain:2; /* True if EXPLAIN present on SQL command */ - bft inVtabMethod:2; /* See comments above */ bft changeCntOn:1; /* True to update the change-counter */ bft expired:1; /* True if the VM needs to be recompiled */ bft runOnlyOnce:1; /* Automatically expire on reset */ @@ -14803,10 +15003,32 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *); */ typedef struct sqlite3StatType sqlite3StatType; static SQLITE_WSD struct sqlite3StatType { - int nowValue[10]; /* Current value */ - int mxValue[10]; /* Maximum value */ +#if SQLITE_PTRSIZE>4 + sqlite3_int64 nowValue[10]; /* Current value */ + sqlite3_int64 mxValue[10]; /* Maximum value */ +#else + u32 nowValue[10]; /* Current value */ + u32 mxValue[10]; /* Maximum value */ +#endif } sqlite3Stat = { {0,}, {0,} }; +/* +** Elements of sqlite3Stat[] are protected by either the memory allocator +** mutex, or by the pcache1 mutex. The following array determines which. +*/ +static const char statMutex[] = { + 0, /* SQLITE_STATUS_MEMORY_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_USED */ + 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */ + 0, /* SQLITE_STATUS_SCRATCH_USED */ + 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */ + 0, /* SQLITE_STATUS_MALLOC_SIZE */ + 0, /* SQLITE_STATUS_PARSER_STACK */ + 1, /* SQLITE_STATUS_PAGECACHE_SIZE */ + 0, /* SQLITE_STATUS_SCRATCH_SIZE */ + 0, /* SQLITE_STATUS_MALLOC_COUNT */ +}; + /* The "wsdStat" macro will resolve to the status information ** state vector. If writable static data is unsupported on the target, @@ -14823,33 +15045,60 @@ static SQLITE_WSD struct sqlite3StatType { #endif /* -** Return the current value of a status parameter. +** Return the current value of a status parameter. The caller must +** be holding the appropriate mutex. */ -SQLITE_PRIVATE int sqlite3StatusValue(int op){ +SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int op){ wsdStatInit; assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); + assert( op>=0 && op<ArraySize(statMutex) ); + assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() + : sqlite3MallocMutex()) ); return wsdStat.nowValue[op]; } /* -** Add N to the value of a status record. It is assumed that the -** caller holds appropriate locks. +** Add N to the value of a status record. The caller must hold the +** appropriate mutex. (Locking is checked by assert()). +** +** The StatusUp() routine can accept positive or negative values for N. +** The value of N is added to the current status value and the high-water +** mark is adjusted if necessary. +** +** The StatusDown() routine lowers the current value by N. The highwater +** mark is unchanged. N must be non-negative for StatusDown(). */ -SQLITE_PRIVATE void sqlite3StatusAdd(int op, int N){ +SQLITE_PRIVATE void sqlite3StatusUp(int op, int N){ wsdStatInit; assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); + assert( op>=0 && op<ArraySize(statMutex) ); + assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() + : sqlite3MallocMutex()) ); wsdStat.nowValue[op] += N; if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; } } +SQLITE_PRIVATE void sqlite3StatusDown(int op, int N){ + wsdStatInit; + assert( N>=0 ); + assert( op>=0 && op<ArraySize(statMutex) ); + assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() + : sqlite3MallocMutex()) ); + assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); + wsdStat.nowValue[op] -= N; +} /* -** Set the value of a status to X. +** Set the value of a status to X. The highwater mark is adjusted if +** necessary. The caller must hold the appropriate mutex. */ SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){ wsdStatInit; assert( op>=0 && op<ArraySize(wsdStat.nowValue) ); + assert( op>=0 && op<ArraySize(statMutex) ); + assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex() + : sqlite3MallocMutex()) ); wsdStat.nowValue[op] = X; if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; @@ -14858,12 +15107,14 @@ SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){ /* ** Query status information. -** -** This implementation assumes that reading or writing an aligned -** 32-bit integer is an atomic operation. If that assumption is not true, -** then this routine is not threadsafe. */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +){ + sqlite3_mutex *pMutex; wsdStatInit; if( op<0 || op>=ArraySize(wsdStat.nowValue) ){ return SQLITE_MISUSE_BKPT; @@ -14871,18 +15122,35 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF #ifdef SQLITE_ENABLE_API_ARMOR if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; #endif + pMutex = statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex(); + sqlite3_mutex_enter(pMutex); *pCurrent = wsdStat.nowValue[op]; *pHighwater = wsdStat.mxValue[op]; if( resetFlag ){ wsdStat.mxValue[op] = wsdStat.nowValue[op]; } + sqlite3_mutex_leave(pMutex); + (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */ return SQLITE_OK; } +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){ + sqlite3_int64 iCur, iHwtr; + int rc; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT; +#endif + rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag); + if( rc==0 ){ + *pCurrent = (int)iCur; + *pHighwater = (int)iHwtr; + } + return rc; +} /* ** Query status information for a single database connection */ -SQLITE_API int sqlite3_db_status( +SQLITE_API int SQLITE_STDCALL sqlite3_db_status( sqlite3 *db, /* The database connection whose status is desired */ int op, /* Status verb */ int *pCurrent, /* Write current value here */ @@ -16506,7 +16774,7 @@ static sqlite3_vfs * SQLITE_WSD vfsList = 0; ** Locate a VFS by name. If no name is given, simply return the ** first VFS on the list. */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){ +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfs){ sqlite3_vfs *pVfs = 0; #if SQLITE_THREADSAFE sqlite3_mutex *mutex; @@ -16552,7 +16820,7 @@ static void vfsUnlink(sqlite3_vfs *pVfs){ ** VFS multiple times. The new VFS becomes the default if makeDflt is ** true. */ -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ MUTEX_LOGIC(sqlite3_mutex *mutex;) #ifndef SQLITE_OMIT_AUTOINIT int rc = sqlite3_initialize(); @@ -16580,7 +16848,7 @@ SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){ /* ** Unregister a VFS so that it is no longer accessible. */ -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs *pVfs){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -18916,7 +19184,7 @@ SQLITE_PRIVATE int sqlite3MutexEnd(void){ /* ** Retrieve a pointer to a static mutex or allocate a new dynamic one. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){ +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int id){ #ifndef SQLITE_OMIT_AUTOINIT if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0; if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0; @@ -18935,7 +19203,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){ /* ** Free a dynamic mutex. */ -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexFree(p); } @@ -18945,7 +19213,7 @@ SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){ ** Obtain the mutex p. If some other thread already has the mutex, block ** until it can be obtained. */ -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexEnter(p); } @@ -18955,7 +19223,7 @@ SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){ ** Obtain the mutex p. If successful, return SQLITE_OK. Otherwise, if another ** thread holds the mutex and it cannot be obtained, return SQLITE_BUSY. */ -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex *p){ int rc = SQLITE_OK; if( p ){ return sqlite3GlobalConfig.mutex.xMutexTry(p); @@ -18969,7 +19237,7 @@ SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){ ** is not currently entered. If a NULL pointer is passed as an argument ** this function is a no-op. */ -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex *p){ if( p ){ sqlite3GlobalConfig.mutex.xMutexLeave(p); } @@ -18980,10 +19248,10 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){ ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use inside assert() statements. */ -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p); } -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex *p){ return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p); } #endif @@ -19113,8 +19381,12 @@ static sqlite3_mutex *debugMutexAlloc(int id){ break; } default: { - assert( id-2 >= 0 ); - assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) ); +#ifdef SQLITE_ENABLE_API_ARMOR + if( id-2<0 || id-2>=ArraySize(aStatic) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif pNew = &aStatic[id-2]; pNew->id = id; break; @@ -19129,8 +19401,13 @@ static sqlite3_mutex *debugMutexAlloc(int id){ static void debugMutexFree(sqlite3_mutex *pX){ sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX; assert( p->cnt==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - sqlite3_free(p); + if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){ + sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; +#endif + } } /* @@ -19241,8 +19518,10 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ */ struct sqlite3_mutex { pthread_mutex_t mutex; /* Mutex controlling the lock */ -#if SQLITE_MUTEX_NREF +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) int id; /* Mutex type */ +#endif +#if SQLITE_MUTEX_NREF volatile int nRef; /* Number of entrances */ volatile pthread_t owner; /* Thread that is within this mutex */ int trace; /* True to trace changes */ @@ -19359,18 +19638,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ pthread_mutex_init(&p->mutex, &recursiveAttr); pthread_mutexattr_destroy(&recursiveAttr); #endif -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif } break; } case SQLITE_MUTEX_FAST: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif pthread_mutex_init(&p->mutex, 0); } break; @@ -19383,12 +19656,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ } #endif p = &staticMutexes[iType-2]; -#if SQLITE_MUTEX_NREF - p->id = iType; -#endif break; } } +#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) + if( p ) p->id = iType; +#endif return p; } @@ -19400,9 +19673,18 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ */ static void pthreadMutexFree(sqlite3_mutex *p){ assert( p->nRef==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); - pthread_mutex_destroy(&p->mutex); - sqlite3_free(p); +#if SQLITE_ENABLE_API_ARMOR + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) +#endif + { + pthread_mutex_destroy(&p->mutex); + sqlite3_free(p); + } +#ifdef SQLITE_ENABLE_API_ARMOR + else{ + (void)SQLITE_MISUSE_BKPT; + } +#endif } /* @@ -19614,16 +19896,6 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -19872,6 +20144,17 @@ SQLITE_API int sqlite3_open_file_count = 0; # define SQLITE_WIN32_VOLATILE volatile #endif +/* +** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() +** functions are not available (e.g. those not using MSVC, Cygwin, etc). +*/ +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) +# define SQLITE_OS_WIN_THREADS 1 +#else +# define SQLITE_OS_WIN_THREADS 0 +#endif + #endif /* _OS_WIN_H_ */ /************** End of os_win.h **********************************************/ @@ -19954,8 +20237,8 @@ static int winMutex_isNt = -1; /* <0 means "need to query" */ */ static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; -SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */ -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void); /* os_win.c */ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ static int winMutexInit(void){ /* The first to increment to 1 does actual initialization */ @@ -20047,8 +20330,8 @@ static sqlite3_mutex *winMutexAlloc(int iType){ case SQLITE_MUTEX_RECURSIVE: { p = sqlite3MallocZero( sizeof(*p) ); if( p ){ -#ifdef SQLITE_DEBUG p->id = iType; +#ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC p->trace = 1; #endif @@ -20068,12 +20351,9 @@ static sqlite3_mutex *winMutexAlloc(int iType){ return 0; } #endif - assert( iType-2 >= 0 ); - assert( iType-2 < ArraySize(winMutex_staticMutexes) ); - assert( winMutex_isInit==1 ); p = &winMutex_staticMutexes[iType-2]; -#ifdef SQLITE_DEBUG p->id = iType; +#ifdef SQLITE_DEBUG #ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC p->trace = 1; #endif @@ -20092,13 +20372,15 @@ static sqlite3_mutex *winMutexAlloc(int iType){ */ static void winMutexFree(sqlite3_mutex *p){ assert( p ); -#ifdef SQLITE_DEBUG assert( p->nRef==0 && p->owner==0 ); - assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); + if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ + DeleteCriticalSection(&p->mutex); + sqlite3_free(p); + }else{ +#ifdef SQLITE_ENABLE_API_ARMOR + (void)SQLITE_MISUSE_BKPT; #endif - assert( winMutex_isInit==1 ); - DeleteCriticalSection(&p->mutex); - sqlite3_free(p); + } } /* @@ -20252,7 +20534,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ ** held by SQLite. An example of non-essential memory is memory used to ** cache database pages that are not currently in use. */ -SQLITE_API int sqlite3_release_memory(int n){ +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT return sqlite3PcacheReleaseMemory(n); #else @@ -20308,6 +20590,13 @@ static SQLITE_WSD struct Mem0Global { #define mem0 GLOBAL(struct Mem0Global, mem0) /* +** Return the memory allocator mutex. sqlite3_status() needs it. +*/ +SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void){ + return mem0.mutex; +} + +/* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap ** limit. @@ -20329,7 +20618,7 @@ static int sqlite3MemoryAlarm( void *pArg, sqlite3_int64 iThreshold ){ - int nUsed; + sqlite3_int64 nUsed; sqlite3_mutex_enter(mem0.mutex); mem0.alarmCallback = xCallback; mem0.alarmArg = pArg; @@ -20345,7 +20634,7 @@ static int sqlite3MemoryAlarm( ** Deprecated external interface. Internal/core SQLite code ** should call sqlite3MemoryAlarm. */ -SQLITE_API int sqlite3_memory_alarm( +SQLITE_API int SQLITE_STDCALL sqlite3_memory_alarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold @@ -20358,7 +20647,7 @@ SQLITE_API int sqlite3_memory_alarm( ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 n){ sqlite3_int64 priorLimit; sqlite3_int64 excess; #ifndef SQLITE_OMIT_AUTOINIT @@ -20378,7 +20667,7 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); return priorLimit; } -SQLITE_API void sqlite3_soft_heap_limit(int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_soft_heap_limit(int n){ if( n<0 ) n = 0; sqlite3_soft_heap_limit64(n); } @@ -20387,6 +20676,7 @@ SQLITE_API void sqlite3_soft_heap_limit(int n){ ** Initialize the memory allocation subsystem. */ SQLITE_PRIVATE int sqlite3MallocInit(void){ + int rc; if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } @@ -20422,7 +20712,9 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){ sqlite3GlobalConfig.szPage = 0; sqlite3GlobalConfig.nPage = 0; } - return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); + rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); + if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); + return rc; } /* @@ -20447,7 +20739,7 @@ SQLITE_PRIVATE void sqlite3MallocEnd(void){ /* ** Return the amount of memory currently checked out. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void){ int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0); @@ -20460,7 +20752,7 @@ SQLITE_API sqlite3_int64 sqlite3_memory_used(void){ ** checked out since either the beginning of this process ** or since the most recent reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag){ int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); @@ -20498,7 +20790,7 @@ static int mallocWithAlarm(int n, void **pp){ nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ - int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); + sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.alarmThreshold - nFull ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); @@ -20515,8 +20807,8 @@ static int mallocWithAlarm(int n, void **pp){ #endif if( p ){ nFull = sqlite3MallocSize(p); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); + sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; @@ -20551,13 +20843,13 @@ SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ ** First make sure the memory subsystem is initialized, then do the ** allocation. */ -SQLITE_API void *sqlite3_malloc(int n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif return n<=0 ? 0 : sqlite3Malloc(n); } -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -20593,14 +20885,14 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ p = mem0.pScratchFree; mem0.pScratchFree = mem0.pScratchFree->pNext; mem0.nScratchFree--; - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1); + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3_mutex_leave(mem0.mutex); p = sqlite3Malloc(n); if( sqlite3GlobalConfig.bMemstat && p ){ sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p)); + sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p)); sqlite3_mutex_leave(mem0.mutex); } sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); @@ -20641,19 +20933,19 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ mem0.pScratchFree = pSlot; mem0.nScratchFree++; assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3_mutex_leave(mem0.mutex); }else{ /* Release memory back to the heap */ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); if( sqlite3GlobalConfig.bMemstat ){ int iSize = sqlite3MallocSize(p); sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); + sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize); + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize); + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20684,7 +20976,7 @@ SQLITE_PRIVATE int sqlite3MallocSize(void *p){ } SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ if( db==0 ){ - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3MallocSize(p); }else{ @@ -20693,13 +20985,13 @@ SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ return db->lookaside.sz; }else{ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); return sqlite3GlobalConfig.m.xSize(p); } } } -SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void *p){ + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return (sqlite3_uint64)sqlite3GlobalConfig.m.xSize(p); } @@ -20707,14 +20999,14 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ /* ** Free memory previously obtained from sqlite3Malloc(). */ -SQLITE_API void sqlite3_free(void *p){ +SQLITE_API void SQLITE_STDCALL sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); - sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); + sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p)); + sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20755,7 +21047,7 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ } } assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3_free(p); @@ -20768,7 +21060,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ int nOld, nNew, nDiff; void *pNew; assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) ); if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ } @@ -20802,7 +21094,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ } if( pNew ){ nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); + sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ @@ -20816,14 +21108,14 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ -SQLITE_API void *sqlite3_realloc(void *pOld, int n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void *pOld, int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif if( n<0 ) n = 0; /* IMP: R-26507-47431 */ return sqlite3Realloc(pOld, n); } -SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif @@ -20935,7 +21227,7 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){ } }else{ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); - assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); pNew = sqlite3_realloc64(p, n); if( !pNew ){ @@ -21188,6 +21480,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ ** Set the StrAccum object to an error mode. */ static void setStrAccumError(StrAccum *p, u8 eError){ + assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG ); p->accError = eError; p->nAlloc = 0; } @@ -21262,13 +21555,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf( PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ -#ifdef SQLITE_ENABLE_API_ARMOR - if( ap==0 ){ - (void)SQLITE_MISUSE_BKPT; - sqlite3StrAccumReset(pAccum); - return; - } -#endif bufpt = 0; if( bFlags ){ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ @@ -21309,7 +21595,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf( } }while( !done && (c=(*++fmt))!=0 ); /* Get the field width */ - width = 0; if( c=='*' ){ if( bArgList ){ width = (int)getIntArg(pArgList); @@ -21318,18 +21603,21 @@ SQLITE_PRIVATE void sqlite3VXPrintf( } if( width<0 ){ flag_leftjustify = 1; - width = -width; + width = width >= -2147483647 ? -width : 0; } c = *++fmt; }else{ + unsigned wx = 0; while( c>='0' && c<='9' ){ - width = width*10 + c - '0'; + wx = wx*10 + c - '0'; c = *++fmt; } + testcase( wx>0x7fffffff ); + width = wx & 0x7fffffff; } + /* Get the precision */ if( c=='.' ){ - precision = 0; c = *++fmt; if( c=='*' ){ if( bArgList ){ @@ -21337,13 +21625,18 @@ SQLITE_PRIVATE void sqlite3VXPrintf( }else{ precision = va_arg(ap,int); } - if( precision<0 ) precision = -precision; c = *++fmt; + if( precision<0 ){ + precision = precision >= -2147483647 ? -precision : -1; + } }else{ + unsigned px = 0; while( c>='0' && c<='9' ){ - precision = precision*10 + c - '0'; + px = px*10 + c - '0'; c = *++fmt; } + testcase( px>0x7fffffff ); + precision = px & 0x7fffffff; } }else{ precision = -1; @@ -21507,7 +21800,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf( else prefix = 0; } if( xtype==etGENERIC && precision>0 ) precision--; - for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){} + testcase( precision>0xfff ); + for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){} if( xtype==etFLOAT ) realvalue += rounder; /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ exp = 0; @@ -21562,8 +21856,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf( }else{ e2 = exp; } - if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){ - bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 ); + if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){ + bufpt = zExtra + = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 ); if( bufpt==0 ){ setStrAccumError(pAccum, STRACCUM_NOMEM); return; @@ -21795,13 +22090,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf( */ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ char *zNew; - assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ + assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ if( p->accError ){ testcase(p->accError==STRACCUM_TOOBIG); testcase(p->accError==STRACCUM_NOMEM); return 0; } - if( !p->useMalloc ){ + if( p->mxAlloc==0 ){ N = p->nAlloc - p->nChar - 1; setStrAccumError(p, STRACCUM_TOOBIG); return N; @@ -21821,10 +22116,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ }else{ p->nAlloc = (int)szNew; } - if( p->useMalloc==1 ){ + if( p->db ){ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); }else{ - zNew = sqlite3_realloc(zOld, p->nAlloc); + zNew = sqlite3_realloc64(zOld, p->nAlloc); } if( zNew ){ assert( p->zText!=0 || p->nChar==0 ); @@ -21844,7 +22139,10 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ ** Append N copies of character c to the given string buffer. */ SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){ - if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return; + testcase( p->nChar + (i64)N > 0x7fffffff ); + if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ + return; + } while( (N--)>0 ) p->zText[p->nChar++] = c; } @@ -21869,7 +22167,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ ** size of the memory allocation for StrAccum if necessary. */ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ - assert( z!=0 ); + assert( z!=0 || N==0 ); assert( p->zText!=0 || p->nChar==0 || p->accError ); assert( N>=0 ); assert( p->accError==0 || p->nAlloc==0 ); @@ -21898,12 +22196,8 @@ SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ if( p->zText ){ p->zText[p->nChar] = 0; - if( p->useMalloc && p->zText==p->zBase ){ - if( p->useMalloc==1 ){ - p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); - }else{ - p->zText = sqlite3_malloc(p->nChar+1); - } + if( p->mxAlloc>0 && p->zText==p->zBase ){ + p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); if( p->zText ){ memcpy(p->zText, p->zBase, p->nChar+1); }else{ @@ -21919,25 +22213,31 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ */ SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){ if( p->zText!=p->zBase ){ - if( p->useMalloc==1 ){ - sqlite3DbFree(p->db, p->zText); - }else{ - sqlite3_free(p->zText); - } + sqlite3DbFree(p->db, p->zText); } p->zText = 0; } /* -** Initialize a string accumulator +** Initialize a string accumulator. +** +** p: The accumulator to be initialized. +** db: Pointer to a database connection. May be NULL. Lookaside +** memory is used if not NULL. db->mallocFailed is set appropriately +** when not NULL. +** zBase: An initial buffer. May be NULL in which case the initial buffer +** is malloced. +** n: Size of zBase in bytes. If total space requirements never exceed +** n then no memory allocations ever occur. +** mx: Maximum number of bytes to accumulate. If mx==0 then no memory +** allocations will ever occur. */ -SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){ +SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ p->zText = p->zBase = zBase; - p->db = 0; + p->db = db; p->nChar = 0; p->nAlloc = n; p->mxAlloc = mx; - p->useMalloc = 1; p->accError = 0; } @@ -21950,9 +22250,8 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a char zBase[SQLITE_PRINT_BUF_SIZE]; StrAccum acc; assert( db!=0 ); - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), + sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); - acc.db = db; sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap); z = sqlite3StrAccumFinish(&acc); if( acc.accError==STRACCUM_NOMEM ){ @@ -21996,7 +22295,7 @@ SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zForma ** Print into memory obtained from sqlite3_malloc(). Omit the internal ** %-conversion extensions. */ -SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char *zFormat, va_list ap){ char *z; char zBase[SQLITE_PRINT_BUF_SIZE]; StrAccum acc; @@ -22010,8 +22309,7 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif - sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); - acc.useMalloc = 2; + sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); sqlite3VXPrintf(&acc, 0, zFormat, ap); z = sqlite3StrAccumFinish(&acc); return z; @@ -22021,7 +22319,7 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){ ** Print into memory obtained from sqlite3_malloc()(). Omit the internal ** %-conversion extensions. */ -SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char *zFormat, ...){ va_list ap; char *z; #ifndef SQLITE_OMIT_AUTOINIT @@ -22046,22 +22344,21 @@ SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ ** ** sqlite3_vsnprintf() is the varargs version. */ -SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ StrAccum acc; if( n<=0 ) return zBuf; #ifdef SQLITE_ENABLE_API_ARMOR if( zBuf==0 || zFormat==0 ) { (void)SQLITE_MISUSE_BKPT; - if( zBuf && n>0 ) zBuf[0] = 0; + if( zBuf ) zBuf[0] = 0; return zBuf; } #endif - sqlite3StrAccumInit(&acc, zBuf, n, 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); sqlite3VXPrintf(&acc, 0, zFormat, ap); return sqlite3StrAccumFinish(&acc); } -SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ char *z; va_list ap; va_start(ap,zFormat); @@ -22083,8 +22380,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ StrAccum acc; /* String accumulator */ char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ - sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); sqlite3VXPrintf(&acc, 0, zFormat, ap); sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode, sqlite3StrAccumFinish(&acc)); @@ -22093,7 +22389,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ /* ** Format and write a message to the log if logging is enabled. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){ +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...){ va_list ap; /* Vararg list */ if( sqlite3GlobalConfig.xLog ){ va_start(ap, zFormat); @@ -22102,7 +22398,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){ } } -#if defined(SQLITE_DEBUG) +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) /* ** A version of printf() that understands %lld. Used for debugging. ** The printf() built into some versions of windows does not understand %lld @@ -22112,8 +22408,7 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ va_list ap; StrAccum acc; char zBuf[500]; - sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); va_start(ap,zFormat); sqlite3VXPrintf(&acc, 0, zFormat, ap); va_end(ap); @@ -22140,7 +22435,7 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ ** is not the last item in the tree. */ SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ if( p==0 ){ - p = sqlite3_malloc( sizeof(*p) ); + p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ) return 0; memset(p, 0, sizeof(*p)); }else{ @@ -22163,8 +22458,7 @@ SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ int i; StrAccum acc; char zBuf[500]; - sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0); - acc.useMalloc = 0; + sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); if( p ){ for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4); @@ -22229,7 +22523,7 @@ static SQLITE_WSD struct sqlite3PrngType { /* ** Return N random bytes. */ -SQLITE_API void sqlite3_randomness(int N, void *pBuf){ +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *pBuf){ unsigned char t; unsigned char *zBuf = pBuf; @@ -22435,7 +22729,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ /********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_OS_WIN_THREADS #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ #include <process.h> @@ -22528,7 +22822,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; } -#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ +#endif /* SQLITE_OS_WIN_THREADS */ /******************************** End Win32 Threads *************************/ @@ -23381,7 +23675,7 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){ ** case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){ +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; if( zLeft==0 ){ return zRight ? -1 : 0; @@ -23393,7 +23687,7 @@ SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } -SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; if( zLeft==0 ){ return zRight ? -1 : 0; @@ -23787,6 +24081,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ } } #endif + while( zNum[0]=='0' ) zNum++; for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ v = v*10 + c; } @@ -24924,23 +25219,25 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 136 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), /* 137 */ "IfPos" OpHelp("if r[P1]>0 goto P2"), /* 138 */ "IfNeg" OpHelp("r[P1]+=P3, if r[P1]<0 goto P2"), - /* 139 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"), - /* 140 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 141 */ "IncrVacuum" OpHelp(""), - /* 142 */ "Expire" OpHelp(""), - /* 143 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 144 */ "VBegin" OpHelp(""), - /* 145 */ "VCreate" OpHelp(""), - /* 146 */ "VDestroy" OpHelp(""), - /* 147 */ "VOpen" OpHelp(""), - /* 148 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 149 */ "VNext" OpHelp(""), - /* 150 */ "VRename" OpHelp(""), - /* 151 */ "Pagecount" OpHelp(""), - /* 152 */ "MaxPgcnt" OpHelp(""), - /* 153 */ "Init" OpHelp("Start at P2"), - /* 154 */ "Noop" OpHelp(""), - /* 155 */ "Explain" OpHelp(""), + /* 139 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]+=P3, goto P2"), + /* 140 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 141 */ "JumpZeroIncr" OpHelp("if (r[P1]++)==0 ) goto P2"), + /* 142 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 143 */ "IncrVacuum" OpHelp(""), + /* 144 */ "Expire" OpHelp(""), + /* 145 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 146 */ "VBegin" OpHelp(""), + /* 147 */ "VCreate" OpHelp(""), + /* 148 */ "VDestroy" OpHelp(""), + /* 149 */ "VOpen" OpHelp(""), + /* 150 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 151 */ "VNext" OpHelp(""), + /* 152 */ "VRename" OpHelp(""), + /* 153 */ "Pagecount" OpHelp(""), + /* 154 */ "MaxPgcnt" OpHelp(""), + /* 155 */ "Init" OpHelp("Start at P2"), + /* 156 */ "Noop" OpHelp(""), + /* 157 */ "Explain" OpHelp(""), }; return azName[i]; } @@ -25021,18 +25318,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ #endif /* -** Define the OS_VXWORKS pre-processor macro to 1 if building on -** vxworks, or 0 otherwise. -*/ -#ifndef OS_VXWORKS -# if defined(__RTP__) || defined(_WRS_KERNEL) -# define OS_VXWORKS 1 -# else -# define OS_VXWORKS 0 -# endif -#endif - -/* ** standard include files. */ #include <sys/types.h> @@ -25046,18 +25331,30 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ # include <sys/mman.h> #endif -#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE # include <sys/ioctl.h> -# if OS_VXWORKS -# include <semaphore.h> -# include <limits.h> -# else -# include <sys/file.h> -# include <sys/param.h> -# endif +# include <sys/file.h> +# include <sys/param.h> #endif /* SQLITE_ENABLE_LOCKING_STYLE */ -#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ + (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) +# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ + && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0)) +# define HAVE_GETHOSTUUID 1 +# else +# warning "gethostuuid() is disabled." +# endif +#endif + + +#if OS_VXWORKS +/* # include <sys/ioctl.h> */ +# include <semaphore.h> +# include <limits.h> +#endif /* OS_VXWORKS */ + +#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE # include <sys/mount.h> #endif @@ -25098,6 +25395,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ */ #define MAX_PATHNAME 512 +/* Always cast the getpid() return type for compatibility with +** kernel modules in VxWorks. */ +#define osGetpid(X) (pid_t)getpid() + /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK @@ -25186,7 +25487,7 @@ struct unixFile { ** method was called. If xOpen() is called from a different process id, ** indicating that a fork() has occurred, the PRNG will be reset. */ -static int randomnessPid = 0; +static pid_t randomnessPid = 0; /* ** Allowed values for the unixFile.ctrlFlags bitmask: @@ -25203,7 +25504,8 @@ static int randomnessPid = 0; #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ -#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings issued */ +#define UNIXFILE_BLOCK 0x0200 /* Next SHM lock might block */ /* ** Include code that is common to all os_*.c files @@ -25241,16 +25543,6 @@ static int randomnessPid = 0; # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -25542,7 +25834,7 @@ static struct unix_syscall { { "read", (sqlite3_syscall_ptr)read, 0 }, #define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) -#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pread", (sqlite3_syscall_ptr)pread, 0 }, #else { "pread", (sqlite3_syscall_ptr)0, 0 }, @@ -25559,7 +25851,7 @@ static struct unix_syscall { { "write", (sqlite3_syscall_ptr)write, 0 }, #define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) -#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS) +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, #else { "pwrite", (sqlite3_syscall_ptr)0, 0 }, @@ -25793,7 +26085,7 @@ static int unixMutexHeld(void) { #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) +#ifdef SQLITE_HAVE_OS_TRACE /* ** Helper function for printing out trace information from debugging ** binaries. This returns the string representation of the supplied @@ -25874,9 +26166,9 @@ static int lockTrace(int fd, int op, struct flock *p){ /* ** Retry ftruncate() calls that fail due to EINTR ** -** All calls to ftruncate() within this file should be made through this wrapper. -** On the Android platform, bypassing the logic below could lead to a corrupt -** database. +** All calls to ftruncate() within this file should be made through +** this wrapper. On the Android platform, bypassing the logic below +** could lead to a corrupt database. */ static int robust_ftruncate(int h, sqlite3_int64 sz){ int rc; @@ -26056,7 +26348,7 @@ static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ assert( zAbsoluteName[0]=='/' ); n = (int)strlen(zAbsoluteName); - pNew = sqlite3_malloc( sizeof(*pNew) + (n+1) ); + pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) ); if( pNew==0 ) return 0; pNew->zCanonicalName = (char*)&pNew[1]; memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); @@ -26336,6 +26628,14 @@ static void robust_close(unixFile *pFile, int h, int lineno){ } /* +** Set the pFile->lastErrno. Do this in a subroutine as that provides +** a convenient place to set a breakpoint. +*/ +static void storeLastErrno(unixFile *pFile, int error){ + pFile->lastErrno = error; +} + +/* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ static void closePendingFds(unixFile *pFile){ @@ -26408,7 +26708,7 @@ static int findInodeInfo( fd = pFile->h; rc = osFstat(fd, &statbuf); if( rc!=0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); #ifdef EOVERFLOW if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; #endif @@ -26429,12 +26729,12 @@ static int findInodeInfo( if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR; } rc = osFstat(fd, &statbuf); if( rc!=0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR; } } @@ -26452,7 +26752,7 @@ static int findInodeInfo( pInode = pInode->pNext; } if( pInode==0 ){ - pInode = sqlite3_malloc( sizeof(*pInode) ); + pInode = sqlite3_malloc64( sizeof(*pInode) ); if( pInode==0 ){ return SQLITE_NOMEM; } @@ -26557,7 +26857,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ lock.l_type = F_WRLCK; if( osFcntl(pFile->h, F_GETLK, &lock) ){ rc = SQLITE_IOERR_CHECKRESERVEDLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } @@ -26690,7 +26990,8 @@ static int unixLock(sqlite3_file *id, int eFileLock){ assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid())); + azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared, + osGetpid(0))); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the end_lock: exit path, as @@ -26757,7 +27058,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_lock; } @@ -26792,7 +27093,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( rc ){ if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_lock; }else{ @@ -26825,7 +27126,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } } @@ -26898,7 +27199,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); + osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ @@ -26932,7 +27233,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ - #if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE (void)handleNFSUnlock; assert( handleNFSUnlock==0 ); @@ -26950,7 +27250,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26962,7 +27262,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26974,7 +27274,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } goto end_unlock; } @@ -26993,7 +27293,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** SQLITE_BUSY would confuse the upper layer (in practice it causes ** an assert to fail). */ rc = SQLITE_IOERR_RDLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); goto end_unlock; } } @@ -27006,7 +27306,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->eFileLock = SHARED_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); goto end_unlock; } } @@ -27024,7 +27324,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->eFileLock = NO_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } @@ -27299,7 +27599,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } return rc; @@ -27326,7 +27626,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27353,7 +27653,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } @@ -27389,10 +27689,9 @@ static int dotlockClose(sqlite3_file *id) { ** still works when you do this, but concurrency is reduced since ** only a single process can be reading the database at a time. ** -** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if -** compiling for VXWORKS. +** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off */ -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE /* ** Retry flock() calls that fail with EINTR @@ -27440,7 +27739,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* unlock failed with an error */ lrc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); rc = lrc; } } @@ -27450,7 +27749,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* someone else might have it reserved */ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(lrc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); rc = lrc; } } @@ -27516,7 +27815,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) { /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } } else { /* got it, set the type and return ok */ @@ -27545,7 +27844,7 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27606,7 +27905,7 @@ static int flockClose(sqlite3_file *id) { ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ -static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { +static int semXCheckReservedLock(sqlite3_file *id, int *pResOut) { int rc = SQLITE_OK; int reserved = 0; unixFile *pFile = (unixFile*)id; @@ -27628,7 +27927,7 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { int tErrno = errno; if( EAGAIN != tErrno ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } else { /* someone else has the lock when we are in NO_LOCK */ reserved = (pFile->eFileLock < SHARED_LOCK); @@ -27673,7 +27972,7 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) { ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ -static int semLock(sqlite3_file *id, int eFileLock) { +static int semXLock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; sem_t *pSem = pFile->pInode->pSem; int rc = SQLITE_OK; @@ -27706,14 +28005,14 @@ static int semLock(sqlite3_file *id, int eFileLock) { ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ -static int semUnlock(sqlite3_file *id, int eFileLock) { +static int semXUnlock(sqlite3_file *id, int eFileLock) { unixFile *pFile = (unixFile*)id; sem_t *pSem = pFile->pInode->pSem; assert( pFile ); assert( pSem ); OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, - pFile->eFileLock, getpid())); + pFile->eFileLock, osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); /* no-op if possible */ @@ -27732,7 +28031,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) { int rc, tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } @@ -27743,10 +28042,10 @@ static int semUnlock(sqlite3_file *id, int eFileLock) { /* ** Close a file. */ -static int semClose(sqlite3_file *id) { +static int semXClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; - semUnlock(id, NO_LOCK); + semXUnlock(id, NO_LOCK); assert( pFile ); unixEnterMutex(); releaseInodeInfo(pFile); @@ -27834,7 +28133,7 @@ static int afpSetLock( setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); #endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; + storeLastErrno(pFile, tErrno); } return rc; } else { @@ -27927,7 +28226,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), - azFileLock(pInode->eFileLock), pInode->nShared , getpid())); + azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0))); /* If there is already a lock of this type or more restrictive on the ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as @@ -28017,7 +28316,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); if( IS_LOCK_ERROR(lrc1) ) { - pFile->lastErrno = lrc1Errno; + storeLastErrno(pFile, lrc1Errno); rc = lrc1; goto afp_end_lock; } else if( IS_LOCK_ERROR(lrc2) ){ @@ -28113,7 +28412,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, - getpid())); + osGetpid(0))); assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ @@ -28304,9 +28603,9 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); }else{ - ((unixFile*)id)->lastErrno = 0; + storeLastErrno((unixFile*)id, 0); } return -1; } @@ -28316,7 +28615,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ if( got<0 ){ if( errno==EINTR ){ got = 1; continue; } prior = 0; - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); break; }else if( got>0 ){ cnt -= got; @@ -28381,7 +28680,7 @@ static int unixRead( /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; }else{ - pFile->lastErrno = 0; /* not a system error */ + storeLastErrno(pFile, 0); /* not a system error */ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; @@ -28410,9 +28709,9 @@ static int seekAndWriteFd( TIMER_START; #if defined(USE_PREAD) - do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); + do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); #elif defined(USE_PREAD64) - do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); + do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); #else do{ i64 iSeek = lseek(fd, iOff, SEEK_SET); @@ -28522,7 +28821,7 @@ static int unixWrite( /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ - pFile->lastErrno = 0; /* not a system error */ + storeLastErrno(pFile, 0); /* not a system error */ return SQLITE_FULL; } } @@ -28731,7 +29030,7 @@ static int unixSync(sqlite3_file *id, int flags){ rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } @@ -28775,7 +29074,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ rc = robust_ftruncate(pFile->h, nByte); if( rc ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); }else{ #ifdef SQLITE_DEBUG @@ -28815,7 +29114,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ - ((unixFile*)id)->lastErrno = errno; + storeLastErrno((unixFile*)id, errno); return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; @@ -28851,7 +29150,9 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ - if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + if( osFstat(pFile->h, &buf) ){ + return SQLITE_IOERR_FSTAT; + } nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ @@ -28898,7 +29199,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ int rc; if( pFile->szChunk<=0 ){ if( robust_ftruncate(pFile->h, nByte) ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } } @@ -28936,11 +29237,15 @@ static int unixGetTempname(int nBuf, char *zBuf); static int unixFileControl(sqlite3_file *id, int op, void *pArg){ unixFile *pFile = (unixFile*)id; switch( op ){ + case SQLITE_FCNTL_WAL_BLOCK: { + /* pFile->ctrlFlags |= UNIXFILE_BLOCK; // Deferred feature */ + return SQLITE_OK; + } case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->eFileLock; return SQLITE_OK; } - case SQLITE_LAST_ERRNO: { + case SQLITE_FCNTL_LAST_ERRNO: { *(int*)pArg = pFile->lastErrno; return SQLITE_OK; } @@ -28968,7 +29273,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ return SQLITE_OK; } case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname ); + char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname ); if( zTFile ){ unixGetTempname(pFile->pVfs->mxPathname, zTFile); *(char**)pArg = zTFile; @@ -29009,8 +29314,8 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) - case SQLITE_SET_LOCKPROXYFILE: - case SQLITE_GET_LOCKPROXYFILE: { + case SQLITE_FCNTL_SET_LOCKPROXYFILE: + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ @@ -29150,7 +29455,9 @@ static int unixDeviceCharacteristics(sqlite3_file *id){ ** Instead, it should be called via macro osGetpagesize(). */ static int unixGetpagesize(void){ -#if defined(_BSD_SOURCE) +#if OS_VXWORKS + return 1024; +#elif defined(_BSD_SOURCE) return getpagesize(); #else return (int)sysconf(_SC_PAGESIZE); @@ -29243,15 +29550,17 @@ struct unixShm { ** otherwise. */ static int unixShmSystemLock( - unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */ + unixFile *pFile, /* Open connection to the WAL file */ int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ int ofst, /* First byte of the locking range */ int n /* Number of bytes to lock */ ){ - struct flock f; /* The posix advisory locking structure */ - int rc = SQLITE_OK; /* Result code form fcntl() */ + unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ + struct flock f; /* The posix advisory locking structure */ + int rc = SQLITE_OK; /* Result code form fcntl() */ /* Access to the unixShmNode object is serialized by the caller */ + pShmNode = pFile->pInode->pShmNode; assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 ); /* Shared locks never span more than one byte */ @@ -29261,6 +29570,7 @@ static int unixShmSystemLock( assert( n>=1 && n<SQLITE_SHM_NLOCK ); if( pShmNode->h>=0 ){ + int lkType; /* Initialize the locking parameters */ memset(&f, 0, sizeof(f)); f.l_type = lockType; @@ -29268,8 +29578,10 @@ static int unixShmSystemLock( f.l_start = ofst; f.l_len = n; - rc = osFcntl(pShmNode->h, F_SETLK, &f); + lkType = (pFile->ctrlFlags & UNIXFILE_BLOCK)!=0 ? F_SETLKW : F_SETLK; + rc = osFcntl(pShmNode->h, lkType, &f); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + pFile->ctrlFlags &= ~UNIXFILE_BLOCK; } /* Update the global lock state and do debug tracing */ @@ -29402,7 +29714,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ int nShmFilename; /* Size of the SHM filename in bytes */ /* Allocate space for the new unixShm object. */ - p = sqlite3_malloc( sizeof(*p) ); + p = sqlite3_malloc64( sizeof(*p) ); if( p==0 ) return SQLITE_NOMEM; memset(p, 0, sizeof(*p)); assert( pDbFd->pShm==0 ); @@ -29415,6 +29727,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ pShmNode = pInode->pShmNode; if( pShmNode==0 ){ struct stat sStat; /* fstat() info for database file */ +#ifndef SQLITE_SHM_DIRECTORY + const char *zBasePath = pDbFd->zPath; +#endif /* Call fstat() to figure out the permissions on the database file. If ** a new *-shm file is created, an attempt will be made to create it @@ -29428,9 +29743,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ #ifdef SQLITE_SHM_DIRECTORY nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; #else - nShmFilename = 6 + (int)strlen(pDbFd->zPath); + nShmFilename = 6 + (int)strlen(zBasePath); #endif - pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename ); + pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); if( pShmNode==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; @@ -29442,7 +29757,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", (u32)sStat.st_ino, (u32)sStat.st_dev); #else - sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); + sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath); sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); #endif pShmNode->h = -1; @@ -29476,13 +29791,13 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** If not, truncate the file to zero length. */ rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( robust_ftruncate(pShmNode->h, 0) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); } } if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); } if( rc ) goto shm_open_err; } @@ -29640,7 +29955,7 @@ static int unixShmMap( goto shmpage_out; } }else{ - pMem = sqlite3_malloc(szRegion); + pMem = sqlite3_malloc64(szRegion); if( pMem==0 ){ rc = SQLITE_NOMEM; goto shmpage_out; @@ -29714,7 +30029,7 @@ static int unixShmLock( /* Unlock the system-level locks */ if( (mask & allMask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -29742,7 +30057,7 @@ static int unixShmLock( /* Get shared locks at the system level, if necessary */ if( rc==SQLITE_OK ){ if( (allShared & mask)==0 ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); }else{ rc = SQLITE_OK; } @@ -29767,7 +30082,7 @@ static int unixShmLock( ** also mark the local connection as being locked. */ if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n); + rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; @@ -29776,7 +30091,7 @@ static int unixShmLock( } sqlite3_mutex_leave(pShmNode->mutex); OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", - p->id, getpid(), p->sharedMask, p->exclMask)); + p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; } @@ -29835,7 +30150,9 @@ static int unixShmUnmap( assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename); + if( deleteFlag && pShmNode->h>=0 ){ + osUnlink(pShmNode->zFilename); + } unixShmPurge(pDbFd); } unixLeaveMutex(); @@ -30112,7 +30429,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ ** * An I/O method finder function called FINDER that returns a pointer ** to the METHOD object in the previous bullet. */ -#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK, SHMMAP) \ +#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \ static const sqlite3_io_methods METHOD = { \ VERSION, /* iVersion */ \ CLOSE, /* xClose */ \ @@ -30177,7 +30494,7 @@ IOMETHODS( 0 /* xShmMap method */ ) -#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS +#if SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( flockIoFinder, /* Finder function name */ flockIoMethods, /* sqlite3_io_methods object name */ @@ -30195,10 +30512,10 @@ IOMETHODS( semIoFinder, /* Finder function name */ semIoMethods, /* sqlite3_io_methods object name */ 1, /* shared memory is disabled */ - semClose, /* xClose method */ - semLock, /* xLock method */ - semUnlock, /* xUnlock method */ - semCheckReservedLock, /* xCheckReservedLock method */ + semXClose, /* xClose method */ + semXLock, /* xLock method */ + semXUnlock, /* xUnlock method */ + semXCheckReservedLock, /* xCheckReservedLock method */ 0 /* xShmMap method */ ) #endif @@ -30322,15 +30639,13 @@ static const sqlite3_io_methods #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ -#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE -/* -** This "finder" function attempts to determine the best locking strategy -** for the database file "filePath". It then returns the sqlite3_io_methods -** object that implements that strategy. -** -** This is for VXWorks only. +#if OS_VXWORKS +/* +** This "finder" function for VxWorks checks to see if posix advisory +** locking works. If it does, then that is what is used. If it does not +** work, then fallback to named semaphore locking. */ -static const sqlite3_io_methods *autolockIoFinderImpl( +static const sqlite3_io_methods *vxworksIoFinderImpl( const char *filePath, /* name of the database file */ unixFile *pNew /* the open file object */ ){ @@ -30356,9 +30671,9 @@ static const sqlite3_io_methods *autolockIoFinderImpl( } } static const sqlite3_io_methods - *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; + *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl; -#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ +#endif /* OS_VXWORKS */ /* ** An abstract type for a pointer to an IO method finder function: @@ -30477,7 +30792,7 @@ static int fillInUnixFile( ** the afpLockingContext. */ afpLockingContext *pCtx; - pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) ); + pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ rc = SQLITE_NOMEM; }else{ @@ -30507,7 +30822,7 @@ static int fillInUnixFile( int nFilename; assert( zFilename!=0 ); nFilename = (int)strlen(zFilename) + 6; - zLockFile = (char *)sqlite3_malloc(nFilename); + zLockFile = (char *)sqlite3_malloc64(nFilename); if( zLockFile==0 ){ rc = SQLITE_NOMEM; }else{ @@ -30540,7 +30855,7 @@ static int fillInUnixFile( } #endif - pNew->lastErrno = 0; + storeLastErrno(pNew, 0); #if OS_VXWORKS if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); @@ -30871,8 +31186,8 @@ static int unixOpen( ** the same instant might all reset the PRNG. But multiple resets ** are harmless. */ - if( randomnessPid!=getpid() ){ - randomnessPid = getpid(); + if( randomnessPid!=osGetpid(0) ){ + randomnessPid = osGetpid(0); sqlite3_randomness(0,0); } @@ -30884,7 +31199,7 @@ static int unixOpen( if( pUnused ){ fd = pUnused->fd; }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); + pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } @@ -30988,13 +31303,16 @@ static int unixOpen( #if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE if( fstatfs(fd, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; + storeLastErrno(p, errno); robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; } + if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { + ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; + } #endif /* Set up appropriate ctrlFlags */ @@ -31017,19 +31335,6 @@ static int unixOpen( if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ - if( statfs(zPath, &fsInfo) == -1 ){ - /* In theory, the close(fd) call is sub-optimal. If the file opened - ** with fd is a database file, and there are other connections open - ** on that file that are currently holding advisory locks on it, - ** then the call to close() will cancel those locks. In practice, - ** we're assuming that statfs() doesn't fail very often. At least - ** not while other file descriptors opened by the same process on - ** the same file are working. */ - p->lastErrno = errno; - robust_close(p, fd, __LINE__); - rc = SQLITE_IOERR_ACCESS; - goto open_finished; - } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ @@ -31273,8 +31578,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ ** tests repeatable. */ memset(zBuf, 0, nBuf); - randomnessPid = getpid(); -#if !defined(SQLITE_TEST) + randomnessPid = osGetpid(0); +#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) { int fd, got; fd = robust_open("/dev/urandom", O_RDONLY, 0); @@ -31455,9 +31760,10 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ ** ** C APIs ** -** sqlite3_file_control(db, dbname, SQLITE_SET_LOCKPROXYFILE, +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE, ** <proxy_path> | ":auto:"); -** sqlite3_file_control(db, dbname, SQLITE_GET_LOCKPROXYFILE, &<proxy_path>); +** sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE, +** &<proxy_path>); ** ** ** SQL pragmas @@ -31550,7 +31856,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ ** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will ** force proxy locking to be used for every database file opened, and 0 ** will force automatic proxy locking to be disabled for all database -** files (explicitly calling the SQLITE_SET_LOCKPROXYFILE pragma or +** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or ** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). */ @@ -31571,6 +31877,7 @@ struct proxyLockingContext { char *lockProxyPath; /* Name of the proxy lock file */ char *dbPath; /* Name of the open file */ int conchHeld; /* 1 if the conch is held, -1 if lockless */ + int nFails; /* Number of conch taking failures */ void *oldLockingContext; /* Original lockingcontext to restore on close */ sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ }; @@ -31592,7 +31899,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ { if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", - lPath, errno, getpid())); + lPath, errno, osGetpid(0))); return SQLITE_IOERR_LOCK; } len = strlcat(lPath, "sqliteplocks", maxLen); @@ -31614,7 +31921,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ } lPath[i+len]='\0'; strlcat(lPath, ":auto:", maxLen); - OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, getpid())); + OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0))); return SQLITE_OK; } @@ -31641,7 +31948,7 @@ static int proxyCreateLockPath(const char *lockPath){ if( err!=EEXIST ) { OSTRACE(("CREATELOCKPATH FAILED creating %s, " "'%s' proxy lock path=%s pid=%d\n", - buf, strerror(err), lockPath, getpid())); + buf, strerror(err), lockPath, osGetpid(0))); return err; } } @@ -31650,7 +31957,7 @@ static int proxyCreateLockPath(const char *lockPath){ } buf[i] = lockPath[i]; } - OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid())); + OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, osGetpid(0))); return 0; } @@ -31684,7 +31991,7 @@ static int proxyCreateUnixFile( if( pUnused ){ fd = pUnused->fd; }else{ - pUnused = sqlite3_malloc(sizeof(*pUnused)); + pUnused = sqlite3_malloc64(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } @@ -31717,7 +32024,7 @@ static int proxyCreateUnixFile( } } - pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew)); + pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew)); if( pNew==NULL ){ rc = SQLITE_NOMEM; goto end_create_proxy; @@ -31750,8 +32057,10 @@ SQLITE_API int sqlite3_hostid_num = 0; #define PROXY_HOSTIDLEN 16 /* conch file host id length */ +#ifdef HAVE_GETHOSTUUID /* Not always defined in the headers as it ought to be */ extern int gethostuuid(uuid_t id, const struct timespec *wait); +#endif /* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN ** bytes of writable memory. @@ -31759,10 +32068,9 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait); static int proxyGetHostID(unsigned char *pHostID, int *pError){ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); -#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ - && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 +#ifdef HAVE_GETHOSTUUID { - static const struct timespec timeout = {1, 0}; /* 1 sec timeout */ + struct timespec timeout = {1, 0}; /* 1 sec timeout */ if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ @@ -31877,7 +32185,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ */ struct stat buf; if( osFstat(conchFile->h, &buf) ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR_LOCK; } @@ -31897,7 +32205,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ char tBuf[PROXY_MAXCONCHLEN]; int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ - pFile->lastErrno = errno; + storeLastErrno(pFile, errno); return SQLITE_IOERR_LOCK; } if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ @@ -31917,7 +32225,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ if( 0==proxyBreakConchLock(pFile, myHostID) ){ rc = SQLITE_OK; if( lockType==EXCLUSIVE_LOCK ){ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); + rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); } if( !rc ){ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); @@ -31955,11 +32263,12 @@ static int proxyTakeConch(unixFile *pFile){ int forceNewLockPath = 0; OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, - (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid())); + (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), + osGetpid(0))); rc = proxyGetHostID(myHostID, &pError); if( (rc&0xff)==SQLITE_IOERR ){ - pFile->lastErrno = pError; + storeLastErrno(pFile, pError); goto end_takeconch; } rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); @@ -31970,7 +32279,7 @@ static int proxyTakeConch(unixFile *pFile){ readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); if( readLen<0 ){ /* I/O error: lastErrno set by seekAndRead */ - pFile->lastErrno = conchFile->lastErrno; + storeLastErrno(pFile, conchFile->lastErrno); rc = SQLITE_IOERR_READ; goto end_takeconch; }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || @@ -32043,7 +32352,7 @@ static int proxyTakeConch(unixFile *pFile){ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } }else{ - rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK); + rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); } if( rc==SQLITE_OK ){ char writeBuffer[PROXY_MAXCONCHLEN]; @@ -32052,7 +32361,8 @@ static int proxyTakeConch(unixFile *pFile){ writeBuffer[0] = (char)PROXY_CONCHVERSION; memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); if( pCtx->lockProxyPath!=NULL ){ - strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN); + strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, + MAXPATHLEN); }else{ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } @@ -32164,7 +32474,7 @@ static int proxyReleaseConch(unixFile *pFile){ conchFile = pCtx->conchFile; OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), - getpid())); + osGetpid(0))); if( pCtx->conchHeld>0 ){ rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); } @@ -32176,7 +32486,7 @@ static int proxyReleaseConch(unixFile *pFile){ /* ** Given the name of a database file, compute the name of its conch file. -** Store the conch filename in memory obtained from sqlite3_malloc(). +** Store the conch filename in memory obtained from sqlite3_malloc64(). ** Make *pConchPath point to the new name. Return SQLITE_OK on success ** or SQLITE_NOMEM if unable to obtain memory. ** @@ -32192,7 +32502,7 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ /* Allocate space for the conch filename and initialize the name to ** the name of the original database file. */ - *pConchPath = conchPath = (char *)sqlite3_malloc(len + 8); + *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8); if( conchPath==0 ){ return SQLITE_NOMEM; } @@ -32264,7 +32574,8 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ /* afp style keeps a reference to the db path in the filePath field ** of the struct */ assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); - strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN); + strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, + MAXPATHLEN); } else #endif if( pFile->pMethod == &dotlockIoMethods ){ @@ -32305,9 +32616,9 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { } OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, - (lockPath ? lockPath : ":auto:"), getpid())); + (lockPath ? lockPath : ":auto:"), osGetpid(0))); - pCtx = sqlite3_malloc( sizeof(*pCtx) ); + pCtx = sqlite3_malloc64( sizeof(*pCtx) ); if( pCtx==0 ){ return SQLITE_NOMEM; } @@ -32377,7 +32688,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { */ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ - case SQLITE_GET_LOCKPROXYFILE: { + case SQLITE_FCNTL_GET_LOCKPROXYFILE: { unixFile *pFile = (unixFile*)id; if( pFile->pMethod == &proxyIoMethods ){ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; @@ -32392,13 +32703,16 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } - case SQLITE_SET_LOCKPROXYFILE: { + case SQLITE_FCNTL_SET_LOCKPROXYFILE: { unixFile *pFile = (unixFile*)id; int rc = SQLITE_OK; int isProxyStyle = (pFile->pMethod == &proxyIoMethods); if( pArg==NULL || (const char *)pArg==0 ){ if( isProxyStyle ){ - /* turn off proxy locking - not supported */ + /* turn off proxy locking - not supported. If support is added for + ** switching proxy locking mode off then it will need to fail if + ** the journal mode is WAL mode. + */ rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; }else{ /* turn off proxy locking - already off - NOOP */ @@ -32589,7 +32903,7 @@ static int proxyClose(sqlite3_file *id) { ** necessarily been initialized when this routine is called, and so they ** should not be used. */ -SQLITE_API int sqlite3_os_init(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ /* ** The following macro defines an initializer for an sqlite3_vfs object. ** The name of the VFS is NAME. The pAppData is a pointer to a pointer @@ -32643,8 +32957,10 @@ SQLITE_API int sqlite3_os_init(void){ ** array cannot be const. */ static sqlite3_vfs aVfs[] = { -#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__)) +#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) UNIXVFS("unix", autolockIoFinder ), +#elif OS_VXWORKS + UNIXVFS("unix", vxworksIoFinder ), #else UNIXVFS("unix", posixIoFinder ), #endif @@ -32654,11 +32970,11 @@ SQLITE_API int sqlite3_os_init(void){ #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif -#if SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS UNIXVFS("unix-posix", posixIoFinder ), -#if !OS_VXWORKS - UNIXVFS("unix-flock", flockIoFinder ), #endif +#if SQLITE_ENABLE_LOCKING_STYLE + UNIXVFS("unix-flock", flockIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) UNIXVFS("unix-afp", afpIoFinder ), @@ -32686,7 +33002,7 @@ SQLITE_API int sqlite3_os_init(void){ ** to release dynamically allocated objects. But not on unix. ** This routine is a no-op for unix. */ -SQLITE_API int sqlite3_os_end(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ return SQLITE_OK; } @@ -32746,16 +33062,6 @@ SQLITE_API int sqlite3_os_end(void){ # error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." #endif -#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) -# ifndef SQLITE_DEBUG_OS_TRACE -# define SQLITE_DEBUG_OS_TRACE 0 -# endif - int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; -# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X -#else -# define OSTRACE(X) -#endif - /* ** Macros for performance tracing. Normally turned off. Only works ** on i486 hardware. @@ -33099,8 +33405,10 @@ WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); #endif /* SQLITE_OS_WINRT */ /* -** This file mapping API is common to both Win32 and WinRT. +** These file mapping APIs are common to both Win32 and WinRT. */ + +WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); #endif /* SQLITE_WIN32_FILEMAPPING_API */ @@ -33968,6 +34276,32 @@ static struct win_syscall { SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) #endif /* defined(InterlockedCompareExchange) */ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreate", (SYSCALL)UuidCreate, 0 }, +#else + { "UuidCreate", (SYSCALL)0, 0 }, +#endif + +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) + +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, +#else + { "UuidCreateSequential", (SYSCALL)0, 0 }, +#endif + +#define osUuidCreateSequential \ + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) + +#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 + { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, +#else + { "FlushViewOfFile", (SYSCALL)0, 0 }, +#endif + +#define osFlushViewOfFile \ + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) + }; /* End of the overrideable system calls */ /* @@ -34061,7 +34395,7 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){ ** "pnLargest" argument, if non-zero, will be used to return the size of the ** largest committed free block in the heap, in bytes. */ -SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_compact_heap(LPUINT pnLargest){ int rc = SQLITE_OK; UINT nLargest = 0; HANDLE hHeap; @@ -34101,7 +34435,7 @@ SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ ** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will ** be returned and no changes will be made to the Win32 native heap. */ -SQLITE_API int sqlite3_win32_reset_heap(){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_reset_heap(){ int rc; MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ @@ -34146,7 +34480,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){ ** (if available). */ -SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_write_debug(const char *zBuf, int nBuf){ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ @@ -34186,7 +34520,7 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ static HANDLE sleepObj = NULL; #endif -SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){ +SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds){ #if SQLITE_OS_WINRT if ( sleepObj==NULL ){ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, @@ -34235,7 +34569,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ ** This function determines if the machine is running a version of Windows ** based on the NT kernel. */ -SQLITE_API int sqlite3_win32_is_nt(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void){ #if SQLITE_OS_WINRT /* ** NOTE: The WinRT sub-platform is always assumed to be based on the NT @@ -34589,7 +34923,7 @@ static char *winUnicodeToMbcs(LPCWSTR zWideFilename){ ** Convert multibyte character string to UTF-8. Space to hold the ** returned string is obtained from sqlite3_malloc(). */ -SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ +SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8(const char *zFilename){ char *zFilenameUtf8; LPWSTR zTmpWide; @@ -34606,7 +34940,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ ** Convert UTF-8 to multibyte character string. Space to hold the ** returned string is obtained from sqlite3_malloc(). */ -SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ +SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs(const char *zFilename){ char *zFilenameMbcs; LPWSTR zTmpWide; @@ -34626,7 +34960,7 @@ SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ ** argument is the name of the directory to use. The return value will be ** SQLITE_OK if successful. */ -SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ char **ppDirectory = 0; #ifndef SQLITE_OMIT_AUTOINIT int rc = sqlite3_initialize(); @@ -34851,11 +35185,11 @@ static int winRetryIoerr(int *pnRetry, DWORD *pError){ /* ** Log a I/O error retry episode. */ -static void winLogIoerr(int nRetry){ +static void winLogIoerr(int nRetry, int lineno){ if( nRetry ){ - sqlite3_log(SQLITE_IOERR, - "delayed %dms for lock/sharing conflict", - winIoerrRetryDelay*nRetry*(nRetry+1)/2 + sqlite3_log(SQLITE_NOTICE, + "delayed %dms for lock/sharing conflict at line %d", + winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno ); } } @@ -35335,7 +35669,8 @@ static int winClose(sqlite3_file *id){ assert( pFile->pShm==0 ); #endif assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); - OSTRACE(("CLOSE file=%p\n", pFile->h)); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n", + osGetCurrentProcessId(), pFile, pFile->h)); #if SQLITE_MAX_MMAP_SIZE>0 winUnmapfile(pFile); @@ -35364,7 +35699,8 @@ static int winClose(sqlite3_file *id){ pFile->h = NULL; } OpenCounter(-1); - OSTRACE(("CLOSE file=%p, rc=%s\n", pFile->h, rc ? "ok" : "failed")); + OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed")); return rc ? SQLITE_OK : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), "winClose", pFile->zPath); @@ -35392,7 +35728,8 @@ static int winRead( assert( amt>0 ); assert( offset>=0 ); SimulateIOError(return SQLITE_IOERR_READ); - OSTRACE(("READ file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, pFile->h, pBuf, amt, offset, pFile->locktype)); #if SQLITE_MAX_MMAP_SIZE>0 @@ -35401,7 +35738,8 @@ static int winRead( if( offset<pFile->mmapSize ){ if( offset+amt <= pFile->mmapSize ){ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); - OSTRACE(("READ-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ int nCopy = (int)(pFile->mmapSize - offset); @@ -35415,7 +35753,8 @@ static int winRead( #if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) if( winSeekFile(pFile, offset) ){ - OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_FULL; } while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ @@ -35429,19 +35768,22 @@ static int winRead( DWORD lastErrno; if( winRetryIoerr(&nRetry, &lastErrno) ) continue; pFile->lastErrno = lastErrno; - OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, "winRead", pFile->zPath); } - winLogIoerr(nRetry); + winLogIoerr(nRetry, __LINE__); if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); - OSTRACE(("READ file=%p, rc=SQLITE_IOERR_SHORT_READ\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_IOERR_SHORT_READ; } - OSTRACE(("READ file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; } @@ -35464,7 +35806,8 @@ static int winWrite( SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); - OSTRACE(("WRITE file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " + "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, pFile->h, pBuf, amt, offset, pFile->locktype)); #if SQLITE_MAX_MMAP_SIZE>0 @@ -35473,7 +35816,8 @@ static int winWrite( if( offset<pFile->mmapSize ){ if( offset+amt <= pFile->mmapSize ){ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); - OSTRACE(("WRITE-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ int nCopy = (int)(pFile->mmapSize - offset); @@ -35536,17 +35880,20 @@ static int winWrite( if( rc ){ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) || ( pFile->lastErrno==ERROR_DISK_FULL )){ - OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_FULL, pFile->lastErrno, "winWrite1", pFile->zPath); } - OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, "winWrite2", pFile->zPath); }else{ - winLogIoerr(nRetry); + winLogIoerr(nRetry, __LINE__); } - OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; } @@ -35560,8 +35907,8 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ assert( pFile ); SimulateIOError(return SQLITE_IOERR_TRUNCATE); - OSTRACE(("TRUNCATE file=%p, size=%lld, lock=%d\n", - pFile->h, nByte, pFile->locktype)); + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype)); /* If the user has configured a chunk-size for this file, truncate the ** file so that it consists of an integer number of chunks (i.e. the @@ -35593,7 +35940,8 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ } #endif - OSTRACE(("TRUNCATE file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); + OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n", + osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc))); return rc; } @@ -35617,7 +35965,7 @@ static int winSync(sqlite3_file *id, int flags){ BOOL rc; #endif #if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ - (defined(SQLITE_TEST) && defined(SQLITE_DEBUG)) + defined(SQLITE_HAVE_OS_TRACE) /* ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or ** OSTRACE() macros. @@ -35638,8 +35986,9 @@ static int winSync(sqlite3_file *id, int flags){ */ SimulateDiskfullError( return SQLITE_FULL ); - OSTRACE(("SYNC file=%p, flags=%x, lock=%d\n", - pFile->h, flags, pFile->locktype)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n", + osGetCurrentProcessId(), pFile, pFile->h, flags, + pFile->locktype)); #ifndef SQLITE_TEST UNUSED_PARAMETER(flags); @@ -35654,19 +36003,38 @@ static int winSync(sqlite3_file *id, int flags){ ** no-op */ #ifdef SQLITE_NO_SYNC - OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; #else +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFile->pMapRegion ){ + if( osFlushViewOfFile(pFile->pMapRegion, 0) ){ + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_OK\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + }else{ + pFile->lastErrno = osGetLastError(); + OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), + pFile, pFile->pMapRegion)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winSync1", pFile->zPath); + } + } +#endif rc = osFlushFileBuffers(pFile->h); SimulateIOError( rc=FALSE ); if( rc ){ - OSTRACE(("SYNC file=%p, rc=SQLITE_OK\n", pFile->h)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile, pFile->h)); return SQLITE_OK; }else{ pFile->lastErrno = osGetLastError(); - OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h)); + OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n", + osGetCurrentProcessId(), pFile, pFile->h)); return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, - "winSync", pFile->zPath); + "winSync2", pFile->zPath); } #endif } @@ -36274,7 +36642,7 @@ struct winShmNode { int nRef; /* Number of winShm objects pointing to this */ winShm *pFirst; /* All winShm objects pointing to this */ winShmNode *pNext; /* Next in list of all winShmNode objects */ -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) u8 nextShmId; /* Next available winShm.id value */ #endif }; @@ -36305,7 +36673,7 @@ struct winShm { u8 hasMutex; /* True if holding the winShmNode mutex */ u16 sharedMask; /* Mask of shared locks held */ u16 exclMask; /* Mask of exclusive locks held */ -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) u8 id; /* Id of this connection with its winShmNode */ #endif }; @@ -36496,7 +36864,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ /* Make the new connection a child of the winShmNode */ p->pShmNode = pShmNode; -#ifdef SQLITE_DEBUG +#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) p->id = pShmNode->nextShmId++; #endif pShmNode->nRef++; @@ -36716,16 +37084,16 @@ static int winShmMap( void volatile **pp /* OUT: Mapped memory */ ){ winFile *pDbFd = (winFile*)fd; - winShm *p = pDbFd->pShm; + winShm *pShm = pDbFd->pShm; winShmNode *pShmNode; int rc = SQLITE_OK; - if( !p ){ + if( !pShm ){ rc = winOpenSharedMemory(pDbFd); if( rc!=SQLITE_OK ) return rc; - p = pDbFd->pShm; + pShm = pDbFd->pShm; } - pShmNode = p->pShmNode; + pShmNode = pShm->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); @@ -36765,7 +37133,7 @@ static int winShmMap( } /* Map the requested memory region into this processes address space. */ - apNew = (struct ShmRegion *)sqlite3_realloc( + apNew = (struct ShmRegion *)sqlite3_realloc64( pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) ); if( !apNew ){ @@ -37637,7 +38005,7 @@ static int winOpen( } } #endif - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); @@ -37821,7 +38189,7 @@ static int winDelete( if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); }else{ - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); } sqlite3_free(zConverted); OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc))); @@ -37871,7 +38239,7 @@ static int winAccess( attr = sAttrData.dwFileAttributes; } }else{ - winLogIoerr(cnt); + winLogIoerr(cnt, __LINE__); if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ sqlite3_free(zConverted); return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", @@ -38212,7 +38580,7 @@ static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ int n = 0; UNUSED_PARAMETER(pVfs); -#if defined(SQLITE_TEST) +#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) n = nBuf; memset(zBuf, 0, nBuf); #else @@ -38246,7 +38614,23 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ memcpy(&zBuf[n], &i, sizeof(i)); n += sizeof(i); } +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreate(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } + if( sizeof(UUID)<=nBuf-n ){ + UUID id; + memset(&id, 0, sizeof(UUID)); + osUuidCreateSequential(&id); + memcpy(zBuf, &id, sizeof(UUID)); + n += sizeof(UUID); + } #endif +#endif /* defined(SQLITE_TEST) || defined(SQLITE_ZERO_PRNG_SEED) */ return n; } @@ -38370,7 +38754,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ /* ** Initialize and deinitialize the operating system interface. */ -SQLITE_API int sqlite3_os_init(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ static sqlite3_vfs winVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ @@ -38424,7 +38808,7 @@ SQLITE_API int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==77 ); + assert( ArraySize(aSyscall)==80 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); @@ -38445,7 +38829,7 @@ SQLITE_API int sqlite3_os_init(void){ return SQLITE_OK; } -SQLITE_API int sqlite3_os_end(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ #if SQLITE_OS_WINRT if( sleepObj!=NULL ){ osCloseHandle(sleepObj); @@ -38801,7 +39185,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ ** bits to act as the reference */ pBitvec = sqlite3BitvecCreate( sz ); pV = sqlite3MallocZero( (sz+7)/8 + 1 ); - pTmpSpace = sqlite3_malloc(BITVEC_SZ); + pTmpSpace = sqlite3_malloc64(BITVEC_SZ); if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; /* NULL pBitvec tests */ @@ -38983,12 +39367,20 @@ static void pcacheUnpin(PgHdr *p){ } /* -** Compute the number of pages of cache requested. +** Compute the number of pages of cache requested. p->szCache is the +** cache size requested by the "PRAGMA cache_size" statement. +** +** */ static int numberOfCachePages(PCache *p){ if( p->szCache>=0 ){ + /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the + ** suggested cache size is set to N. */ return p->szCache; }else{ + /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then + ** the number of cache pages is adjusted to use approximately abs(N*1024) + ** bytes of memory. */ return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); } } @@ -39728,7 +40120,6 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ static void *pcache1Alloc(int nByte){ void *p = 0; assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); - sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot ){ sqlite3_mutex_enter(pcache1.mutex); p = (PgHdr1 *)pcache1.pFree; @@ -39737,7 +40128,8 @@ static void *pcache1Alloc(int nByte){ pcache1.nFreeSlot--; pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; assert( pcache1.nFreeSlot>=0 ); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); + sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); } sqlite3_mutex_leave(pcache1.mutex); } @@ -39750,7 +40142,8 @@ static void *pcache1Alloc(int nByte){ if( p ){ int sz = sqlite3MallocSize(p); sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); + sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); + sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); sqlite3_mutex_leave(pcache1.mutex); } #endif @@ -39768,7 +40161,7 @@ static int pcache1Free(void *p){ if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); + sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; @@ -39782,7 +40175,7 @@ static int pcache1Free(void *p){ nFreed = sqlite3MallocSize(p); #ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS sqlite3_mutex_enter(pcache1.mutex); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed); + sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); sqlite3_mutex_leave(pcache1.mutex); #endif sqlite3_free(p); @@ -40519,6 +40912,14 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ */ SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } +/* +** Return the global mutex used by this PCACHE implementation. The +** sqlite3_status() routine needs access to this mutex. +*/ +SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void){ + return pcache1.mutex; +} + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** This function is called to free superfluous dynamically allocated memory @@ -44273,9 +44674,7 @@ static int pagerWalFrames( ){ int rc; /* Return code */ int nList; /* Number of pages in pList */ -#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) PgHdr *p; /* For looping over pages */ -#endif assert( pPager->pWal ); assert( pList ); @@ -44292,7 +44691,6 @@ static int pagerWalFrames( ** any pages with page numbers greater than nTruncate into the WAL file. ** They will never be read by any client. So remove them from the pDirty ** list here. */ - PgHdr *p; PgHdr **ppNext = &pList; nList = 0; for(p=pList; (*ppNext = p)!=0; p=p->pDirty){ @@ -44312,7 +44710,6 @@ static int pagerWalFrames( pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags ); if( rc==SQLITE_OK && pPager->pBackup ){ - PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } @@ -48243,6 +48640,8 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ } assert( state==pPager->eState ); } + }else if( eMode==PAGER_JOURNALMODE_OFF ){ + sqlite3OsClose(pPager->jfd); } } @@ -49025,7 +49424,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ if( pWal->nWiData<=iPage ){ int nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; - apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte); + apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; return SQLITE_NOMEM; @@ -49291,9 +49690,10 @@ static void walUnlockShared(Wal *pWal, int lockIdx){ SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED); WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx))); } -static int walLockExclusive(Wal *pWal, int lockIdx, int n){ +static int walLockExclusive(Wal *pWal, int lockIdx, int n, int fBlock){ int rc; if( pWal->exclusiveMode ) return SQLITE_OK; + if( fBlock ) sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_WAL_BLOCK, 0); rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n, SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE); WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal, @@ -49579,7 +49979,7 @@ static int walIndexRecover(Wal *pWal){ assert( pWal->writeLock ); iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; nLock = SQLITE_SHM_NLOCK - iLock; - rc = walLockExclusive(pWal, iLock, nLock); + rc = walLockExclusive(pWal, iLock, nLock, 0); if( rc ){ return rc; } @@ -49649,7 +50049,7 @@ static int walIndexRecover(Wal *pWal){ /* Malloc a buffer to read frames into. */ szFrame = szPage + WAL_FRAME_HDRSIZE; - aFrame = (u8 *)sqlite3_malloc(szFrame); + aFrame = (u8 *)sqlite3_malloc64(szFrame); if( !aFrame ){ rc = SQLITE_NOMEM; goto recovery_error; @@ -50042,7 +50442,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ nByte = sizeof(WalIterator) + (nSegment-1)*sizeof(struct WalSegment) + iLast*sizeof(ht_slot); - p = (WalIterator *)sqlite3_malloc(nByte); + p = (WalIterator *)sqlite3_malloc64(nByte); if( !p ){ return SQLITE_NOMEM; } @@ -50052,7 +50452,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ /* Allocate temporary space used by the merge-sort routine. This block ** of memory will be freed before this function returns. */ - aTmp = (ht_slot *)sqlite3_malloc( + aTmp = (ht_slot *)sqlite3_malloc64( sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) ); if( !aTmp ){ @@ -50113,7 +50513,7 @@ static int walBusyLock( ){ int rc; do { - rc = walLockExclusive(pWal, lockIdx, n); + rc = walLockExclusive(pWal, lockIdx, n, 0); }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); return rc; } @@ -50232,6 +50632,14 @@ static int walCheckpoint( mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; for(i=1; i<WAL_NREADER; i++){ + /* Thread-sanitizer reports that the following is an unsafe read, + ** as some other thread may be in the process of updating the value + ** of the aReadMark[] slot. The assumption here is that if that is + ** happening, the other client may only be increasing the value, + ** not decreasing it. So assuming either that either the "old" or + ** "new" version of the value is read, and not some arbitrary value + ** that would never be written by a real client, things are still + ** safe. */ u32 y = pInfo->aReadMark[i]; if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); @@ -50546,7 +50954,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } - }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ + }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 1)) ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); @@ -50752,7 +51160,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ && (mxReadMark<pWal->hdr.mxFrame || mxI==0) ){ for(i=1; i<WAL_NREADER; i++){ - rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); + rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1, 0); if( rc==SQLITE_OK ){ mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame; mxI = i; @@ -51008,7 +51416,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){ /* Only one writer allowed at a time. Get the write lock. Return ** SQLITE_BUSY if unable. */ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 0); if( rc ){ return rc; } @@ -51153,7 +51561,7 @@ static int walRestartLog(Wal *pWal){ if( pInfo->nBackfill>0 ){ u32 salt1; sqlite3_randomness(4, &salt1); - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1, 0); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions @@ -51478,7 +51886,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. */ - rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1, 0); if( rc ){ /* EVIDENCE-OF: R-10421-19736 If any other process is running a ** checkpoint operation at the same time, the lock cannot be obtained and @@ -51953,6 +52361,7 @@ struct MemPage { u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ u8 max1bytePayload; /* min(maxLocal,127) */ + u8 bBusy; /* Prevent endless loops on corrupt database files */ u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ @@ -52091,6 +52500,9 @@ struct BtShared { #endif u8 inTransaction; /* Transaction state */ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ +#ifdef SQLITE_HAS_CODEC + u8 optimalReserve; /* Desired amount of reserved space per page */ +#endif u16 btsFlags; /* Boolean parameters. See BTS_* macros below */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ @@ -52477,6 +52889,7 @@ static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){ ** Exit the recursive mutex on a Btree. */ SQLITE_PRIVATE void sqlite3BtreeLeave(Btree *p){ + assert( sqlite3_mutex_held(p->db->mutex) ); if( p->sharable ){ assert( p->wantToLock>0 ); p->wantToLock--; @@ -52724,7 +53137,7 @@ static BtShared *SQLITE_WSD sqlite3SharedCacheList = 0; ** The shared cache setting effects only future calls to ** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(). */ -SQLITE_API int sqlite3_enable_shared_cache(int enable){ +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int enable){ sqlite3GlobalConfig.sharedCacheEnabled = enable; return SQLITE_OK; } @@ -52813,6 +53226,12 @@ static int hasSharedCacheTableLock( for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ Index *pIdx = (Index *)sqliteHashData(p); if( pIdx->tnum==(int)iRoot ){ + if( iTab ){ + /* Two or more indexes share the same root page. There must + ** be imposter tables. So just return true. The assert is not + ** useful in that case. */ + return 1; + } iTab = pIdx->pTable->tnum; } } @@ -53232,10 +53651,15 @@ static void btreeReleaseAllCursorPages(BtCursor *pCur){ static int saveCursorPosition(BtCursor *pCur){ int rc; - assert( CURSOR_VALID==pCur->eState ); + assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState ); assert( 0==pCur->pKey ); assert( cursorHoldsMutex(pCur) ); + if( pCur->eState==CURSOR_SKIPNEXT ){ + pCur->eState = CURSOR_VALID; + }else{ + pCur->skipNext = 0; + } rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ @@ -53306,7 +53730,7 @@ static int SQLITE_NOINLINE saveCursorsOnList( ){ do{ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ - if( p->eState==CURSOR_VALID ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; @@ -53378,17 +53802,19 @@ static int btreeMoveto( */ static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; + int skipNext; assert( cursorHoldsMutex(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); + pCur->skipNext |= skipNext; if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ pCur->eState = CURSOR_SKIPNEXT; } @@ -53440,9 +53866,10 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) *pDifferentRow = 1; return rc; } - if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){ + if( pCur->eState!=CURSOR_VALID ){ *pDifferentRow = 1; }else{ + assert( pCur->skipNext==0 ); *pDifferentRow = 0; } return SQLITE_OK; @@ -54583,16 +55010,18 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( */ if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ + int nFilename = sqlite3Strlen30(zFilename)+1; int nFullPathname = pVfs->mxPathname+1; - char *zFullPathname = sqlite3Malloc(nFullPathname); + char *zFullPathname = sqlite3Malloc(MAX(nFullPathname,nFilename)); MUTEX_LOGIC( sqlite3_mutex *mutexShared; ) + p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } if( isMemdb ){ - memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1); + memcpy(zFullPathname, zFilename, nFilename); }else{ rc = sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname); @@ -54649,8 +55078,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( ** the right size. This is to guard against size changes that result ** when compiling on a different architecture. */ - assert( sizeof(i64)==8 || sizeof(i64)==4 ); - assert( sizeof(u64)==8 || sizeof(u64)==4 ); + assert( sizeof(i64)==8 ); + assert( sizeof(u64)==8 ); assert( sizeof(u32)==4 ); assert( sizeof(u16)==2 ); assert( sizeof(Pgno)==4 ); @@ -55037,6 +55466,9 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, BtShared *pBt = p->pBt; assert( nReserve>=-1 && nReserve<=255 ); sqlite3BtreeEnter(p); +#if SQLITE_HAS_CODEC + if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve; +#endif if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ sqlite3BtreeLeave(p); return SQLITE_READONLY; @@ -55048,7 +55480,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE && ((pageSize-1)&pageSize)==0 ){ assert( (pageSize & 7)==0 ); - assert( !pBt->pPage1 && !pBt->pCursor ); + assert( !pBt->pCursor ); pBt->pageSize = (u32)pageSize; freeTempSpace(pBt); } @@ -55066,7 +55498,6 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } -#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) /* ** This function is similar to sqlite3BtreeGetReserve(), except that it ** may only be called if it is guaranteed that the b-tree mutex is already @@ -55079,25 +55510,33 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ ** database handle that owns *p, causing undefined behavior. */ SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){ + int n; assert( sqlite3_mutex_held(p->pBt->mutex) ); - return p->pBt->pageSize - p->pBt->usableSize; + n = p->pBt->pageSize - p->pBt->usableSize; + return n; } -#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */ -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that ** are intentually left unused. This is the "reserved" space that is ** sometimes used by extensions. +** +** If SQLITE_HAS_MUTEX is defined then the number returned is the +** greater of the current reserved space and the maximum requested +** reserve space. */ -SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree *p){ +SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){ int n; sqlite3BtreeEnter(p); - n = p->pBt->pageSize - p->pBt->usableSize; + n = sqlite3BtreeGetReserveNoMutex(p); +#ifdef SQLITE_HAS_CODEC + if( n<p->pBt->optimalReserve ) n = p->pBt->optimalReserve; +#endif sqlite3BtreeLeave(p); return n; } + /* ** Set the maximum page count for a database if mxPage is positive. ** No changes are made if mxPage is 0 or negative. @@ -55128,7 +55567,6 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){ sqlite3BtreeLeave(p); return b; } -#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */ /* ** Change the 'auto-vacuum' property of the database. If the 'autoVacuum' @@ -56248,7 +56686,7 @@ SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int wr for(p=pBtree->pBt->pCursor; p; p=p->pNext){ int i; if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){ - if( p->eState==CURSOR_VALID ){ + if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){ rc = saveCursorPosition(p); if( rc!=SQLITE_OK ){ (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0); @@ -56654,6 +57092,8 @@ SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 ); + assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 ); getCellInfo(pCur); *pSize = pCur->info.nPayload; @@ -57062,13 +57502,18 @@ static const void *fetchPayload( BtCursor *pCur, /* Cursor pointing to entry to read from */ u32 *pAmt /* Write the number of available bytes here */ ){ + u32 amt; assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); assert( pCur->eState==CURSOR_VALID ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); assert( pCur->info.nSize>0 ); - *pAmt = pCur->info.nLocal; + assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); + assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB); + amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload); + if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal; + *pAmt = amt; return (void*)pCur->info.pPayload; } @@ -57132,7 +57577,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ return SQLITE_OK; } -#if 0 +#if SQLITE_DEBUG /* ** Page pParent is an internal (non-leaf) tree page. This function ** asserts that page number iChild is the left-child if the iIdx'th @@ -57141,6 +57586,8 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ ** the page. */ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){ + if( CORRUPT_DB ) return; /* The conditions tested below might not be true + ** in a corrupt database */ assert( iIdx<=pParent->nCell ); if( iIdx==pParent->nCell ){ assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild ); @@ -57165,19 +57612,11 @@ static void moveToParent(BtCursor *pCur){ assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); assert( pCur->apPage[pCur->iPage] ); - - /* UPDATE: It is actually possible for the condition tested by the assert - ** below to be untrue if the database file is corrupt. This can occur if - ** one cursor has modified page pParent while a reference to it is held - ** by a second cursor. Which can only happen if a single page is linked - ** into more than one b-tree structure in a corrupt database. */ -#if 0 assertParentIndex( pCur->apPage[pCur->iPage-1], pCur->aiIdx[pCur->iPage-1], pCur->apPage[pCur->iPage]->pgno ); -#endif testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell ); releasePage(pCur->apPage[pCur->iPage]); @@ -59352,7 +59791,6 @@ static int balance_nonroot( }else if( iParentIdx==i ){ nxDiv = i-2+bBulk; }else{ - assert( bBulk==0 ); nxDiv = iParentIdx-1; } i = 2-bBulk; @@ -60103,7 +60541,8 @@ static int balance(BtCursor *pCur){ ** pSpace buffer passed to the latter call to balance_nonroot(). */ u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize); - rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, pCur->hints); + rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, + pCur->hints&BTREE_BULKLOAD); if( pFree ){ /* If pFree is not NULL, it points to the pSpace buffer used ** by a previous call to balance_nonroot(). Its contents are @@ -60124,6 +60563,7 @@ static int balance(BtCursor *pCur){ /* The next iteration of the do-loop balances the parent page. */ releasePage(pPage); pCur->iPage--; + assert( pCur->iPage>=0 ); } }while( rc==SQLITE_OK ); @@ -60600,9 +61040,13 @@ static int clearDatabasePage( if( pgno>btreePagecount(pBt) ){ return SQLITE_CORRUPT_BKPT; } - rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; + if( pPage->bBusy ){ + rc = SQLITE_CORRUPT_BKPT; + goto cleardatabasepage_out; + } + pPage->bBusy = 1; hdr = pPage->hdrOffset; for(i=0; i<pPage->nCell; i++){ pCell = findCell(pPage, i); @@ -60627,6 +61071,7 @@ static int clearDatabasePage( } cleardatabasepage_out: + pPage->bBusy = 0; releasePage(pPage); return rc; } @@ -61134,6 +61579,57 @@ static void checkList( } #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ +/* +** An implementation of a min-heap. +** +** aHeap[0] is the number of elements on the heap. aHeap[1] is the +** root element. The daughter nodes of aHeap[N] are aHeap[N*2] +** and aHeap[N*2+1]. +** +** The heap property is this: Every node is less than or equal to both +** of its daughter nodes. A consequence of the heap property is that the +** root node aHeap[1] is always the minimum value currently in the heap. +** +** The btreeHeapInsert() routine inserts an unsigned 32-bit number onto +** the heap, preserving the heap property. The btreeHeapPull() routine +** removes the root element from the heap (the minimum value in the heap) +** and then moves other nodes around as necessary to preserve the heap +** property. +** +** This heap is used for cell overlap and coverage testing. Each u32 +** entry represents the span of a cell or freeblock on a btree page. +** The upper 16 bits are the index of the first byte of a range and the +** lower 16 bits are the index of the last byte of that range. +*/ +static void btreeHeapInsert(u32 *aHeap, u32 x){ + u32 j, i = ++aHeap[0]; + aHeap[i] = x; + while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){ + x = aHeap[j]; + aHeap[j] = aHeap[i]; + aHeap[i] = x; + i = j; + } +} +static int btreeHeapPull(u32 *aHeap, u32 *pOut){ + u32 j, i, x; + if( (x = aHeap[0])==0 ) return 0; + *pOut = aHeap[1]; + aHeap[1] = aHeap[x]; + aHeap[x] = 0xffffffff; + aHeap[0]--; + i = 1; + while( (j = i*2)<=aHeap[0] ){ + if( aHeap[j]>aHeap[j+1] ) j++; + if( aHeap[i]<aHeap[j] ) break; + x = aHeap[i]; + aHeap[i] = aHeap[j]; + aHeap[j] = x; + i = j; + } + return 1; +} + #ifndef SQLITE_OMIT_INTEGRITY_CHECK /* ** Do various sanity checks on a single page of a tree. Return @@ -61166,7 +61662,8 @@ static int checkTreePage( u8 *data; BtShared *pBt; int usableSize; - char *hit = 0; + u32 *heap = 0; + u32 x, prev = 0; i64 nMinKey = 0; i64 nMaxKey = 0; const char *saved_zPfx = pCheck->zPfx; @@ -61311,15 +61808,15 @@ static int checkTreePage( */ data = pPage->aData; hdr = pPage->hdrOffset; - hit = sqlite3PageMalloc( pBt->pageSize ); + heap = (u32*)sqlite3PageMalloc( pBt->pageSize ); pCheck->zPfx = 0; - if( hit==0 ){ + if( heap==0 ){ pCheck->mallocFailed = 1; }else{ int contentOffset = get2byteNotZero(&data[hdr+5]); assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ - memset(hit+contentOffset, 0, usableSize-contentOffset); - memset(hit, 1, contentOffset); + heap[0] = 0; + btreeHeapInsert(heap, contentOffset-1); /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); @@ -61331,7 +61828,6 @@ static int checkTreePage( for(i=0; i<nCell; i++){ int pc = get2byte(&data[cellStart+i*2]); u32 size = 65536; - int j; if( pc<=usableSize-4 ){ size = cellSizePtr(pPage, &data[pc]); } @@ -61340,7 +61836,7 @@ static int checkTreePage( checkAppendMsg(pCheck, "Corruption detected in cell %d on page %d",i,iPage); }else{ - for(j=pc+size-1; j>=pc; j--) hit[j]++; + btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header @@ -61352,7 +61848,7 @@ static int checkTreePage( assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ size = get2byte(&data[i+2]); assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ - for(j=i+size-1; j>=i; j--) hit[j]++; + btreeHeapInsert(heap, (i<<16)|(i+size-1)); /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a ** big-endian integer which is the offset in the b-tree page of the next ** freeblock in the chain, or zero if the freeblock is the last on the @@ -61364,27 +61860,33 @@ static int checkTreePage( assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ i = j; } - for(i=cnt=0; i<usableSize; i++){ - if( hit[i]==0 ){ - cnt++; - }else if( hit[i]>1 ){ + cnt = 0; + assert( heap[0]>0 ); + assert( (heap[1]>>16)==0 ); + btreeHeapPull(heap,&prev); + while( btreeHeapPull(heap,&x) ){ + if( (prev&0xffff)+1>(x>>16) ){ checkAppendMsg(pCheck, - "Multiple uses for byte %d of page %d", i, iPage); + "Multiple uses for byte %u of page %d", x>>16, iPage); break; + }else{ + cnt += (x>>16) - (prev&0xffff) - 1; + prev = x; } } + cnt += usableSize - (prev&0xffff) - 1; /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments ** is stored in the fifth field of the b-tree page header. ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the ** number of fragmented free bytes within the cell content area. */ - if( cnt!=data[hdr+7] ){ + if( heap[0]==0 && cnt!=data[hdr+7] ){ checkAppendMsg(pCheck, "Fragmentation of %d bytes reported as %d on page %d", cnt, data[hdr+7], iPage); } } - sqlite3PageFree(hit); + sqlite3PageFree(heap); releasePage(pPage); end_of_check: @@ -61448,8 +61950,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); - sCheck.errMsg.useMalloc = 2; + sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); /* Check the integrity of the freelist */ @@ -61766,14 +62267,23 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){ } /* -** set the mask of hint flags for cursor pCsr. Currently the only valid -** values are 0 and BTREE_BULKLOAD. +** set the mask of hint flags for cursor pCsr. */ SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){ - assert( mask==BTREE_BULKLOAD || mask==0 ); + assert( mask==BTREE_BULKLOAD || mask==BTREE_SEEK_EQ || mask==0 ); pCsr->hints = mask; } +#ifdef SQLITE_DEBUG +/* +** Return true if the cursor has a hint specified. This routine is +** only used from within assert() statements +*/ +SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor *pCsr, unsigned int mask){ + return (pCsr->hints & mask)!=0; +} +#endif + /* ** Return true if the given Btree is read-only. */ @@ -61932,7 +62442,7 @@ static int checkReadTransaction(sqlite3 *db, Btree *p){ ** If an error occurs, NULL is returned and an error code and error message ** stored in database handle pDestDb. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3* pDestDb, /* Database to write to */ const char *zDestDb, /* Name of database within pDestDb */ sqlite3* pSrcDb, /* Database connection to read from */ @@ -62035,7 +62545,7 @@ static int backupOnePage( ** guaranteed that the shared-mutex is held by this thread, handle ** p->pSrc may not actually be the owner. */ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc); - int nDestReserve = sqlite3BtreeGetReserve(p->pDest); + int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest); #endif int rc = SQLITE_OK; i64 iOff; @@ -62140,7 +62650,7 @@ static void attachBackupObject(sqlite3_backup *p){ /* ** Copy nPage pages from the source b-tree to the destination. */ -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; int destMode; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ @@ -62385,7 +62895,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* ** Release all resources associated with an sqlite3_backup* handle. */ -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p){ sqlite3_backup **pp; /* Ptr to head of pagers backup list */ sqlite3 *pSrcDb; /* Source database connection */ int rc; /* Value to return */ @@ -62437,7 +62947,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ ** Return the number of pages still to be backed up as of the most recent ** call to sqlite3_backup_step(). */ -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ){ (void)SQLITE_MISUSE_BKPT; @@ -62451,7 +62961,7 @@ SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){ ** Return the total number of pages in the source database as of the most ** recent call to sqlite3_backup_step(). */ -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p){ #ifdef SQLITE_ENABLE_API_ARMOR if( p==0 ){ (void)SQLITE_MISUSE_BKPT; @@ -62776,10 +63286,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){ pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; + } + pMem->flags &= ~MEM_Ephem; #ifdef SQLITE_DEBUG - pMem->pScopyFrom = 0; + pMem->pScopyFrom = 0; #endif - } return SQLITE_OK; } @@ -63666,7 +64177,7 @@ struct ValueNewStat4Ctx { ** Otherwise, if the second argument is non-zero, then this function is ** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not ** already been allocated, allocate the UnpackedRecord structure that -** that function will return to its caller here. Then return a pointer +** that function will return to its caller here. Then return a pointer to ** an sqlite3_value within the UnpackedRecord.a[] array. */ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ @@ -63711,6 +64222,113 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ } /* +** The expression object indicated by the second argument is guaranteed +** to be a scalar SQL function. If +** +** * all function arguments are SQL literals, +** * the SQLITE_FUNC_CONSTANT function flag is set, and +** * the SQLITE_FUNC_NEEDCOLL function flag is not set, +** +** then this routine attempts to invoke the SQL function. Assuming no +** error occurs, output parameter (*ppVal) is set to point to a value +** object containing the result before returning SQLITE_OK. +** +** Affinity aff is applied to the result of the function before returning. +** If the result is a text value, the sqlite3_value object uses encoding +** enc. +** +** If the conditions above are not met, this function returns SQLITE_OK +** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to +** NULL and an SQLite error code returned. +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static int valueFromFunction( + sqlite3 *db, /* The database connection */ + Expr *p, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 aff, /* Affinity to use */ + sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ +){ + sqlite3_context ctx; /* Context object for function invocation */ + sqlite3_value **apVal = 0; /* Function arguments */ + int nVal = 0; /* Size of apVal[] array */ + FuncDef *pFunc = 0; /* Function definition */ + sqlite3_value *pVal = 0; /* New value */ + int rc = SQLITE_OK; /* Return code */ + int nName; /* Size of function name in bytes */ + ExprList *pList = 0; /* Function arguments */ + int i; /* Iterator variable */ + + assert( pCtx!=0 ); + assert( (p->flags & EP_TokenOnly)==0 ); + pList = p->x.pList; + if( pList ) nVal = pList->nExpr; + nName = sqlite3Strlen30(p->u.zToken); + pFunc = sqlite3FindFunction(db, p->u.zToken, nName, nVal, enc, 0); + assert( pFunc ); + if( (pFunc->funcFlags & SQLITE_FUNC_CONSTANT)==0 + || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + ){ + return SQLITE_OK; + } + + if( pList ){ + apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal); + if( apVal==0 ){ + rc = SQLITE_NOMEM; + goto value_from_function_out; + } + for(i=0; i<nVal; i++){ + rc = sqlite3ValueFromExpr(db, pList->a[i].pExpr, enc, aff, &apVal[i]); + if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out; + } + } + + pVal = valueNew(db, pCtx); + if( pVal==0 ){ + rc = SQLITE_NOMEM; + goto value_from_function_out; + } + + assert( pCtx->pParse->rc==SQLITE_OK ); + memset(&ctx, 0, sizeof(ctx)); + ctx.pOut = pVal; + ctx.pFunc = pFunc; + pFunc->xFunc(&ctx, nVal, apVal); + if( ctx.isError ){ + rc = ctx.isError; + sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal)); + }else{ + sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8); + assert( rc==SQLITE_OK ); + rc = sqlite3VdbeChangeEncoding(pVal, enc); + if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){ + rc = SQLITE_TOOBIG; + pCtx->pParse->nErr++; + } + } + pCtx->pParse->rc = rc; + + value_from_function_out: + if( rc!=SQLITE_OK ){ + pVal = 0; + } + if( apVal ){ + for(i=0; i<nVal; i++){ + sqlite3ValueFree(apVal[i]); + } + sqlite3DbFree(db, apVal); + } + + *ppVal = pVal; + return rc; +} +#else +# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK +#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */ + +/* ** Extract a value from the supplied expression in the manner described ** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object ** using valueNew(). @@ -63742,6 +64360,12 @@ static int valueFromExpr( while( (op = pExpr->op)==TK_UPLUS ) pExpr = pExpr->pLeft; if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; + /* Compressed expressions only appear when parsing the DEFAULT clause + ** on a table column definition, and hence only when pCtx==0. This + ** check ensures that an EP_TokenOnly expression is never passed down + ** into valueFromFunction(). */ + assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 ); + if( op==TK_CAST ){ u8 aff = sqlite3AffinityType(pExpr->u.zToken,0); rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx); @@ -63818,6 +64442,12 @@ static int valueFromExpr( } #endif +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + else if( op==TK_FUNCTION && pCtx!=0 ){ + rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx); + } +#endif + *ppVal = pVal; return rc; @@ -64104,7 +64734,7 @@ SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ Mem *aMem = pRec->aMem; sqlite3 *db = aMem[0].db; for(i=0; i<nCol; i++){ - if( aMem[i].szMalloc ) sqlite3DbFree(db, aMem[i].zMalloc); + sqlite3VdbeMemRelease(&aMem[i]); } sqlite3KeyInfoUnref(pRec->pKeyInfo); sqlite3DbFree(db, pRec); @@ -64207,7 +64837,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepa /* ** Return the SQL associated with a prepared statement */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe *)pStmt; return (p && p->isPrepareV2) ? p->zSql : 0; } @@ -65270,7 +65900,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ #ifndef SQLITE_OMIT_VIRTUALTABLE case P4_VTAB: { sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; - sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); + sqlite3_snprintf(nTemp, zTemp, "vtab:%p", pVtab); break; } #endif @@ -65934,20 +66564,37 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ else if( pCx->pVtabCursor ){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; - p->inVtabMethod = 1; + assert( pVtabCursor->pVtab->nRef>0 ); + pVtabCursor->pVtab->nRef--; pModule->xClose(pVtabCursor); - p->inVtabMethod = 0; } #endif } /* +** Close all cursors in the current frame. +*/ +static void closeCursorsInFrame(Vdbe *p){ + if( p->apCsr ){ + int i; + for(i=0; i<p->nCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + sqlite3VdbeFreeCursor(p, pC); + p->apCsr[i] = 0; + } + } + } +} + +/* ** Copy the values stored in the VdbeFrame structure to its Vdbe. This ** is used, for example, when a trigger sub-program is halted to restore ** control to the main program. */ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; + closeCursorsInFrame(v); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS v->anExec = pFrame->anExec; #endif @@ -65982,17 +66629,7 @@ static void closeAllCursors(Vdbe *p){ p->nFrame = 0; } assert( p->nFrame==0 ); - - if( p->apCsr ){ - int i; - for(i=0; i<p->nCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC ){ - sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; - } - } - } + closeCursorsInFrame(p); if( p->aMem ){ releaseMemArray(&p->aMem[1], p->nMem); } @@ -66295,7 +66932,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ ** doing this the directory is synced again before any individual ** transaction files are deleted. */ - rc = sqlite3OsDelete(pVfs, zMaster, 1); + rc = sqlite3OsDelete(pVfs, zMaster, needSync); sqlite3DbFree(db, zMaster); zMaster = 0; if( rc ){ @@ -67525,7 +68162,8 @@ static void vdbeAssertFieldCountWithinLimits( if( CORRUPT_DB ) return; idx = getVarint32(aKey, szHdr); - assert( szHdr<=nKey ); + assert( nKey>=0 ); + assert( szHdr<=(u32)nKey ); while( idx<szHdr ){ idx += getVarint32(aKey+idx, notUsed); nField++; @@ -67736,7 +68374,7 @@ static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){ ** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the ** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db). */ -static int vdbeRecordCompareWithSkip( +SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2, /* Right key */ int bSkip /* If true, skip the first field */ @@ -67922,7 +68560,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2 /* Right key */ ){ - return vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); + return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0); } @@ -68010,7 +68648,7 @@ static int vdbeRecordCompareInt( }else if( pPKey2->nField>1 ){ /* The first fields of the two keys are equal. Compare the trailing ** fields. */ - res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); }else{ /* The first fields of the two keys are equal and there are no trailing ** fields. Return pPKey2->default_rc in this case. */ @@ -68058,7 +68696,7 @@ static int vdbeRecordCompareString( res = nStr - pPKey2->aMem[0].n; if( res==0 ){ if( pPKey2->nField>1 ){ - res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); + res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1); }else{ res = pPKey2->default_rc; } @@ -68362,7 +69000,7 @@ SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){ ** collating sequences are registered or if an authorizer function is ** added or changed. */ -SQLITE_API int sqlite3_expired(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; return p==0 || p->expired; } @@ -68399,7 +69037,7 @@ static int vdbeSafetyNotNull(Vdbe *p){ ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL @@ -68425,7 +69063,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){ ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ rc = SQLITE_OK; @@ -68444,7 +69082,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ /* ** Set all the parameters in the compiled SQL statement to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int i; int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; @@ -68468,7 +69106,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ ** The following routines extract information from a Mem or sqlite3_value ** structure. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); @@ -68478,36 +69116,40 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ return sqlite3_value_text(pVal); } } -SQLITE_API int sqlite3_value_bytes(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } -SQLITE_API int sqlite3_value_bytes16(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE); } -SQLITE_API double sqlite3_value_double(sqlite3_value *pVal){ +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value *pVal){ return sqlite3VdbeRealValue((Mem*)pVal); } -SQLITE_API int sqlite3_value_int(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value *pVal){ return (int)sqlite3VdbeIntValue((Mem*)pVal); } -SQLITE_API sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value *pVal){ return sqlite3VdbeIntValue((Mem*)pVal); } -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){ +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value *pVal){ return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_value_text16(sqlite3_value* pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value* pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE); } -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value *pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16BE); } -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value *pVal){ return sqlite3ValueText(pVal, SQLITE_UTF16LE); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ +/* EVIDENCE-OF: R-12793-43283 Every value in SQLite has one of five +** fundamental datatypes: 64-bit signed integer 64-bit IEEE floating +** point number string BLOB NULL +*/ +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value* pVal){ static const u8 aType[] = { SQLITE_BLOB, /* 0x00 */ SQLITE_NULL, /* 0x01 */ @@ -68583,7 +69225,7 @@ static int invokeValueDestructor( if( pCtx ) sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } -SQLITE_API void sqlite3_result_blob( +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob( sqlite3_context *pCtx, const void *z, int n, @@ -68593,7 +69235,7 @@ SQLITE_API void sqlite3_result_blob( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, 0, xDel); } -SQLITE_API void sqlite3_result_blob64( +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob64( sqlite3_context *pCtx, const void *z, sqlite3_uint64 n, @@ -68607,37 +69249,37 @@ SQLITE_API void sqlite3_result_blob64( setResultStrOrError(pCtx, z, (int)n, 0, xDel); } } -SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context *pCtx, double rVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } -SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; pCtx->fErrorOrAux = 1; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif -SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context *pCtx, int iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } -SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } -SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } -SQLITE_API void sqlite3_result_text( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text( sqlite3_context *pCtx, const char *z, int n, @@ -68646,7 +69288,7 @@ SQLITE_API void sqlite3_result_text( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); } -SQLITE_API void sqlite3_result_text64( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text64( sqlite3_context *pCtx, const char *z, sqlite3_uint64 n, @@ -68663,7 +69305,7 @@ SQLITE_API void sqlite3_result_text64( } } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API void sqlite3_result_text16( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16( sqlite3_context *pCtx, const void *z, int n, @@ -68672,7 +69314,7 @@ SQLITE_API void sqlite3_result_text16( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel); } -SQLITE_API void sqlite3_result_text16be( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be( sqlite3_context *pCtx, const void *z, int n, @@ -68681,7 +69323,7 @@ SQLITE_API void sqlite3_result_text16be( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel); } -SQLITE_API void sqlite3_result_text16le( +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le( sqlite3_context *pCtx, const void *z, int n, @@ -68691,17 +69333,20 @@ SQLITE_API void sqlite3_result_text16le( setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemCopy(pCtx->pOut, pValue); } -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n); } -SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ pCtx->isError = errCode; pCtx->fErrorOrAux = 1; +#ifdef SQLITE_DEBUG + if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; +#endif if( pCtx->pOut->flags & MEM_Null ){ sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1, SQLITE_UTF8, SQLITE_STATIC); @@ -68709,7 +69354,7 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ } /* Force an SQLITE_TOOBIG error. */ -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; pCtx->fErrorOrAux = 1; @@ -68718,7 +69363,7 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ } /* An SQLITE_NOMEM error. */ -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context *pCtx){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM; @@ -68782,7 +69427,7 @@ static int sqlite3Step(Vdbe *p){ ** or SQLITE_BUSY error. */ #ifdef SQLITE_OMIT_AUTORESET - if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){ + if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){ sqlite3_reset((sqlite3_stmt*)p); }else{ return SQLITE_MISUSE_BKPT; @@ -68828,6 +69473,9 @@ static int sqlite3Step(Vdbe *p){ if( p->bIsReader ) db->nVdbeRead++; p->pc = 0; } +#ifdef SQLITE_DEBUG + p->rcApp = SQLITE_OK; +#endif #ifndef SQLITE_OMIT_EXPLAIN if( p->explain ){ rc = sqlite3VdbeList(p); @@ -68872,7 +69520,7 @@ end_of_step: assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || rc==SQLITE_BUSY || rc==SQLITE_MISUSE ); - assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE ); + assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using sqlite3_prepare_v2(), and an ** error has occurred, then return the error code in p->rc to the @@ -68888,7 +69536,7 @@ end_of_step: ** sqlite3Step() to do most of the work. If a schema error occurs, ** call sqlite3Reprepare() and try again. */ -SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt *pStmt){ int rc = SQLITE_OK; /* Result from sqlite3Step() */ int rc2 = SQLITE_OK; /* Result from sqlite3Reprepare() */ Vdbe *v = (Vdbe*)pStmt; /* the prepared statement */ @@ -68939,7 +69587,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context *p){ assert( p && p->pFunc ); return p->pFunc->pUserData; } @@ -68954,22 +69602,32 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ ** sqlite3_create_function16() routines that originally registered the ** application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->pOut->db; } /* -** Return the current time for a statement +** Return the current time for a statement. If the current time +** is requested more than once within the same run of a single prepared +** statement, the exact same time is returned for each invocation regardless +** of the amount of time that elapses between invocations. In other words, +** the time returned is always the time of the first call. */ SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){ - Vdbe *v = p->pVdbe; int rc; - if( v->iCurrentTime==0 ){ - rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, &v->iCurrentTime); - if( rc ) v->iCurrentTime = 0; +#ifndef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime; + assert( p->pVdbe!=0 ); +#else + sqlite3_int64 iTime = 0; + sqlite3_int64 *piTime = p->pVdbe!=0 ? &p->pVdbe->iCurrentTime : &iTime; +#endif + if( *piTime==0 ){ + rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, piTime); + if( rc ) *piTime = 0; } - return v->iCurrentTime; + return *piTime; } /* @@ -69020,7 +69678,7 @@ static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){ ** context is allocated on the first call. Subsequent calls return the ** same context that was returned on prior calls. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context *p, int nByte){ assert( p && p->pFunc && p->pFunc->xStep ); assert( sqlite3_mutex_held(p->pOut->db->mutex) ); testcase( nByte<0 ); @@ -69035,10 +69693,15 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ ** Return the auxiliary data pointer, if any, for the iArg'th argument to ** the user-function defined by pCtx. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); +#if SQLITE_ENABLE_STAT3_OR_STAT4 + if( pCtx->pVdbe==0 ) return 0; +#else + assert( pCtx->pVdbe!=0 ); +#endif for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break; } @@ -69051,7 +69714,7 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ ** argument to the user-function defined by pCtx. Any previous value is ** deleted by calling the delete function specified when it was set. */ -SQLITE_API void sqlite3_set_auxdata( +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata( sqlite3_context *pCtx, int iArg, void *pAux, @@ -69062,6 +69725,11 @@ SQLITE_API void sqlite3_set_auxdata( assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); if( iArg<0 ) goto failed; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( pVdbe==0 ) goto failed; +#else + assert( pVdbe!=0 ); +#endif for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break; @@ -69101,7 +69769,7 @@ failed: ** implementations should keep their own counts within their aggregate ** context. */ -SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ +SQLITE_API int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context *p){ assert( p && p->pMem && p->pFunc && p->pFunc->xStep ); return p->pMem->n; } @@ -69110,7 +69778,7 @@ SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){ /* ** Return the number of columns in the result set for the statement pStmt. */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; return pVm ? pVm->nResColumn : 0; } @@ -69119,7 +69787,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt){ ** Return the number of values available from the current row of the ** currently executing statement pStmt. */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; if( pVm==0 || pVm->pResultSet==0 ) return 0; return pVm->nResColumn; @@ -69221,7 +69889,7 @@ static void columnMallocFailure(sqlite3_stmt *pStmt) ** The following routines are used to access elements of the current row ** in the result set. */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ const void *val; val = sqlite3_value_blob( columnMem(pStmt,i) ); /* Even though there is no encoding conversion, value_blob() might @@ -69231,37 +69899,37 @@ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){ columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_bytes( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_bytes16( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API double sqlite3_column_double(sqlite3_stmt *pStmt, int i){ +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt *pStmt, int i){ double val = sqlite3_value_double( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API int sqlite3_column_int(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt *pStmt, int i){ int val = sqlite3_value_int( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt *pStmt, int i){ sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){ +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt *pStmt, int i){ const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt *pStmt, int i){ Mem *pOut = columnMem(pStmt, i); if( pOut->flags&MEM_Static ){ pOut->flags &= ~MEM_Static; @@ -69271,13 +69939,13 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){ return (sqlite3_value *)pOut; } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt *pStmt, int i){ const void *val = sqlite3_value_text16( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return val; } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt *pStmt, int i){ int iType = sqlite3_value_type( columnMem(pStmt,i) ); columnMallocFailure(pStmt); return iType; @@ -69341,12 +70009,12 @@ static const void *columnName( ** Return the name of the Nth column of the result set returned by SQL ** statement pStmt. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_NAME); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_NAME); } @@ -69366,12 +70034,12 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){ ** Return the column declaration type (if applicable) of the 'i'th column ** of the result set of SQL statement pStmt. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DECLTYPE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DECLTYPE); } @@ -69384,12 +70052,12 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){ ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_DATABASE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_DATABASE); } @@ -69400,12 +70068,12 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_TABLE); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_TABLE); } @@ -69416,12 +70084,12 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){ ** NULL is returned if the result column is an expression or constant or ** anything else which is not an unambiguous reference to a database column. */ -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, COLNAME_COLUMN); } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){ return columnName( pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, COLNAME_COLUMN); } @@ -69522,7 +70190,7 @@ static int bindText( /* ** Bind a blob value to an SQL statement variable. */ -SQLITE_API int sqlite3_bind_blob( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69531,7 +70199,7 @@ SQLITE_API int sqlite3_bind_blob( ){ return bindText(pStmt, i, zData, nData, xDel, 0); } -SQLITE_API int sqlite3_bind_blob64( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69545,7 +70213,7 @@ SQLITE_API int sqlite3_bind_blob64( return bindText(pStmt, i, zData, (int)nData, xDel, 0); } } -SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69555,10 +70223,10 @@ SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){ } return rc; } -SQLITE_API int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){ return sqlite3_bind_int64(p, i, (i64)iValue); } -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69568,7 +70236,7 @@ SQLITE_API int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValu } return rc; } -SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ int rc; Vdbe *p = (Vdbe*)pStmt; rc = vdbeUnbind(p, i); @@ -69577,7 +70245,7 @@ SQLITE_API int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){ } return rc; } -SQLITE_API int sqlite3_bind_text( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text( sqlite3_stmt *pStmt, int i, const char *zData, @@ -69586,7 +70254,7 @@ SQLITE_API int sqlite3_bind_text( ){ return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8); } -SQLITE_API int sqlite3_bind_text64( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64( sqlite3_stmt *pStmt, int i, const char *zData, @@ -69603,7 +70271,7 @@ SQLITE_API int sqlite3_bind_text64( } } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_bind_text16( +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16( sqlite3_stmt *pStmt, int i, const void *zData, @@ -69613,7 +70281,7 @@ SQLITE_API int sqlite3_bind_text16( return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE); } #endif /* SQLITE_OMIT_UTF16 */ -SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ int rc; switch( sqlite3_value_type((sqlite3_value*)pValue) ){ case SQLITE_INTEGER: { @@ -69644,7 +70312,7 @@ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_valu } return rc; } -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ int rc; Vdbe *p = (Vdbe *)pStmt; rc = vdbeUnbind(p, i); @@ -69659,7 +70327,7 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ ** Return the number of wildcards that can be potentially bound to. ** This routine is added to support DBD::SQLite. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; return p ? p->nVar : 0; } @@ -69670,7 +70338,7 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ ** ** The result is always UTF-8. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; if( p==0 || i<1 || i>p->nzVar ){ return 0; @@ -69698,7 +70366,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nNa } return 0; } -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName)); } @@ -69732,7 +70400,7 @@ SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt ** an SQLITE_ERROR is returned. Nothing else can go wrong, so otherwise ** SQLITE_OK is returned. */ -SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt *pToStmt){ Vdbe *pFrom = (Vdbe*)pFromStmt; Vdbe *pTo = (Vdbe*)pToStmt; if( pFrom->nVar!=pTo->nVar ){ @@ -69754,7 +70422,7 @@ SQLITE_API int sqlite3_transfer_bindings(sqlite3_stmt *pFromStmt, sqlite3_stmt * ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } @@ -69762,14 +70430,14 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ ** Return true if the prepared statement is guaranteed to not modify the ** database. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } /* ** Return true if the prepared statement is in need of being reset. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt *pStmt){ Vdbe *v = (Vdbe*)pStmt; return v!=0 && v->pc>=0 && v->magic==VDBE_MAGIC_RUN; } @@ -69780,7 +70448,7 @@ SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){ ** prepared statement for the database connection. Return NULL if there ** are no more. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ sqlite3_stmt *pNext; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(pDb) ){ @@ -69801,7 +70469,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ /* ** Return the value of a status counter for a prepared statement */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ Vdbe *pVdbe = (Vdbe*)pStmt; u32 v; #ifdef SQLITE_ENABLE_API_ARMOR @@ -69819,7 +70487,7 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){ /* ** Return status data for a single loop within query pStmt. */ -SQLITE_API int sqlite3_stmt_scanstatus( +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement being queried */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Which metric to return */ @@ -69878,7 +70546,7 @@ SQLITE_API int sqlite3_stmt_scanstatus( /* ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. */ -SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ +SQLITE_API void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; memset(p->anExec, 0, p->nOp * sizeof(i64)); } @@ -69970,9 +70638,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( char zBase[100]; /* Initial working space */ db = p->db; - sqlite3StrAccumInit(&out, zBase, sizeof(zBase), + sqlite3StrAccumInit(&out, db, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); - out.db = db; if( db->nVdbeExec>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; @@ -69981,6 +70648,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( assert( (zRawSql - zStart) > 0 ); sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); } + }else if( p->nVar==0 ){ + sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql)); }else{ while( zRawSql[0] ){ n = findNextHostParameter(zRawSql, &nToken); @@ -69997,10 +70666,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( idx = nextIndex; } }else{ - assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); + assert( zRawSql[0]==':' || zRawSql[0]=='$' || + zRawSql[0]=='@' || zRawSql[0]=='#' ); testcase( zRawSql[0]==':' ); testcase( zRawSql[0]=='$' ); testcase( zRawSql[0]=='@' ); + testcase( zRawSql[0]=='#' ); idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); assert( idx>0 ); } @@ -70368,6 +71039,7 @@ static void applyAffinity( if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){ sqlite3VdbeMemStringify(pRec, enc, 1); } + pRec->flags &= ~(MEM_Real|MEM_Int); } } @@ -70377,7 +71049,7 @@ static void applyAffinity( ** is appropriate. But only do the conversion if it is possible without ** loss of information and return the revised type of the argument. */ -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value *pVal){ int eType = sqlite3_value_type(pVal); if( eType==SQLITE_TEXT ){ Mem *pMem = (Mem*)pVal; @@ -70675,6 +71347,21 @@ static int checkSavepointCount(sqlite3 *db){ } #endif +/* +** Return the register of pOp->p2 after first preparing it to be +** overwritten with an integer value. +*/ +static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){ + Mem *pOut; + assert( pOp->p2>0 ); + assert( pOp->p2<=(p->nMem-p->nCursor) ); + pOut = &p->aMem[pOp->p2]; + memAboutToChange(p, pOut); + if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); + pOut->flags = MEM_Int; + return pOut; +} + /* ** Execute as much of a VDBE program as we can. @@ -70683,9 +71370,11 @@ static int checkSavepointCount(sqlite3 *db){ SQLITE_PRIVATE int sqlite3VdbeExec( Vdbe *p /* The VDBE */ ){ - int pc=0; /* The program counter */ Op *aOp = p->aOp; /* Copy of p->aOp */ - Op *pOp; /* Current operation */ + Op *pOp = aOp; /* Current operation */ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + Op *pOrigOp; /* Value of pOp at the top of the loop */ +#endif int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ @@ -70761,23 +71450,22 @@ SQLITE_PRIVATE int sqlite3VdbeExec( } sqlite3EndBenignMalloc(); #endif - for(pc=p->pc; rc==SQLITE_OK; pc++){ - assert( pc>=0 && pc<p->nOp ); + for(pOp=&aOp[p->pc]; rc==SQLITE_OK; pOp++){ + assert( pOp>=aOp && pOp<&aOp[p->nOp]); if( db->mallocFailed ) goto no_mem; #ifdef VDBE_PROFILE start = sqlite3Hwtime(); #endif nVmStep++; - pOp = &aOp[pc]; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - if( p->anExec ) p->anExec[pc]++; + if( p->anExec ) p->anExec[(int)(pOp-aOp)]++; #endif /* Only allow tracing if SQLITE_DEBUG is defined. */ #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeTrace ){ - sqlite3VdbePrintOp(stdout, pc, pOp); + sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp); } #endif @@ -70794,23 +71482,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec( } #endif - /* On any opcode with the "out2-prerelease" tag, free any - ** external allocations out of mem[p2] and set mem[p2] to be - ** an undefined integer. Opcodes will either fill in the integer - ** value or convert mem[p2] to a different type. - */ - assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); - if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=(p->nMem-p->nCursor) ); - pOut = &aMem[pOp->p2]; - memAboutToChange(p, pOut); - if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); - pOut->flags = MEM_Int; - } - /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG + assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=(p->nMem-p->nCursor) ); @@ -70843,6 +71517,9 @@ SQLITE_PRIVATE int sqlite3VdbeExec( memAboutToChange(p, &aMem[pOp->p3]); } #endif +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + pOrigOp = pOp; +#endif switch( pOp->opcode ){ @@ -70866,7 +71543,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( ** ** Other keywords in the comment that follows each case are used to ** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[]. -** Keywords include: in1, in2, in3, out2_prerelease, out2, out3. See +** Keywords include: in1, in2, in3, out2, out3. See ** the mkopcodeh.awk script for additional information. ** ** Documentation about VDBE opcodes is generated by scanning this file @@ -70894,7 +71571,8 @@ SQLITE_PRIVATE int sqlite3VdbeExec( ** to the current line should be indented for EXPLAIN output. */ case OP_Goto: { /* jump */ - pc = pOp->p2 - 1; +jump_to_p2_and_check_for_interrupt: + pOp = &aOp[pOp->p2 - 1]; /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev, ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon @@ -70939,9 +71617,13 @@ case OP_Gosub: { /* jump */ assert( VdbeMemDynamic(pIn1)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; - pIn1->u.i = pc; + pIn1->u.i = (int)(pOp-aOp); REGISTER_TRACE(pOp->p1, pIn1); - pc = pOp->p2 - 1; + + /* Most jump operations do a goto to this spot in order to update + ** the pOp pointer. */ +jump_to_p2: + pOp = &aOp[pOp->p2 - 1]; break; } @@ -70953,7 +71635,7 @@ case OP_Gosub: { /* jump */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags==MEM_Int ); - pc = (int)pIn1->u.i; + pOp = &aOp[pIn1->u.i]; pIn1->flags = MEM_Undefined; break; } @@ -70977,7 +71659,7 @@ case OP_InitCoroutine: { /* jump */ assert( !VdbeMemDynamic(pOut) ); pOut->u.i = pOp->p3 - 1; pOut->flags = MEM_Int; - if( pOp->p2 ) pc = pOp->p2 - 1; + if( pOp->p2 ) goto jump_to_p2; break; } @@ -70997,7 +71679,7 @@ case OP_EndCoroutine: { /* in1 */ pCaller = &aOp[pIn1->u.i]; assert( pCaller->opcode==OP_Yield ); assert( pCaller->p2>=0 && pCaller->p2<p->nOp ); - pc = pCaller->p2 - 1; + pOp = &aOp[pCaller->p2 - 1]; pIn1->flags = MEM_Undefined; break; } @@ -71021,9 +71703,9 @@ case OP_Yield: { /* in1, jump */ assert( VdbeMemDynamic(pIn1)==0 ); pIn1->flags = MEM_Int; pcDest = (int)pIn1->u.i; - pIn1->u.i = pc; + pIn1->u.i = (int)(pOp - aOp); REGISTER_TRACE(pOp->p1, pIn1); - pc = pcDest; + pOp = &aOp[pcDest]; break; } @@ -71074,30 +71756,34 @@ case OP_HaltIfNull: { /* in3 */ case OP_Halt: { const char *zType; const char *zLogFmt; + VdbeFrame *pFrame; + int pcx; + pcx = (int)(pOp - aOp); if( pOp->p1==SQLITE_OK && p->pFrame ){ /* Halt the sub-program. Return control to the parent frame. */ - VdbeFrame *pFrame = p->pFrame; + pFrame = p->pFrame; p->pFrame = pFrame->pParent; p->nFrame--; sqlite3VdbeSetChanges(db, p->nChange); - pc = sqlite3VdbeFrameRestore(pFrame); + pcx = sqlite3VdbeFrameRestore(pFrame); lastRowid = db->lastRowid; if( pOp->p2==OE_Ignore ){ - /* Instruction pc is the OP_Program that invoked the sub-program + /* Instruction pcx is the OP_Program that invoked the sub-program ** currently being halted. If the p2 instruction of this OP_Halt ** instruction is set to OE_Ignore, then the sub-program is throwing ** an IGNORE exception. In this case jump to the address specified ** as the p2 of the calling OP_Program. */ - pc = p->aOp[pc].p2-1; + pcx = p->aOp[pcx].p2-1; } aOp = p->aOp; aMem = p->aMem; + pOp = &aOp[pcx]; break; } p->rc = pOp->p1; p->errorAction = (u8)pOp->p2; - p->pc = pc; + p->pc = pcx; if( p->rc ){ if( pOp->p5 ){ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK", @@ -71121,7 +71807,7 @@ case OP_Halt: { }else{ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType); } - sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg); + sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg); } rc = sqlite3VdbeHalt(p); assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); @@ -71140,7 +71826,8 @@ case OP_Halt: { ** ** The 32-bit integer value P1 is written into register P2. */ -case OP_Integer: { /* out2-prerelease */ +case OP_Integer: { /* out2 */ + pOut = out2Prerelease(p, pOp); pOut->u.i = pOp->p1; break; } @@ -71151,7 +71838,8 @@ case OP_Integer: { /* out2-prerelease */ ** P4 is a pointer to a 64-bit integer value. ** Write that value into register P2. */ -case OP_Int64: { /* out2-prerelease */ +case OP_Int64: { /* out2 */ + pOut = out2Prerelease(p, pOp); assert( pOp->p4.pI64!=0 ); pOut->u.i = *pOp->p4.pI64; break; @@ -71164,7 +71852,8 @@ case OP_Int64: { /* out2-prerelease */ ** P4 is a pointer to a 64-bit floating point value. ** Write that value into register P2. */ -case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ +case OP_Real: { /* same as TK_FLOAT, out2 */ + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Real; assert( !sqlite3IsNaN(*pOp->p4.pReal) ); pOut->u.r = *pOp->p4.pReal; @@ -71176,12 +71865,13 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ ** Synopsis: r[P2]='P4' ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed -** into a String before it is executed for the first time. During +** into a String opcode before it is executed for the first time. During ** this transformation, the length of string P4 is computed and stored ** as the P1 parameter. */ -case OP_String8: { /* same as TK_STRING, out2-prerelease */ +case OP_String8: { /* same as TK_STRING, out2 */ assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); pOp->opcode = OP_String; pOp->p1 = sqlite3Strlen30(pOp->p4.z); @@ -71208,18 +71898,31 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */ /* Fall through to the next case, OP_String */ } -/* Opcode: String P1 P2 * P4 * +/* Opcode: String P1 P2 P3 P4 P5 ** Synopsis: r[P2]='P4' (len=P1) ** ** The string value P4 of length P1 (bytes) is stored in register P2. +** +** If P5!=0 and the content of register P3 is greater than zero, then +** the datatype of the register P2 is converted to BLOB. The content is +** the same sequence of bytes, it is merely interpreted as a BLOB instead +** of a string, as if it had been CAST. */ -case OP_String: { /* out2-prerelease */ +case OP_String: { /* out2 */ assert( pOp->p4.z!=0 ); + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = pOp->p4.z; pOut->n = pOp->p1; pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); + if( pOp->p5 ){ + assert( pOp->p3>0 ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); + pIn3 = &aMem[pOp->p3]; + assert( pIn3->flags & MEM_Int ); + if( pIn3->u.i ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term; + } break; } @@ -71235,9 +71938,10 @@ case OP_String: { /* out2-prerelease */ ** NULL values will not compare equal even if SQLITE_NULLEQ is set on ** OP_Ne or OP_Eq. */ -case OP_Null: { /* out2-prerelease */ +case OP_Null: { /* out2 */ int cnt; u16 nullFlag; + pOut = out2Prerelease(p, pOp); cnt = pOp->p3-pOp->p2; assert( pOp->p3<=(p->nMem-p->nCursor) ); pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; @@ -71272,8 +71976,9 @@ case OP_SoftNull: { ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ -case OP_Blob: { /* out2-prerelease */ +case OP_Blob: { /* out2 */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); + pOut = out2Prerelease(p, pOp); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); @@ -71288,7 +71993,7 @@ case OP_Blob: { /* out2-prerelease */ ** If the parameter is named, then its name appears in P4. ** The P4 value is used by sqlite3_bind_parameter_name(). */ -case OP_Variable: { /* out2-prerelease */ +case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); @@ -71297,6 +72002,7 @@ case OP_Variable: { /* out2-prerelease */ if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } + pOut = out2Prerelease(p, pOp); sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); UPDATE_MAX_BLOBSIZE(pOut); break; @@ -71331,10 +72037,11 @@ case OP_Move: { memAboutToChange(p, pOut); sqlite3VdbeMemMove(pOut, pIn1); #ifdef SQLITE_DEBUG - if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){ - pOut->pScopyFrom += p1 - pOp->p2; + if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<pOut ){ + pOut->pScopyFrom += pOp->p2 - p1; } #endif + Deephemeralize(pOut); REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; @@ -71473,7 +72180,7 @@ case OP_ResultRow: { /* Return SQLITE_ROW */ - p->pc = pc + 1; + p->pc = (int)(pOp - aOp) + 1; rc = SQLITE_ROW; goto vdbe_return; } @@ -71666,7 +72373,7 @@ arithmetic_result_is_null: ** ** The interface used by the implementation of the aforementioned functions ** to retrieve the collation sequence set by this opcode is not available -** publicly, only to user functions defined in func.c. +** publicly. Only built-in functions have access to this feature. */ case OP_CollSeq: { assert( pOp->p4type==P4_COLLSEQ ); @@ -71719,7 +72426,7 @@ case OP_Function: { assert( pOp->p4type==P4_FUNCDEF ); ctx.pFunc = pOp->p4.pFunc; - ctx.iOp = pc; + ctx.iOp = (int)(pOp - aOp); ctx.pVdbe = p; MemSetTypeFlag(ctx.pOut, MEM_Null); ctx.fErrorOrAux = 0; @@ -71733,7 +72440,7 @@ case OP_Function: { sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(ctx.pOut)); rc = ctx.isError; } - sqlite3VdbeDeleteAuxData(p, pc, pOp->p1); + sqlite3VdbeDeleteAuxData(p, (int)(pOp - aOp), pOp->p1); } /* Copy the result of the function into register P3 */ @@ -71862,8 +72569,7 @@ case OP_MustBeInt: { /* jump, in1 */ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ - pc = pOp->p2 - 1; - break; + goto jump_to_p2; } } } @@ -72049,7 +72755,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ }else{ VdbeBranchTaken(2,3); if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; + goto jump_to_p2; } } break; @@ -72069,11 +72775,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ testcase( pIn1->flags & MEM_Int ); testcase( pIn1->flags & MEM_Real ); sqlite3VdbeMemStringify(pIn1, encoding, 1); + testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) ); + flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask); } if( (pIn3->flags & MEM_Str)==0 && (pIn3->flags & (MEM_Int|MEM_Real))!=0 ){ testcase( pIn3->flags & MEM_Int ); testcase( pIn3->flags & MEM_Real ); sqlite3VdbeMemStringify(pIn3, encoding, 1); + testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) ); + flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask); } } assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); @@ -72097,6 +72807,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ default: res = res>=0; break; } + /* Undo any changes made by applyAffinity() to the input registers. */ + assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) ); + pIn1->flags = flags1; + assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) ); + pIn3->flags = flags3; + if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); @@ -72106,12 +72822,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ }else{ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res ){ - pc = pOp->p2-1; + goto jump_to_p2; } } - /* Undo any changes made by applyAffinity() to the input registers. */ - pIn1->flags = flags1; - pIn3->flags = flags3; break; } @@ -72206,11 +72919,11 @@ case OP_Compare: { */ case OP_Jump: { /* jump */ if( iCompare<0 ){ - pc = pOp->p1 - 1; VdbeBranchTaken(0,3); + VdbeBranchTaken(0,3); pOp = &aOp[pOp->p1 - 1]; }else if( iCompare==0 ){ - pc = pOp->p2 - 1; VdbeBranchTaken(1,3); + VdbeBranchTaken(1,3); pOp = &aOp[pOp->p2 - 1]; }else{ - pc = pOp->p3 - 1; VdbeBranchTaken(2,3); + VdbeBranchTaken(2,3); pOp = &aOp[pOp->p3 - 1]; } break; } @@ -72320,7 +73033,7 @@ case OP_Once: { /* jump */ assert( pOp->p1<p->nOnceFlag ); VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2); if( p->aOnceFlag[pOp->p1] ){ - pc = pOp->p2-1; + goto jump_to_p2; }else{ p->aOnceFlag[pOp->p1] = 1; } @@ -72355,7 +73068,7 @@ case OP_IfNot: { /* jump, in1 */ } VdbeBranchTaken(c!=0, 2); if( c ){ - pc = pOp->p2-1; + goto jump_to_p2; } break; } @@ -72369,7 +73082,7 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); if( (pIn1->flags & MEM_Null)!=0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -72383,7 +73096,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -72597,7 +73310,7 @@ case OP_Column: { } } - /* If after trying to extra new entries from the header, nHdrParsed is + /* If after trying to extract new entries from the header, nHdrParsed is ** still not up to p2, that means that the record has fewer than p2 ** columns. So the result will be either the default value or a NULL. */ @@ -72721,7 +73434,7 @@ case OP_MakeRecord: { u64 nData; /* Number of bytes of data space */ int nHdr; /* Number of bytes of header space */ i64 nByte; /* Data space required for this record */ - int nZero; /* Number of zero bytes at the end of the record */ + i64 nZero; /* Number of zero bytes at the end of the record */ int nVarint; /* Number of bytes in a varint */ u32 serial_type; /* Type field */ Mem *pData0; /* First field to be combined into the record */ @@ -72813,7 +73526,7 @@ case OP_MakeRecord: { if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++; } nByte = nHdr+nData; - if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } @@ -72864,7 +73577,7 @@ case OP_MakeRecord: { ** opened by cursor P1 in register P2 */ #ifndef SQLITE_OMIT_BTREECOUNT -case OP_Count: { /* out2-prerelease */ +case OP_Count: { /* out2 */ i64 nEntry; BtCursor *pCrsr; @@ -72872,6 +73585,7 @@ case OP_Count: { /* out2-prerelease */ assert( pCrsr ); nEntry = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3BtreeCount(pCrsr, &nEntry); + pOut = out2Prerelease(p, pOp); pOut->u.i = nEntry; break; } @@ -72985,7 +73699,7 @@ case OP_Savepoint: { } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); db->autoCommit = 0; p->rc = rc = SQLITE_BUSY; goto vdbe_return; @@ -73044,7 +73758,7 @@ case OP_Savepoint: { db->nDeferredImmCons = pSavepoint->nDeferredImmCons; } - if( !isTransaction ){ + if( !isTransaction || p1==SAVEPOINT_ROLLBACK ){ rc = sqlite3VtabSavepoint(db, p1, iSavepoint); if( rc!=SQLITE_OK ) goto abort_due_to_error; } @@ -73104,7 +73818,7 @@ case OP_AutoCommit: { }else{ db->autoCommit = (u8)desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); db->autoCommit = (u8)(1-desiredAutoCommit); p->rc = rc = SQLITE_BUSY; goto vdbe_return; @@ -73181,7 +73895,7 @@ case OP_Transaction: { if( pBt ){ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); if( rc==SQLITE_BUSY ){ - p->pc = pc; + p->pc = (int)(pOp - aOp); p->rc = rc = SQLITE_BUSY; goto vdbe_return; } @@ -73211,7 +73925,12 @@ case OP_Transaction: { p->nStmtDefImmCons = db->nDeferredImmCons; } - /* Gather the schema version number for checking */ + /* Gather the schema version number for checking: + ** IMPLEMENTATION-OF: R-32195-19465 The schema version is used by SQLite + ** each time a query is executed to ensure that the internal cache of the + ** schema used when compiling the SQL query matches the schema of the + ** database against which the compiled query is actually executed. + */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ @@ -73255,7 +73974,7 @@ case OP_Transaction: { ** must be started or there must be an open cursor) before ** executing this instruction. */ -case OP_ReadCookie: { /* out2-prerelease */ +case OP_ReadCookie: { /* out2 */ int iMeta; int iDb; int iCookie; @@ -73269,6 +73988,7 @@ case OP_ReadCookie: { /* out2-prerelease */ assert( DbMaskTest(p->btreeMask, iDb) ); sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); + pOut = out2Prerelease(p, pOp); pOut->u.i = iMeta; break; } @@ -73379,31 +74099,29 @@ case OP_SetCookie: { /* in3 */ ** See also OpenRead. */ case OP_ReopenIdx: { + int nField; + KeyInfo *pKeyInfo; + int p2; + int iDb; + int wrFlag; + Btree *pX; VdbeCursor *pCur; + Db *pDb; - assert( pOp->p5==0 ); + assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); assert( pOp->p4type==P4_KEYINFO ); pCur = p->apCsr[pOp->p1]; if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){ assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */ - break; + goto open_cursor_set_hints; } /* If the cursor is not currently open or is open on a different ** index, then fall through into OP_OpenRead to force a reopen */ -} case OP_OpenRead: -case OP_OpenWrite: { - int nField; - KeyInfo *pKeyInfo; - int p2; - int iDb; - int wrFlag; - Btree *pX; - VdbeCursor *pCur; - Db *pDb; +case OP_OpenWrite: - assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 ); - assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 ); + assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR|OPFLAG_SEEKEQ))==pOp->p5 ); + assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ ); assert( p->bIsReader ); assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx || p->readOnly==0 ); @@ -73466,14 +74184,17 @@ case OP_OpenWrite: { pCur->pgnoRoot = p2; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; - assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); - sqlite3BtreeCursorHints(pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR)); - /* Set the VdbeCursor.isTable variable. Previous versions of ** SQLite used to check if the root-page flags were sane at this point ** and report database corruption if they were not, but this check has ** since moved into the btree layer. */ pCur->isTable = pOp->p4type!=P4_KEYINFO; + +open_cursor_set_hints: + assert( OPFLAG_BULKCSR==BTREE_BULKLOAD ); + assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ ); + sqlite3BtreeCursorHints(pCur->pCursor, + (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ))); break; } @@ -73589,7 +74310,7 @@ case OP_SequenceTest: { pC = p->apCsr[pOp->p1]; assert( pC->pSorter ); if( (pC->seqCount++)==0 ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -73734,6 +74455,22 @@ case OP_SeekGT: { /* jump, in3 */ #ifdef SQLITE_DEBUG pC->seekOp = pOp->opcode; #endif + + /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and + ** OP_SeekLE opcodes are allowed, and these must be immediately followed + ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key. + */ +#ifdef SQLITE_DEBUG + if( sqlite3BtreeCursorHasHint(pC->pCursor, BTREE_SEEK_EQ) ){ + assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE ); + assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT ); + assert( pOp[1].p1==pOp[0].p1 ); + assert( pOp[1].p2==pOp[0].p2 ); + assert( pOp[1].p3==pOp[0].p3 ); + assert( pOp[1].p4.i==pOp[0].p4.i ); + } +#endif + if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do @@ -73750,7 +74487,7 @@ case OP_SeekGT: { /* jump, in3 */ if( (pIn3->flags & MEM_Real)==0 ){ /* If the P3 value cannot be converted into any kind of a number, ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + VdbeBranchTaken(1,2); goto jump_to_p2; break; } @@ -73841,7 +74578,7 @@ case OP_SeekGT: { /* jump, in3 */ assert( pOp->p2>0 ); VdbeBranchTaken(res!=0,2); if( res ){ - pc = pOp->p2 - 1; + goto jump_to_p2; } break; } @@ -73935,6 +74672,7 @@ case OP_NoConflict: /* jump, in3 */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ int alreadyExists; + int takeJump; int ii; VdbeCursor *pC; int res; @@ -73957,7 +74695,7 @@ case OP_Found: { /* jump, in3 */ pIn3 = &aMem[pOp->p3]; assert( pC->pCursor!=0 ); assert( pC->isTable==0 ); - pFree = 0; /* Not needed. Only used to suppress a compiler warning. */ + pFree = 0; if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; @@ -73980,21 +74718,20 @@ case OP_Found: { /* jump, in3 */ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); } pIdxKey->default_rc = 0; + takeJump = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ for(ii=0; ii<pIdxKey->nField; ii++){ if( pIdxKey->aMem[ii].flags & MEM_Null ){ - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + takeJump = 1; break; } } } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res); - if( pOp->p4.i==0 ){ - sqlite3DbFree(db, pFree); - } + sqlite3DbFree(db, pFree); if( rc!=SQLITE_OK ){ break; } @@ -74005,10 +74742,10 @@ case OP_Found: { /* jump, in3 */ pC->cacheStatus = CACHE_STALE; if( pOp->opcode==OP_Found ){ VdbeBranchTaken(alreadyExists!=0,2); - if( alreadyExists ) pc = pOp->p2 - 1; + if( alreadyExists ) goto jump_to_p2; }else{ - VdbeBranchTaken(alreadyExists==0,2); - if( !alreadyExists ) pc = pOp->p2 - 1; + VdbeBranchTaken(takeJump||alreadyExists==0,2); + if( takeJump || !alreadyExists ) goto jump_to_p2; } break; } @@ -74057,10 +74794,8 @@ case OP_NotExists: { /* jump, in3 */ pC->cacheStatus = CACHE_STALE; pC->deferredMoveto = 0; VdbeBranchTaken(res!=0,2); - if( res!=0 ){ - pc = pOp->p2 - 1; - } pC->seekResult = res; + if( res!=0 ) goto jump_to_p2; break; } @@ -74072,9 +74807,10 @@ case OP_NotExists: { /* jump, in3 */ ** The sequence number on the cursor is incremented after this ** instruction. */ -case OP_Sequence: { /* out2-prerelease */ +case OP_Sequence: { /* out2 */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( p->apCsr[pOp->p1]!=0 ); + pOut = out2Prerelease(p, pOp); pOut->u.i = p->apCsr[pOp->p1]->seqCount++; break; } @@ -74095,7 +74831,7 @@ case OP_Sequence: { /* out2-prerelease */ ** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ -case OP_NewRowid: { /* out2-prerelease */ +case OP_NewRowid: { /* out2 */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ @@ -74105,6 +74841,7 @@ case OP_NewRowid: { /* out2-prerelease */ v = 0; res = 0; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -74418,9 +75155,7 @@ case OP_SorterCompare: { res = 0; rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res); VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2-1; - } + if( res ) goto jump_to_p2; break; }; @@ -74549,12 +75284,13 @@ case OP_RowData: { ** be a separate OP_VRowid opcode for use with virtual tables, but this ** one opcode now works for both table types. */ -case OP_Rowid: { /* out2-prerelease */ +case OP_Rowid: { /* out2 */ VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -74607,7 +75343,7 @@ case OP_NullRow: { break; } -/* Opcode: Last P1 P2 * * * +/* Opcode: Last P1 P2 P3 * * ** ** The next use of the Rowid or Column or Prev instruction for P1 ** will refer to the last entry in the database table or index. @@ -74634,12 +75370,13 @@ case OP_Last: { /* jump */ pC->nullRow = (u8)res; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; + pC->seekResult = pOp->p3; #ifdef SQLITE_DEBUG pC->seekOp = OP_Last; #endif if( pOp->p2>0 ){ VdbeBranchTaken(res!=0,2); - if( res ) pc = pOp->p2 - 1; + if( res ) goto jump_to_p2; } break; } @@ -74703,9 +75440,7 @@ case OP_Rewind: { /* jump */ pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2<p->nOp ); VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2 - 1; - } + if( res ) goto jump_to_p2; break; } @@ -74816,11 +75551,11 @@ next_tail: VdbeBranchTaken(res==0,2); if( res==0 ){ pC->nullRow = 0; - pc = pOp->p2 - 1; p->aCounter[pOp->p5]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif + goto jump_to_p2_and_check_for_interrupt; }else{ pC->nullRow = 1; } @@ -74928,11 +75663,12 @@ case OP_IdxDelete: { ** ** See also: Rowid, MakeRecord. */ -case OP_IdxRowid: { /* out2-prerelease */ +case OP_IdxRowid: { /* out2 */ BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; + pOut = out2Prerelease(p, pOp); assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -75045,9 +75781,7 @@ case OP_IdxGE: { /* jump */ res++; } VdbeBranchTaken(res>0,2); - if( res>0 ){ - pc = pOp->p2 - 1 ; - } + if( res>0 ) goto jump_to_p2; break; } @@ -75071,32 +75805,18 @@ case OP_IdxGE: { /* jump */ ** ** See also: Clear */ -case OP_Destroy: { /* out2-prerelease */ +case OP_Destroy: { /* out2 */ int iMoved; - int iCnt; - Vdbe *pVdbe; int iDb; assert( p->readOnly==0 ); -#ifndef SQLITE_OMIT_VIRTUALTABLE - iCnt = 0; - for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){ - if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader - && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 - ){ - iCnt++; - } - } -#else - iCnt = db->nVdbeRead; -#endif + pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Null; - if( iCnt>1 ){ + if( db->nVdbeRead > db->nVDestroy+1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ iDb = pOp->p3; - assert( iCnt==1 ); assert( DbMaskTest(p->btreeMask, iDb) ); iMoved = 0; /* Not needed. Only to silence a warning. */ rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); @@ -75199,12 +75919,13 @@ case OP_ResetSorter: { ** ** See documentation on OP_CreateTable for additional information. */ -case OP_CreateIndex: /* out2-prerelease */ -case OP_CreateTable: { /* out2-prerelease */ +case OP_CreateIndex: /* out2 */ +case OP_CreateTable: { /* out2 */ int pgno; int flags; Db *pDb; + pOut = out2Prerelease(p, pOp); pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( DbMaskTest(p->btreeMask, pOp->p1) ); @@ -75430,12 +76151,12 @@ case OP_RowSetRead: { /* jump, in1, out3 */ ){ /* The boolean index is empty */ sqlite3VdbeMemSetNull(pIn1); - pc = pOp->p2 - 1; VdbeBranchTaken(1,2); + goto jump_to_p2_and_check_for_interrupt; }else{ /* A value was pulled from the index */ - sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); VdbeBranchTaken(0,2); + sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); } goto check_for_interrupt; } @@ -75486,10 +76207,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ if( iSet ){ exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i); VdbeBranchTaken(exists!=0,2); - if( exists ){ - pc = pOp->p2 - 1; - break; - } + if( exists ) goto jump_to_p2; } if( iSet>=0 ){ sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); @@ -75578,7 +76296,7 @@ case OP_Program: { /* jump */ pFrame->v = p; pFrame->nChildMem = nMem; pFrame->nChildCsr = pProgram->nCsr; - pFrame->pc = pc; + pFrame->pc = (int)(pOp - aOp); pFrame->aMem = p->aMem; pFrame->nMem = p->nMem; pFrame->apCsr = p->apCsr; @@ -75601,7 +76319,7 @@ case OP_Program: { /* jump */ pFrame = pRt->u.pFrame; assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem ); assert( pProgram->nCsr==pFrame->nChildCsr ); - assert( pc==pFrame->pc ); + assert( (int)(pOp - aOp)==pFrame->pc ); } p->nFrame++; @@ -75622,7 +76340,7 @@ case OP_Program: { /* jump */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS p->anExec = 0; #endif - pc = -1; + pOp = &aOp[-1]; memset(p->aOnceFlag, 0, p->nOnceFlag); break; @@ -75640,9 +76358,10 @@ case OP_Program: { /* jump */ ** the value of the P1 argument to the value of the P1 argument to the ** calling OP_Program instruction. */ -case OP_Param: { /* out2-prerelease */ +case OP_Param: { /* out2 */ VdbeFrame *pFrame; Mem *pIn; + pOut = out2Prerelease(p, pOp); pFrame = p->pFrame; pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); @@ -75686,10 +76405,10 @@ case OP_FkCounter: { case OP_FkIfZero: { /* jump */ if( pOp->p1 ){ VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2); - if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; + if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; }else{ VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2); - if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; + if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) goto jump_to_p2; } break; } @@ -75729,18 +76448,18 @@ case OP_MemMax: { /* in2 */ /* Opcode: IfPos P1 P2 * * * ** Synopsis: if r[P1]>0 goto P2 ** -** If the value of register P1 is 1 or greater, jump to P2. +** Register P1 must contain an integer. +** If the value of register P1 is 1 or greater, jump to P2 and +** add the literal value P3 to register P1. ** -** It is illegal to use this instruction on a register that does -** not contain an integer. An assertion fault will result if you try. +** If the initial value of register P1 is less than 1, then the +** value is unchanged and control passes through to the next instruction. */ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); VdbeBranchTaken( pIn1->u.i>0, 2); - if( pIn1->u.i>0 ){ - pc = pOp->p2 - 1; - } + if( pIn1->u.i>0 ) goto jump_to_p2; break; } @@ -75755,26 +76474,56 @@ case OP_IfNeg: { /* jump, in1 */ assert( pIn1->flags&MEM_Int ); pIn1->u.i += pOp->p3; VdbeBranchTaken(pIn1->u.i<0, 2); - if( pIn1->u.i<0 ){ - pc = pOp->p2 - 1; + if( pIn1->u.i<0 ) goto jump_to_p2; + break; +} + +/* Opcode: IfNotZero P1 P2 P3 * * +** Synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2 +** +** Register P1 must contain an integer. If the content of register P1 is +** initially nonzero, then add P3 to P1 and jump to P2. If register P1 is +** initially zero, leave it unchanged and fall through. +*/ +case OP_IfNotZero: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i<0, 2); + if( pIn1->u.i ){ + pIn1->u.i += pOp->p3; + goto jump_to_p2; } break; } -/* Opcode: IfZero P1 P2 P3 * * -** Synopsis: r[P1]+=P3, if r[P1]==0 goto P2 +/* Opcode: DecrJumpZero P1 P2 * * * +** Synopsis: if (--r[P1])==0 goto P2 ** -** The register P1 must contain an integer. Add literal P3 to the -** value in register P1. If the result is exactly 0, jump to P2. +** Register P1 must hold an integer. Decrement the value in register P1 +** then jump to P2 if the new value is exactly zero. */ -case OP_IfZero: { /* jump, in1 */ +case OP_DecrJumpZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); - pIn1->u.i += pOp->p3; + pIn1->u.i--; VdbeBranchTaken(pIn1->u.i==0, 2); - if( pIn1->u.i==0 ){ - pc = pOp->p2 - 1; - } + if( pIn1->u.i==0 ) goto jump_to_p2; + break; +} + + +/* Opcode: JumpZeroIncr P1 P2 * * * +** Synopsis: if (r[P1]++)==0 ) goto P2 +** +** The register P1 must contain an integer. If register P1 is initially +** zero, then jump to P2. Increment register P1 regardless of whether or +** not the jump is taken. +*/ +case OP_JumpZeroIncr: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i==0, 2); + if( (pIn1->u.i++)==0 ) goto jump_to_p2; break; } @@ -75816,7 +76565,7 @@ case OP_AggStep: { ctx.pOut = &t; ctx.isError = 0; ctx.pVdbe = p; - ctx.iOp = pc; + ctx.iOp = (int)(pOp - aOp); ctx.skipFlag = 0; (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ if( ctx.isError ){ @@ -75911,7 +76660,7 @@ case OP_Checkpoint: { ** ** Write a string containing the final journal-mode to register P2. */ -case OP_JournalMode: { /* out2-prerelease */ +case OP_JournalMode: { /* out2 */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ @@ -75920,6 +76669,7 @@ case OP_JournalMode: { /* out2-prerelease */ const char *zFilename; /* Name of database file for pPager */ #endif + pOut = out2Prerelease(p, pOp); eNew = pOp->p3; assert( eNew==PAGER_JOURNALMODE_DELETE || eNew==PAGER_JOURNALMODE_TRUNCATE @@ -75995,7 +76745,6 @@ case OP_JournalMode: { /* out2-prerelease */ } eNew = sqlite3PagerSetJournalMode(pPager, eNew); - pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; pOut->z = (char *)sqlite3JournalModename(eNew); pOut->n = sqlite3Strlen30(pOut->z); @@ -76036,8 +76785,8 @@ case OP_IncrVacuum: { /* jump */ rc = sqlite3BtreeIncrVacuum(pBt); VdbeBranchTaken(rc==SQLITE_DONE,2); if( rc==SQLITE_DONE ){ - pc = pOp->p2 - 1; rc = SQLITE_OK; + goto jump_to_p2; } break; } @@ -76115,13 +76864,29 @@ case OP_VBegin: { #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VCreate P1 * * P4 * +/* Opcode: VCreate P1 P2 * * * ** -** P4 is the name of a virtual table in database P1. Call the xCreate method -** for that table. +** P2 is a register that holds the name of a virtual table in database +** P1. Call the xCreate method for that table. */ case OP_VCreate: { - rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p4.z, &p->zErrMsg); + Mem sMem; /* For storing the record being decoded */ + const char *zTab; /* Name of the virtual table */ + + memset(&sMem, 0, sizeof(sMem)); + sMem.db = db; + /* Because P2 is always a static string, it is impossible for the + ** sqlite3VdbeMemCopy() to fail */ + assert( (aMem[pOp->p2].flags & MEM_Str)!=0 ); + assert( (aMem[pOp->p2].flags & MEM_Static)!=0 ); + rc = sqlite3VdbeMemCopy(&sMem, &aMem[pOp->p2]); + assert( rc==SQLITE_OK ); + zTab = (const char*)sqlite3_value_text(&sMem); + assert( zTab || db->mallocFailed ); + if( zTab ){ + rc = sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg); + } + sqlite3VdbeMemRelease(&sMem); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76133,9 +76898,9 @@ case OP_VCreate: { ** of that table. */ case OP_VDestroy: { - p->inVtabMethod = 2; + db->nVDestroy++; rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); - p->inVtabMethod = 0; + db->nVDestroy--; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76151,14 +76916,17 @@ case OP_VOpen: { VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; - sqlite3_module *pModule; + const sqlite3_module *pModule; assert( p->bIsReader ); pCur = 0; pVtabCursor = 0; pVtab = pOp->p4.pVtab->pVtab; - pModule = (sqlite3_module *)pVtab->pModule; - assert(pVtab && pModule); + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + break; + } + pModule = pVtab->pModule; rc = pModule->xOpen(pVtab, &pVtabCursor); sqlite3VtabImportErrmsg(p, pVtab); if( SQLITE_OK==rc ){ @@ -76169,9 +76937,11 @@ case OP_VOpen: { pCur = allocateCursor(p, pOp->p1, 0, -1, 0); if( pCur ){ pCur->pVtabCursor = pVtabCursor; + pVtab->nRef++; }else{ - db->mallocFailed = 1; + assert( db->mallocFailed ); pModule->xClose(pVtabCursor); + goto no_mem; } } break; @@ -76227,27 +76997,19 @@ case OP_VFilter: { /* jump */ iQuery = (int)pQuery->u.i; /* Invoke the xFilter method */ - { - res = 0; - apArg = p->apArg; - for(i = 0; i<nArg; i++){ - apArg[i] = &pArgc[i+1]; - } - - p->inVtabMethod = 1; - rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); - p->inVtabMethod = 0; - sqlite3VtabImportErrmsg(p, pVtab); - if( rc==SQLITE_OK ){ - res = pModule->xEof(pVtabCursor); - } - VdbeBranchTaken(res!=0,2); - if( res ){ - pc = pOp->p2 - 1; - } + res = 0; + apArg = p->apArg; + for(i = 0; i<nArg; i++){ + apArg[i] = &pArgc[i+1]; + } + rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg); + sqlite3VtabImportErrmsg(p, pVtab); + if( rc==SQLITE_OK ){ + res = pModule->xEof(pVtabCursor); } pCur->nullRow = 0; - + VdbeBranchTaken(res!=0,2); + if( res ) goto jump_to_p2; break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -76326,9 +77088,7 @@ case OP_VNext: { /* jump */ ** data is available) and the error code returned when xColumn or ** some other method is next invoked on the save virtual table cursor. */ - p->inVtabMethod = 1; rc = pModule->xNext(pCur->pVtabCursor); - p->inVtabMethod = 0; sqlite3VtabImportErrmsg(p, pVtab); if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); @@ -76336,7 +77096,7 @@ case OP_VNext: { /* jump */ VdbeBranchTaken(!res,2); if( !res ){ /* If there is data, jump to P2 */ - pc = pOp->p2 - 1; + goto jump_to_p2_and_check_for_interrupt; } goto check_for_interrupt; } @@ -76403,7 +77163,7 @@ case OP_VRename: { */ case OP_VUpdate: { sqlite3_vtab *pVtab; - sqlite3_module *pModule; + const sqlite3_module *pModule; int nArg; int i; sqlite_int64 rowid; @@ -76415,7 +77175,11 @@ case OP_VUpdate: { ); assert( p->readOnly==0 ); pVtab = pOp->p4.pVtab->pVtab; - pModule = (sqlite3_module *)pVtab->pModule; + if( pVtab==0 || NEVER(pVtab->pModule==0) ){ + rc = SQLITE_LOCKED; + break; + } + pModule = pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ @@ -76455,7 +77219,8 @@ case OP_VUpdate: { ** ** Write the current number of pages in database P1 to memory cell P2. */ -case OP_Pagecount: { /* out2-prerelease */ +case OP_Pagecount: { /* out2 */ + pOut = out2Prerelease(p, pOp); pOut->u.i = sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt); break; } @@ -76471,10 +77236,11 @@ case OP_Pagecount: { /* out2-prerelease */ ** ** Store the maximum page count after the change in register P2. */ -case OP_MaxPgcnt: { /* out2-prerelease */ +case OP_MaxPgcnt: { /* out2 */ unsigned int newMax; Btree *pBt; + pOut = out2Prerelease(p, pOp); pBt = db->aDb[pOp->p1].pBt; newMax = 0; if( pOp->p3 ){ @@ -76503,9 +77269,6 @@ case OP_Init: { /* jump */ char *zTrace; char *z; - if( pOp->p2 ){ - pc = pOp->p2 - 1; - } #ifndef SQLITE_OMIT_TRACE if( db->xTrace && !p->doingRerun @@ -76533,6 +77296,7 @@ case OP_Init: { /* jump */ } #endif /* SQLITE_DEBUG */ #endif /* SQLITE_OMIT_TRACE */ + if( pOp->p2 ) goto jump_to_p2; break; } @@ -76564,8 +77328,8 @@ default: { /* This is really OP_Noop and OP_Explain */ #ifdef VDBE_PROFILE { u64 endTime = sqlite3Hwtime(); - if( endTime>start ) pOp->cycles += endTime - start; - pOp->cnt++; + if( endTime>start ) pOrigOp->cycles += endTime - start; + pOrigOp->cnt++; } #endif @@ -76575,16 +77339,16 @@ default: { /* This is really OP_Noop and OP_Explain */ ** the evaluator loop. So we can leave it out when NDEBUG is defined. */ #ifndef NDEBUG - assert( pc>=-1 && pc<p->nOp ); + assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] ); #ifdef SQLITE_DEBUG if( db->flags & SQLITE_VdbeTrace ){ if( rc!=0 ) printf("rc=%d\n",rc); - if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ - registerTrace(pOp->p2, &aMem[pOp->p2]); + if( pOrigOp->opflags & (OPFLG_OUT2) ){ + registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]); } - if( pOp->opflags & OPFLG_OUT3 ){ - registerTrace(pOp->p3, &aMem[pOp->p3]); + if( pOrigOp->opflags & OPFLG_OUT3 ){ + registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]); } } #endif /* SQLITE_DEBUG */ @@ -76599,7 +77363,7 @@ vdbe_error_halt: p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", - pc, p->zSql, p->zErrMsg); + (int)(pOp - aOp), p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; @@ -76762,7 +77526,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ /* ** Open a blob handle. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ @@ -76812,12 +77576,17 @@ SQLITE_API int sqlite3_blob_open( Incrblob *pBlob = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || ppBlob==0 || zTable==0 ){ + if( ppBlob==0 ){ return SQLITE_MISUSE_BKPT; } #endif - flags = !!flags; /* flags = (flags ? 1 : 0); */ *ppBlob = 0; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + flags = !!flags; /* flags = (flags ? 1 : 0); */ sqlite3_mutex_enter(db->mutex); @@ -76994,7 +77763,7 @@ blob_open_out: ** Close a blob handle that was previously created using ** sqlite3_blob_open(). */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; int rc; sqlite3 *db; @@ -77031,7 +77800,7 @@ static int blobReadWrite( sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; - if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ + if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; }else if( v==0 ){ @@ -77063,14 +77832,14 @@ static int blobReadWrite( /* ** Read data from a blob handle. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *pBlob, void *z, int n, int iOffset){ return blobReadWrite(pBlob, z, n, iOffset, sqlite3BtreeData); } /* ** Write data to a blob handle. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); } @@ -77080,7 +77849,7 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob ** so no mutex is required for access. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return (p && p->pStmt) ? p->nByte : 0; } @@ -77095,7 +77864,7 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) ** immediately return SQLITE_ABORT. */ -SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ +SQLITE_API int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ int rc; Incrblob *p = (Incrblob *)pBlob; sqlite3 *db; @@ -77420,6 +78189,7 @@ struct MergeEngine { ** after the thread has finished are not dire. So we don't worry about ** memory barriers and such here. */ +typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); struct SortSubtask { SQLiteThread *pThread; /* Background thread, if any */ int bDone; /* Set if thread is finished but not joined */ @@ -77427,10 +78197,12 @@ struct SortSubtask { UnpackedRecord *pUnpacked; /* Space to unpack a record */ SorterList list; /* List for thread to write to a PMA */ int nPMA; /* Number of PMAs currently in file */ + SorterCompare xCompare; /* Compare function to use */ SorterFile file; /* Temp file for level-0 PMAs */ SorterFile file2; /* Space for other PMAs */ }; + /* ** Main sorter structure. A single instance of this is allocated for each ** sorter cursor created by the VDBE. @@ -77457,9 +78229,13 @@ struct VdbeSorter { u8 bUseThreads; /* True to use background threads */ u8 iPrev; /* Previous thread used to flush PMA */ u8 nTask; /* Size of aTask[] array */ + u8 typeMask; SortSubtask aTask[1]; /* One or more subtasks */ }; +#define SORTER_TYPE_INTEGER 0x01 +#define SORTER_TYPE_TEXT 0x02 + /* ** An instance of the following object is used to read records out of a ** PMA, in sorted order. The next key to be read is cached in nKey/aKey. @@ -77871,33 +78647,163 @@ static int vdbePmaReaderInit( return rc; } +/* +** A version of vdbeSorterCompare() that assumes that it has already been +** determined that the first field of key1 is equal to the first field of +** key2. +*/ +static int vdbeSorterCompareTail( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + UnpackedRecord *r2 = pTask->pUnpacked; + if( *pbKey2Cached==0 ){ + sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; + } + return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1); +} /* ** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2, ** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences ** used by the comparison. Return the result of the comparison. ** -** Before returning, object (pTask->pUnpacked) is populated with the -** unpacked version of key2. Or, if pKey2 is passed a NULL pointer, then it -** is assumed that the (pTask->pUnpacked) structure already contains the -** unpacked key to use as key2. +** If IN/OUT parameter *pbKey2Cached is true when this function is called, +** it is assumed that (pTask->pUnpacked) contains the unpacked version +** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked +** version of key2 and *pbKey2Cached set to true before returning. ** ** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set ** to SQLITE_NOMEM. */ static int vdbeSorterCompare( SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ const void *pKey1, int nKey1, /* Left side of comparison */ const void *pKey2, int nKey2 /* Right side of comparison */ ){ UnpackedRecord *r2 = pTask->pUnpacked; - if( pKey2 ){ + if( !*pbKey2Cached ){ sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2); + *pbKey2Cached = 1; } return sqlite3VdbeRecordCompare(nKey1, pKey1, r2); } /* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is a TEXT value and that the collation +** sequence to compare them with is BINARY. +*/ +static int vdbeSorterCompareText( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + + int n1; + int n2; + int res; + + getVarint32(&p1[1], n1); n1 = (n1 - 13) / 2; + getVarint32(&p2[1], n2); n2 = (n2 - 13) / 2; + res = memcmp(v1, v2, MIN(n1, n2)); + if( res==0 ){ + res = n1 - n2; + } + + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); + } + }else{ + if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ + res = res * -1; + } + } + + return res; +} + +/* +** A specially optimized version of vdbeSorterCompare() that assumes that +** the first field of each key is an INTEGER value. +*/ +static int vdbeSorterCompareInt( + SortSubtask *pTask, /* Subtask context (for pKeyInfo) */ + int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */ + const void *pKey1, int nKey1, /* Left side of comparison */ + const void *pKey2, int nKey2 /* Right side of comparison */ +){ + const u8 * const p1 = (const u8 * const)pKey1; + const u8 * const p2 = (const u8 * const)pKey2; + const int s1 = p1[1]; /* Left hand serial type */ + const int s2 = p2[1]; /* Right hand serial type */ + const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */ + const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */ + int res; /* Return value */ + + assert( (s1>0 && s1<7) || s1==8 || s1==9 ); + assert( (s2>0 && s2<7) || s2==8 || s2==9 ); + + if( s1>7 && s2>7 ){ + res = s1 - s2; + }else{ + if( s1==s2 ){ + if( (*v1 ^ *v2) & 0x80 ){ + /* The two values have different signs */ + res = (*v1 & 0x80) ? -1 : +1; + }else{ + /* The two values have the same sign. Compare using memcmp(). */ + static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8 }; + int i; + res = 0; + for(i=0; i<aLen[s1]; i++){ + if( (res = v1[i] - v2[i]) ) break; + } + } + }else{ + if( s2>7 ){ + res = +1; + }else if( s1>7 ){ + res = -1; + }else{ + res = s1 - s2; + } + assert( res!=0 ); + + if( res>0 ){ + if( *v1 & 0x80 ) res = -1; + }else{ + if( *v2 & 0x80 ) res = +1; + } + } + } + + if( res==0 ){ + if( pTask->pSorter->pKeyInfo->nField>1 ){ + res = vdbeSorterCompareTail( + pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2 + ); + } + }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){ + res = res * -1; + } + + return res; +} + +/* ** Initialize the temporary index cursor just opened as a sorter cursor. ** ** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField) @@ -77964,9 +78870,13 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz); memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo); pKeyInfo->db = 0; - if( nField && nWorker==0 ) pKeyInfo->nField = nField; + if( nField && nWorker==0 ){ + pKeyInfo->nXField += (pKeyInfo->nField - nField); + pKeyInfo->nField = nField; + } pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt); pSorter->nTask = nWorker + 1; + pSorter->iPrev = nWorker-1; pSorter->bUseThreads = (pSorter->nTask>1); pSorter->db = db; for(i=0; i<pSorter->nTask; i++){ @@ -77992,6 +78902,12 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit( if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM; } } + + if( (pKeyInfo->nField+pKeyInfo->nXField)<13 + && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl) + ){ + pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT; + } } return rc; @@ -78016,30 +78932,24 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){ */ static void vdbeSortSubtaskCleanup(sqlite3 *db, SortSubtask *pTask){ sqlite3DbFree(db, pTask->pUnpacked); - pTask->pUnpacked = 0; #if SQLITE_MAX_WORKER_THREADS>0 /* pTask->list.aMemory can only be non-zero if it was handed memory ** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */ if( pTask->list.aMemory ){ sqlite3_free(pTask->list.aMemory); - pTask->list.aMemory = 0; }else #endif { assert( pTask->list.aMemory==0 ); vdbeSorterRecordFree(0, pTask->list.pList); } - pTask->list.pList = 0; if( pTask->file.pFd ){ sqlite3OsCloseFree(pTask->file.pFd); - pTask->file.pFd = 0; - pTask->file.iEof = 0; } if( pTask->file2.pFd ){ sqlite3OsCloseFree(pTask->file2.pFd); - pTask->file2.pFd = 0; - pTask->file2.iEof = 0; } + memset(pTask, 0, sizeof(SortSubtask)); } #ifdef SQLITE_DEBUG_SORTER_THREADS @@ -78219,6 +79129,7 @@ SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){ for(i=0; i<pSorter->nTask; i++){ SortSubtask *pTask = &pSorter->aTask[i]; vdbeSortSubtaskCleanup(db, pTask); + pTask->pSorter = pSorter; } if( pSorter->list.aMemory==0 ){ vdbeSorterRecordFree(0, pSorter->list.pList); @@ -78280,6 +79191,7 @@ static int vdbeSorterOpenTempFile( sqlite3_file **ppFd ){ int rc; + if( sqlite3FaultSim(202) ) return SQLITE_IOERR_ACCESS; rc = sqlite3OsOpenMalloc(db->pVfs, 0, ppFd, SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | @@ -78327,22 +79239,23 @@ static void vdbeSorterMerge( ){ SorterRecord *pFinal = 0; SorterRecord **pp = &pFinal; - void *pVal2 = p2 ? SRVAL(p2) : 0; + int bCached = 0; while( p1 && p2 ){ int res; - res = vdbeSorterCompare(pTask, SRVAL(p1), p1->nVal, pVal2, p2->nVal); + res = pTask->xCompare( + pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal + ); + if( res<=0 ){ *pp = p1; pp = &p1->u.pNext; p1 = p1->u.pNext; - pVal2 = 0; }else{ *pp = p2; - pp = &p2->u.pNext; + pp = &p2->u.pNext; p2 = p2->u.pNext; - if( p2==0 ) break; - pVal2 = SRVAL(p2); + bCached = 0; } } *pp = p1 ? p1 : p2; @@ -78350,6 +79263,19 @@ static void vdbeSorterMerge( } /* +** Return the SorterCompare function to compare values collected by the +** sorter object passed as the only argument. +*/ +static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){ + if( p->typeMask==SORTER_TYPE_INTEGER ){ + return vdbeSorterCompareInt; + }else if( p->typeMask==SORTER_TYPE_TEXT ){ + return vdbeSorterCompareText; + } + return vdbeSorterCompare; +} + +/* ** Sort the linked list of records headed at pTask->pList. Return ** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if ** an error occurs. @@ -78363,12 +79289,14 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){ rc = vdbeSortAllocUnpacked(pTask); if( rc!=SQLITE_OK ) return rc; + p = pList->pList; + pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter); + aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *)); if( !aSlot ){ return SQLITE_NOMEM; } - p = pList->pList; while( p ){ SorterRecord *pNext; if( pList->aMemory ){ @@ -78582,13 +79510,12 @@ static int vdbeMergeEngineStep( int i; /* Index of aTree[] to recalculate */ PmaReader *pReadr1; /* First PmaReader to compare */ PmaReader *pReadr2; /* Second PmaReader to compare */ - u8 *pKey2; /* To pReadr2->aKey, or 0 if record cached */ + int bCached = 0; /* Find the first two PmaReaders to compare. The one that was just ** advanced (iPrev) and the one next to it in the array. */ pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)]; pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)]; - pKey2 = pReadr2->aKey; for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){ /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */ @@ -78598,8 +79525,8 @@ static int vdbeMergeEngineStep( }else if( pReadr2->pFd==0 ){ iRes = -1; }else{ - iRes = vdbeSorterCompare(pTask, - pReadr1->aKey, pReadr1->nKey, pKey2, pReadr2->nKey + iRes = pTask->xCompare(pTask, &bCached, + pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey ); } @@ -78621,9 +79548,9 @@ static int vdbeMergeEngineStep( if( iRes<0 || (iRes==0 && pReadr1<pReadr2) ){ pMerger->aTree[i] = (int)(pReadr1 - pMerger->aReadr); pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; - pKey2 = pReadr2->aKey; + bCached = 0; }else{ - if( pReadr1->pFd ) pKey2 = 0; + if( pReadr1->pFd ) bCached = 0; pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr); pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ]; } @@ -78730,6 +79657,16 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( int bFlush; /* True to flush contents of memory to PMA */ int nReq; /* Bytes of memory required */ int nPMA; /* Bytes of PMA space required */ + int t; /* serial type of first record field */ + + getVarint32((const u8*)&pVal->z[1], t); + if( t>0 && t<10 && t!=7 ){ + pSorter->typeMask &= SORTER_TYPE_INTEGER; + }else if( t>10 && (t & 0x01) ){ + pSorter->typeMask &= SORTER_TYPE_TEXT; + }else{ + pSorter->typeMask = 0; + } assert( pSorter ); @@ -78995,10 +79932,12 @@ static void vdbeMergeEngineCompare( }else if( p2->pFd==0 ){ iRes = i1; }else{ + SortSubtask *pTask = pMerger->pTask; + int bCached = 0; int res; - assert( pMerger->pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ - res = vdbeSorterCompare( - pMerger->pTask, p1->aKey, p1->nKey, p2->aKey, p2->nKey + assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */ + res = pTask->xCompare( + pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey ); if( res<=0 ){ iRes = i1; @@ -79022,11 +79961,12 @@ static void vdbeMergeEngineCompare( #define INCRINIT_TASK 1 #define INCRINIT_ROOT 2 -/* Forward reference. -** The vdbeIncrMergeInit() and vdbePmaReaderIncrMergeInit() routines call each -** other (when building a merge tree). +/* +** Forward reference required as the vdbeIncrMergeInit() and +** vdbePmaReaderIncrInit() routines are called mutually recursively when +** building a merge tree. */ -static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode); +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode); /* ** Initialize the MergeEngine object passed as the second argument. Once this @@ -79073,7 +80013,7 @@ static int vdbeMergeEngineInit( ** better advantage of multi-processor hardware. */ rc = vdbePmaReaderNext(&pMerger->aReadr[nTree-i-1]); }else{ - rc = vdbePmaReaderIncrMergeInit(&pMerger->aReadr[i], INCRINIT_NORMAL); + rc = vdbePmaReaderIncrInit(&pMerger->aReadr[i], INCRINIT_NORMAL); } if( rc!=SQLITE_OK ) return rc; } @@ -79085,17 +80025,15 @@ static int vdbeMergeEngineInit( } /* -** Initialize the IncrMerge field of a PmaReader. -** -** If the PmaReader passed as the first argument is not an incremental-reader -** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it serves -** to open and/or initialize the temp file related fields of the IncrMerge +** The PmaReader passed as the first argument is guaranteed to be an +** incremental-reader (pReadr->pIncr!=0). This function serves to open +** and/or initialize the temp file related fields of the IncrMerge ** object at (pReadr->pIncr). ** ** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders -** in the sub-tree headed by pReadr are also initialized. Data is then loaded -** into the buffers belonging to pReadr and it is set to -** point to the first key in its range. +** in the sub-tree headed by pReadr are also initialized. Data is then +** loaded into the buffers belonging to pReadr and it is set to point to +** the first key in its range. ** ** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed ** to be a multi-threaded PmaReader and this function is being called in a @@ -79122,59 +80060,62 @@ static int vdbeMergeEngineInit( static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ int rc = SQLITE_OK; IncrMerger *pIncr = pReadr->pIncr; + SortSubtask *pTask = pIncr->pTask; + sqlite3 *db = pTask->pSorter->db; /* eMode is always INCRINIT_NORMAL in single-threaded mode */ assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL ); - if( pIncr ){ - SortSubtask *pTask = pIncr->pTask; - sqlite3 *db = pTask->pSorter->db; - - rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); + rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode); - /* Set up the required files for pIncr. A multi-theaded IncrMerge object - ** requires two temp files to itself, whereas a single-threaded object - ** only requires a region of pTask->file2. */ - if( rc==SQLITE_OK ){ - int mxSz = pIncr->mxSz; + /* Set up the required files for pIncr. A multi-theaded IncrMerge object + ** requires two temp files to itself, whereas a single-threaded object + ** only requires a region of pTask->file2. */ + if( rc==SQLITE_OK ){ + int mxSz = pIncr->mxSz; #if SQLITE_MAX_WORKER_THREADS>0 - if( pIncr->bUseThread ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); - if( rc==SQLITE_OK ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); - } - }else + if( pIncr->bUseThread ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); + if( rc==SQLITE_OK ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); + } + }else #endif - /*if( !pIncr->bUseThread )*/{ - if( pTask->file2.pFd==0 ){ - assert( pTask->file2.iEof>0 ); - rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); - pTask->file2.iEof = 0; - } - if( rc==SQLITE_OK ){ - pIncr->aFile[1].pFd = pTask->file2.pFd; - pIncr->iStartOff = pTask->file2.iEof; - pTask->file2.iEof += mxSz; - } + /*if( !pIncr->bUseThread )*/{ + if( pTask->file2.pFd==0 ){ + assert( pTask->file2.iEof>0 ); + rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); + pTask->file2.iEof = 0; + } + if( rc==SQLITE_OK ){ + pIncr->aFile[1].pFd = pTask->file2.pFd; + pIncr->iStartOff = pTask->file2.iEof; + pTask->file2.iEof += mxSz; } } + } #if SQLITE_MAX_WORKER_THREADS>0 - if( rc==SQLITE_OK && pIncr->bUseThread ){ - /* Use the current thread to populate aFile[1], even though this - ** PmaReader is multi-threaded. The reason being that this function - ** is already running in background thread pIncr->pTask->thread. */ - assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); - rc = vdbeIncrPopulate(pIncr); - } + if( rc==SQLITE_OK && pIncr->bUseThread ){ + /* Use the current thread to populate aFile[1], even though this + ** PmaReader is multi-threaded. If this is an INCRINIT_TASK object, + ** then this function is already running in background thread + ** pIncr->pTask->thread. + ** + ** If this is the INCRINIT_ROOT object, then it is running in the + ** main VDBE thread. But that is Ok, as that thread cannot return + ** control to the VDBE or proceed with anything useful until the + ** first results are ready from this merger object anyway. + */ + assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); + rc = vdbeIncrPopulate(pIncr); + } #endif - if( rc==SQLITE_OK - && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) - ){ - rc = vdbePmaReaderNext(pReadr); - } + if( rc==SQLITE_OK && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) ){ + rc = vdbePmaReaderNext(pReadr); } + return rc; } @@ -79183,7 +80124,7 @@ static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){ ** The main routine for vdbePmaReaderIncrMergeInit() operations run in ** background threads. */ -static void *vdbePmaReaderBgInit(void *pCtx){ +static void *vdbePmaReaderBgIncrInit(void *pCtx){ PmaReader *pReader = (PmaReader*)pCtx; void *pRet = SQLITE_INT_TO_PTR( vdbePmaReaderIncrMergeInit(pReader,INCRINIT_TASK) @@ -79191,20 +80132,36 @@ static void *vdbePmaReaderBgInit(void *pCtx){ pReader->pIncr->pTask->bDone = 1; return pRet; } +#endif /* -** Use a background thread to invoke vdbePmaReaderIncrMergeInit(INCRINIT_TASK) -** on the PmaReader object passed as the first argument. -** -** This call will initialize the various fields of the pReadr->pIncr -** structure and, if it is a multi-threaded IncrMerger, launch a -** background thread to populate aFile[1]. +** If the PmaReader passed as the first argument is not an incremental-reader +** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes +** the vdbePmaReaderIncrMergeInit() function with the parameters passed to +** this routine to initialize the incremental merge. +** +** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1), +** then a background thread is launched to call vdbePmaReaderIncrMergeInit(). +** Or, if the IncrMerger is single threaded, the same function is called +** using the current thread. */ -static int vdbePmaReaderBgIncrInit(PmaReader *pReadr){ - void *pCtx = (void*)pReadr; - return vdbeSorterCreateThread(pReadr->pIncr->pTask, vdbePmaReaderBgInit, pCtx); -} +static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){ + IncrMerger *pIncr = pReadr->pIncr; /* Incremental merger */ + int rc = SQLITE_OK; /* Return code */ + if( pIncr ){ +#if SQLITE_MAX_WORKER_THREADS>0 + assert( pIncr->bUseThread==0 || eMode==INCRINIT_TASK ); + if( pIncr->bUseThread ){ + void *pCtx = (void*)pReadr; + rc = vdbeSorterCreateThread(pIncr->pTask, vdbePmaReaderBgIncrInit, pCtx); + }else #endif + { + rc = vdbePmaReaderIncrMergeInit(pReadr, eMode); + } + } + return rc; +} /* ** Allocate a new MergeEngine object to merge the contents of nPMA level-0 @@ -79416,6 +80373,11 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ MergeEngine *pMain = 0; #if SQLITE_MAX_WORKER_THREADS sqlite3 *db = pTask0->pSorter->db; + int i; + SorterCompare xCompare = vdbeSorterGetCompare(pSorter); + for(i=0; i<pSorter->nTask; i++){ + pSorter->aTask[i].xCompare = xCompare; + } #endif rc = vdbeSorterMergeTreeBuild(pSorter, &pMain); @@ -79444,15 +80406,21 @@ static int vdbeSorterSetupMerge(VdbeSorter *pSorter){ } } for(iTask=0; rc==SQLITE_OK && iTask<pSorter->nTask; iTask++){ + /* Check that: + ** + ** a) The incremental merge object is configured to use the + ** right task, and + ** b) If it is using task (nTask-1), it is configured to run + ** in single-threaded mode. This is important, as the + ** root merge (INCRINIT_ROOT) will be using the same task + ** object. + */ PmaReader *p = &pMain->aReadr[iTask]; - assert( p->pIncr==0 || p->pIncr->pTask==&pSorter->aTask[iTask] ); - if( p->pIncr ){ - if( iTask==pSorter->nTask-1 ){ - rc = vdbePmaReaderIncrMergeInit(p, INCRINIT_TASK); - }else{ - rc = vdbePmaReaderBgIncrInit(p); - } - } + assert( p->pIncr==0 || ( + (p->pIncr->pTask==&pSorter->aTask[iTask]) /* a */ + && (iTask!=pSorter->nTask-1 || p->pIncr->bUseThread==0) /* b */ + )); + rc = vdbePmaReaderIncrInit(p, INCRINIT_TASK); } } pMain = 0; @@ -80407,7 +81375,7 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){ ** SELECT a+b, c+d FROM t1 ORDER BY (a+b) COLLATE nocase; ** ** The nSubquery parameter specifies how many levels of subquery the -** alias is removed from the original expression. The usually value is +** alias is removed from the original expression. The usual value is ** zero but it might be more if the alias is contained within a subquery ** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION ** structures must be increased by the nSubquery amount. @@ -80427,7 +81395,6 @@ static void resolveAlias( assert( iCol>=0 && iCol<pEList->nExpr ); pOrig = pEList->a[iCol].pExpr; assert( pOrig!=0 ); - assert( pOrig->flags & EP_Resolved ); db = pParse->db; pDup = sqlite3ExprDup(db, pOrig, 0); if( pDup==0 ) return; @@ -80575,9 +81542,10 @@ static int lookupName( testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IsCheck ); if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){ - /* Silently ignore database qualifiers inside CHECK constraints and partial - ** indices. Do not raise errors because that might break legacy and - ** because it does not hurt anything to just ignore the database name. */ + /* Silently ignore database qualifiers inside CHECK constraints and + ** partial indices. Do not raise errors because that might break + ** legacy and because it does not hurt anything to just ignore the + ** database name. */ zDb = 0; }else{ for(i=0; i<db->nDb; i++){ @@ -80648,7 +81616,8 @@ static int lookupName( if( pMatch ){ pExpr->iTable = pMatch->iCursor; pExpr->pTab = pMatch->pTab; - assert( (pMatch->jointype & JT_RIGHT)==0 ); /* RIGHT JOIN not (yet) supported */ + /* RIGHT JOIN not (yet) supported */ + assert( (pMatch->jointype & JT_RIGHT)==0 ); if( (pMatch->jointype & JT_LEFT)!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } @@ -80969,7 +81938,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pExpr->affinity = SQLITE_AFF_INTEGER; break; } -#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */ +#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) + && !defined(SQLITE_OMIT_SUBQUERY) */ /* A lone identifier is the name of a column. */ @@ -81034,19 +82004,20 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( n==2 ){ pExpr->iTable = exprProbability(pList->a[1].pExpr); if( pExpr->iTable<0 ){ - sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a " - "constant between 0.0 and 1.0"); + sqlite3ErrorMsg(pParse, + "second argument to likelihood() must be a " + "constant between 0.0 and 1.0"); pNC->nErr++; } }else{ - /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to - ** likelihood(X, 0.0625). - ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for - ** likelihood(X,0.0625). - ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand for - ** likelihood(X,0.9375). - ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent to - ** likelihood(X,0.9375). */ + /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is + ** equivalent to likelihood(X, 0.0625). + ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is + ** short-hand for likelihood(X,0.0625). + ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand + ** for likelihood(X,0.9375). + ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent + ** to likelihood(X,0.9375). */ /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */ pExpr->iTable = pDef->zName[0]=='u' ? 8388608 : 125829120; } @@ -81063,7 +82034,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ return WRC_Prune; } #endif - if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant); + if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ){ + ExprSetProperty(pExpr,EP_ConstFunc); + } } if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); @@ -81315,9 +82288,11 @@ static int resolveCompoundOrderBy( if( pItem->pExpr==pE ){ pItem->pExpr = pNew; }else{ - assert( pItem->pExpr->op==TK_COLLATE ); - assert( pItem->pExpr->pLeft==pE ); - pItem->pExpr->pLeft = pNew; + Expr *pParent = pItem->pExpr; + assert( pParent->op==TK_COLLATE ); + while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; + assert( pParent->pLeft==pE ); + pParent->pLeft = pNew; } sqlite3ExprDelete(db, pE); pItem->u.x.iOrderByCol = (u16)iCol; @@ -81374,7 +82349,8 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0); + resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, + zType,0); } } return 0; @@ -81507,6 +82483,20 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ sqlite3ResolveExprNames(&sNC, p->pOffset) ){ return WRC_Abort; } + + /* If the SF_Converted flags is set, then this Select object was + ** was created by the convertCompoundSelectToSubquery() function. + ** In this case the ORDER BY clause (p->pOrderBy) should be resolved + ** as if it were part of the sub-query, not the parent. This block + ** moves the pOrderBy down to the sub-query. It will be moved back + ** after the names have been resolved. */ + if( p->selFlags & SF_Converted ){ + Select *pSub = p->pSrc->a[0].pSelect; + assert( p->pSrc->nSrc==1 && p->pOrderBy ); + assert( pSub->pPrior && pSub->pOrderBy==0 ); + pSub->pOrderBy = p->pOrderBy; + p->pOrderBy = 0; + } /* Recursively resolve names in all subqueries */ @@ -81589,12 +82579,30 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ sNC.pNext = 0; sNC.ncFlags |= NC_AllowAgg; + /* If this is a converted compound query, move the ORDER BY clause from + ** the sub-query back to the parent query. At this point each term + ** within the ORDER BY clause has been transformed to an integer value. + ** These integers will be replaced by copies of the corresponding result + ** set expressions by the call to resolveOrderGroupBy() below. */ + if( p->selFlags & SF_Converted ){ + Select *pSub = p->pSrc->a[0].pSelect; + p->pOrderBy = pSub->pOrderBy; + pSub->pOrderBy = 0; + } + /* Process the ORDER BY clause for singleton SELECT statements. ** The ORDER BY clause for compounds SELECT statements is handled ** below, after all of the result-sets for all of the elements of ** the compound have been resolved. + ** + ** If there is an ORDER BY clause on a term of a compound-select other + ** than the right-most term, then that is a syntax error. But the error + ** is not detected until much later, and so we need to go ahead and + ** resolve those symbols on the incorrect ORDER BY for consistency. */ - if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){ + if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */ + && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") + ){ return WRC_Abort; } if( db->mallocFailed ){ @@ -81864,10 +82872,11 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken( Parse *pParse, /* Parsing context */ Expr *pExpr, /* Add the "COLLATE" clause to this expression */ - const Token *pCollName /* Name of collating sequence */ + const Token *pCollName, /* Name of collating sequence */ + int dequote /* True to dequote pCollName */ ){ if( pCollName->n>0 ){ - Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1); + Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote); if( pNew ){ pNew->pLeft = pExpr; pNew->flags |= EP_Collate|EP_Skip; @@ -81881,7 +82890,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con assert( zC!=0 ); s.z = zC; s.n = sqlite3Strlen30(s.z); - return sqlite3ExprAddCollateToken(pParse, pExpr, &s); + return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0); } /* @@ -81927,9 +82936,9 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } - if( p->pTab!=0 - && (op==TK_AGG_COLUMN || op==TK_COLUMN + if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER) + && p->pTab!=0 ){ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ @@ -81941,10 +82950,25 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ break; } if( p->flags & EP_Collate ){ - if( ALWAYS(p->pLeft) && (p->pLeft->flags & EP_Collate)!=0 ){ + if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){ p = p->pLeft; }else{ - p = p->pRight; + Expr *pNext = p->pRight; + /* The Expr.x union is never used at the same time as Expr.pRight */ + assert( p->x.pList==0 || p->pRight==0 ); + /* p->flags holds EP_Collate and p->pLeft->flags does not. And + ** p->x.pSelect cannot. So if p->x.pLeft exists, it must hold at + ** least one EP_Collate. Thus the following two ALWAYS. */ + if( p->x.pList!=0 && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) ){ + int i; + for(i=0; ALWAYS(i<p->x.pList->nExpr); i++){ + if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){ + pNext = p->x.pList->a[i].pExpr; + break; + } + } + } + p = pNext; } }else{ break; @@ -82150,6 +83174,9 @@ static void heightOfSelect(Select *p, int *pnHeight){ ** Expr.pSelect member has a height of 1. Any other expression ** has a height equal to the maximum height of any other ** referenced Expr plus one. +** +** Also propagate EP_Propagate flags up from Expr.x.pList to Expr.flags, +** if appropriate. */ static void exprSetHeight(Expr *p){ int nHeight = 0; @@ -82157,8 +83184,9 @@ static void exprSetHeight(Expr *p){ heightOfExpr(p->pRight, &nHeight); if( ExprHasProperty(p, EP_xIsSelect) ){ heightOfSelect(p->x.pSelect, &nHeight); - }else{ + }else if( p->x.pList ){ heightOfExprList(p->x.pList, &nHeight); + p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); } p->nHeight = nHeight + 1; } @@ -82167,8 +83195,12 @@ static void exprSetHeight(Expr *p){ ** Set the Expr.nHeight variable using the exprSetHeight() function. If ** the height is greater than the maximum allowed expression depth, ** leave an error in pParse. +** +** Also propagate all EP_Propagate flags from the Expr.x.pList into +** Expr.flags. */ -SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p){ +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + if( pParse->nErr ) return; exprSetHeight(p); sqlite3ExprCheckHeight(pParse, p->nHeight); } @@ -82182,8 +83214,17 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){ heightOfSelect(p, &nHeight); return nHeight; } -#else - #define exprSetHeight(y) +#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */ +/* +** Propagate all EP_Propagate flags from the Expr.x.pList into +** Expr.flags. +*/ +SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){ + if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){ + p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList); + } +} +#define exprSetHeight(y) #endif /* SQLITE_MAX_EXPR_DEPTH>0 */ /* @@ -82285,11 +83326,11 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees( }else{ if( pRight ){ pRoot->pRight = pRight; - pRoot->flags |= EP_Collate & pRight->flags; + pRoot->flags |= EP_Propagate & pRight->flags; } if( pLeft ){ pRoot->pLeft = pLeft; - pRoot->flags |= EP_Collate & pLeft->flags; + pRoot->flags |= EP_Propagate & pLeft->flags; } exprSetHeight(pRoot); } @@ -82389,7 +83430,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token * } pNew->x.pList = pList; assert( !ExprHasProperty(pNew, EP_xIsSelect) ); - sqlite3ExprSetHeight(pParse, pNew); + sqlite3ExprSetHeightAndFlags(pParse, pNew); return pNew; } @@ -83005,6 +84046,22 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ } /* +** Return the bitwise-OR of all Expr.flags fields in the given +** ExprList. +*/ +SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){ + int i; + u32 m = 0; + if( pList ){ + for(i=0; i<pList->nExpr; i++){ + Expr *pExpr = pList->a[i].pExpr; + if( ALWAYS(pExpr) ) m |= pExpr->flags; + } + } + return m; +} + +/* ** These routines are Walker callbacks used to check expressions to ** see if they are "constant" for some definition of constant. The ** Walker.eCode value determines the type of "constant" we are looking @@ -83044,7 +84101,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ ** and either pWalker->eCode==4 or 5 or the function has the ** SQLITE_FUNC_CONST flag. */ case TK_FUNCTION: - if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_Constant) ){ + if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc) ){ return WRC_Continue; }else{ pWalker->eCode = 0; @@ -83438,7 +84495,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int ** ephemeral table. */ p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0); - if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){ + if( pParse->nErr==0 && isCandidateForInOpt(p) ){ sqlite3 *db = pParse->db; /* Database connection */ Table *pTab; /* Table <table>. */ Expr *pExpr; /* Expression <column> */ @@ -83763,6 +84820,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[1]); pSel->iLimit = 0; + pSel->selFlags &= ~SF_MultiValue; if( sqlite3Select(pParse, pSel, &dest) ){ return 0; } @@ -84051,7 +85109,8 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int int idxLru; struct yColCache *p; - assert( iReg>0 ); /* Register numbers are always positive */ + /* Unless an error has occurred, register numbers are always positive. */ + assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed ); assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */ /* The SQLITE_ColumnCache flag disables the column cache. This is used @@ -85127,7 +86186,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m break; } case TK_ID: { - sqlite3TreeViewLine(pView,"ID %Q", pExpr->u.zToken); + sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); break; } #ifndef SQLITE_OMIT_CAST @@ -85762,7 +86821,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){ if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2; if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - if( ALWAYS((combinedFlags & EP_Reduced)==0) ){ + if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){ if( pA->iColumn!=pB->iColumn ) return 2; if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; @@ -86294,6 +87353,7 @@ static void renameParentFunc( n = sqlite3GetToken(z, &token); }while( token==TK_SPACE ); + if( token==TK_ILLEGAL ) break; zParent = sqlite3DbStrNDup(db, (const char *)z, n); if( zParent==0 ) break; sqlite3Dequote(zParent); @@ -86858,7 +87918,10 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ */ if( pDflt ){ sqlite3_value *pVal = 0; - if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ + int rc; + rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal); + assert( rc==SQLITE_OK || rc==SQLITE_NOMEM ); + if( rc!=SQLITE_OK ){ db->mallocFailed = 1; return; } @@ -88517,14 +89580,17 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ z = argv[2]; if( pIndex ){ + tRowcnt *aiRowEst = 0; int nCol = pIndex->nKeyCol+1; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero( - sizeof(tRowcnt) * nCol - ); - if( aiRowEst==0 ) pInfo->db->mallocFailed = 1; -#else - tRowcnt * const aiRowEst = 0; + /* Index.aiRowEst may already be set here if there are duplicate + ** sqlite_stat1 entries for this index. In that case just clobber + ** the old data with the new instead of allocating a new array. */ + if( pIndex->aiRowEst==0 ){ + pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol); + if( pIndex->aiRowEst==0 ) pInfo->db->mallocFailed = 1; + } + aiRowEst = pIndex->aiRowEst; #endif pIndex->bUnordered = 0; decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); @@ -89080,7 +90146,7 @@ static void attachFunc( case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ + if( nKey>0 || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); } break; @@ -89187,7 +90253,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetAllSchemasOfConnection(db); + sqlite3CollapseDatabaseArray(db); return; detach_error: @@ -89221,7 +90287,6 @@ static void codeAttach( SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) || SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey)) ){ - pParse->nErr++; goto attach_end; } @@ -89543,7 +90608,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep( ** Setting the auth function to NULL disables this hook. The default ** setting of the auth function is NULL. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3 *db, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pArg @@ -89880,9 +90945,11 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ assert( pParse->pToplevel==0 ); db = pParse->db; - if( db->mallocFailed ) return; if( pParse->nested ) return; - if( pParse->nErr ) return; + if( db->mallocFailed || pParse->nErr ){ + if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; + return; + } /* Begin by generating some termination code at the end of the ** vdbe program @@ -89964,7 +91031,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ /* Get the VDBE program ready for execution */ - if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){ + if( v && pParse->nErr==0 && !db->mallocFailed ){ assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ @@ -90046,10 +91113,6 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha Table *p = 0; int i; -#ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || zName==0 ) return 0; -#endif - /* All mutexes are required for schema access. Make sure we hold them. */ assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); #if SQLITE_USER_AUTHENTICATION @@ -90503,14 +91566,12 @@ SQLITE_PRIVATE int sqlite3TwoPartName( if( ALWAYS(pName2!=0) && pName2->n>0 ){ if( db->init.busy ) { sqlite3ErrorMsg(pParse, "corrupt database"); - pParse->nErr++; return -1; } *pUnqual = pName2; iDb = sqlite3FindDb(db, pName1); if( iDb<0 ){ sqlite3ErrorMsg(pParse, "unknown database %T", pName1); - pParse->nErr++; return -1; } }else{ @@ -90669,7 +91730,7 @@ SQLITE_PRIVATE void sqlite3StartTable( if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); }else{ - assert( !db->init.busy ); + assert( !db->init.busy || CORRUPT_DB ); sqlite3CodeVerifySchema(pParse, iDb); } goto begin_table_error; @@ -90958,7 +92019,8 @@ SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){ p = pParse->pNewTable; if( p==0 || NEVER(p->nCol<1) ) return; pCol = &p->aCol[p->nCol-1]; - assert( pCol->zType==0 ); + assert( pCol->zType==0 || CORRUPT_DB ); + sqlite3DbFree(pParse->db, pCol->zType); pCol->zType = sqlite3NameFromToken(pParse->db, pType); pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst); } @@ -91469,11 +92531,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ assert( pPk!=0 ); nPk = pPk->nKeyCol; - /* Make sure every column of the PRIMARY KEY is NOT NULL */ - for(i=0; i<nPk; i++){ - pTab->aCol[pPk->aiColumn[i]].notNull = 1; + /* Make sure every column of the PRIMARY KEY is NOT NULL. (Except, + ** do not enforce this for imposter tables.) */ + if( !db->init.imposterTable ){ + for(i=0; i<nPk; i++){ + pTab->aCol[pPk->aiColumn[i]].notNull = 1; + } + pPk->uniqNotNull = 1; } - pPk->uniqNotNull = 1; /* The root page of the PRIMARY KEY is the table root page */ pPk->tnum = pTab->tnum; @@ -92189,6 +93254,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); + if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; @@ -92502,7 +93568,8 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ addr2 = sqlite3VdbeCurrentAddr(v); } sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx); - sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); + sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1); + sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v); @@ -92595,8 +93662,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - assert( pParse->nErr==0 ); /* Never called with prior errors */ - if( db->mallocFailed || IN_DECLARE_VTAB ){ + if( db->mallocFailed || IN_DECLARE_VTAB || pParse->nErr>0 ){ goto exit_create_index; } if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ @@ -92922,6 +93988,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pIdx->onError = pIndex->onError; } } + pRet = pIdx; goto exit_create_index; } } @@ -93514,7 +94581,6 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){ if( p ){ int i; - assert( p->a || p->nSrc==0 ); for(i=p->nSrc-1; i>0; i--){ p->a[i].jointype = p->a[i-1].jointype; } @@ -93761,8 +94827,7 @@ SQLITE_PRIVATE void sqlite3UniqueConstraint( StrAccum errMsg; Table *pTab = pIdx->pTable; - sqlite3StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = pParse->db; + sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200); for(j=0; j<pIdx->nKeyCol; j++){ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2); @@ -94708,7 +95773,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere( pInClause->x.pSelect = pSelect; pInClause->flags |= EP_xIsSelect; - sqlite3ExprSetHeight(pParse, pInClause); + sqlite3ExprSetHeightAndFlags(pParse, pInClause); return pInClause; /* something went wrong. clean up anything allocated. */ @@ -95381,7 +96446,9 @@ SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){ ** Return the collating function associated with a function. */ static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){ - VdbeOp *pOp = &context->pVdbe->aOp[context->iOp-1]; + VdbeOp *pOp; + assert( context->pVdbe!=0 ); + pOp = &context->pVdbe->aOp[context->iOp-1]; assert( pOp->opcode==OP_CollSeq ); assert( pOp->p4type==P4_COLLSEQ ); return pOp->p4.pColl; @@ -95589,13 +96656,13 @@ static void printfFunc( StrAccum str; const char *zFormat; int n; + sqlite3 *db = sqlite3_context_db_handle(context); if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){ x.nArg = argc-1; x.nUsed = 0; x.apArg = argv+1; - sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH); - str.db = sqlite3_context_db_handle(context); + sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x); n = str.nChar; sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, @@ -95650,6 +96717,14 @@ static void substrFunc( } } } +#ifdef SQLITE_SUBSTR_COMPATIBILITY + /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as + ** as substr(X,1,N) - it returns the first N characters of X. This + ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8] + ** from 2009-02-02 for compatibility of applications that exploited the + ** old buggy behavior. */ + if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */ +#endif if( argc==3 ){ p2 = sqlite3_value_int(argv[2]); if( p2<0 ){ @@ -95737,7 +96812,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ #endif /* -** Allocate nByte bytes of space using sqlite3_malloc(). If the +** Allocate nByte bytes of space using sqlite3Malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed and return NULL. ** If nByte is larger than the maximum string or blob length, then @@ -96111,7 +97186,7 @@ static int patternCompare( /* ** The sqlite3_strglob() interface. */ -SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){ +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; } @@ -96406,7 +97481,7 @@ static void charFunc( ){ unsigned char *z, *zOut; int i; - zOut = z = sqlite3_malloc( argc*4+1 ); + zOut = z = sqlite3_malloc64( argc*4+1 ); if( z==0 ){ sqlite3_result_error_nomem(context); return; @@ -96554,7 +97629,7 @@ static void replaceFunc( return; } zOld = zOut; - zOut = sqlite3_realloc(zOut, (int)nOut); + zOut = sqlite3_realloc64(zOut, (int)nOut); if( zOut==0 ){ sqlite3_result_error_nomem(context); sqlite3_free(zOld); @@ -96916,8 +97991,7 @@ static void groupConcatStep( if( pAccum ){ sqlite3 *db = sqlite3_context_db_handle(context); - int firstTerm = pAccum->useMalloc==0; - pAccum->useMalloc = 2; + int firstTerm = pAccum->mxAlloc==0; pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH]; if( !firstTerm ){ if( argc==2 ){ @@ -97001,6 +98075,11 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) ** then set aWc[0] through aWc[2] to the wildcard characters and ** return TRUE. If the function is not a LIKE-style function then ** return FALSE. +** +** *pIsNocase is set to true if uppercase and lowercase are equivalent for +** the function (default for LIKE). If the function makes the distinction +** between uppercase and lowercase (as does GLOB) then *pIsNocase is set to +** false. */ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){ FuncDef *pDef; @@ -98332,7 +99411,8 @@ static Trigger *fkActionTrigger( iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; assert( iFromCol>=0 ); - tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; + assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) ); + tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName; tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; tToCol.n = sqlite3Strlen30(tToCol.z); @@ -98344,10 +99424,10 @@ static Trigger *fkActionTrigger( ** parent table are used for the comparison. */ pEq = sqlite3PExpr(pParse, TK_EQ, sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + sqlite3ExprAlloc(db, TK_ID, &tOld, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) , 0), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) + sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0) , 0); pWhere = sqlite3ExprAnd(db, pWhere, pEq); @@ -98359,12 +99439,12 @@ static Trigger *fkActionTrigger( if( pChanges ){ pEq = sqlite3PExpr(pParse, TK_IS, sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + sqlite3ExprAlloc(db, TK_ID, &tOld, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), 0), sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + sqlite3ExprAlloc(db, TK_ID, &tNew, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0), 0), 0); pWhen = sqlite3ExprAnd(db, pWhen, pEq); @@ -98374,8 +99454,8 @@ static Trigger *fkActionTrigger( Expr *pNew; if( action==OE_Cascade ){ pNew = sqlite3PExpr(pParse, TK_DOT, - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + sqlite3ExprAlloc(db, TK_ID, &tNew, 0), + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0) , 0); }else if( action==OE_SetDflt ){ Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; @@ -98422,13 +99502,12 @@ static Trigger *fkActionTrigger( pTrigger = (Trigger *)sqlite3DbMallocZero(db, sizeof(Trigger) + /* struct Trigger */ sizeof(TriggerStep) + /* Single step in trigger program */ - nFrom + 1 /* Space for pStep->target.z */ + nFrom + 1 /* Space for pStep->zTarget */ ); if( pTrigger ){ pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; - pStep->target.z = (char *)&pStep[1]; - pStep->target.n = nFrom; - memcpy((char *)pStep->target.z, zFrom, nFrom); + pStep->zTarget = (char *)&pStep[1]; + memcpy((char *)pStep->zTarget, zFrom, nFrom); pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); @@ -98893,20 +99972,23 @@ static int xferOptimization( /* ** This routine is called to handle SQL of the following forms: ** -** insert into TABLE (IDLIST) values(EXPRLIST) +** insert into TABLE (IDLIST) values(EXPRLIST),(EXPRLIST),... ** insert into TABLE (IDLIST) select +** insert into TABLE (IDLIST) default values ** ** The IDLIST following the table name is always optional. If omitted, -** then a list of all columns for the table is substituted. The IDLIST -** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted. +** then a list of all (non-hidden) columns for the table is substituted. +** The IDLIST appears in the pColumn parameter. pColumn is NULL if IDLIST +** is omitted. ** -** The pList parameter holds EXPRLIST in the first form of the INSERT -** statement above, and pSelect is NULL. For the second form, pList is -** NULL and pSelect is a pointer to the select statement used to generate -** data for the insert. +** For the pSelect parameter holds the values to be inserted for the +** first two forms shown above. A VALUES clause is really just short-hand +** for a SELECT statement that omits the FROM clause and everything else +** that follows. If the pSelect parameter is NULL, that means that the +** DEFAULT VALUES form of the INSERT statement is intended. ** ** The code generated follows one of four templates. For a simple -** insert with data coming from a VALUES clause, the code executes +** insert with data coming from a single-row VALUES clause, the code executes ** once straight down through. Pseudo-code follows (we call this ** the "1st template"): ** @@ -99013,7 +100095,7 @@ SQLITE_PRIVATE void sqlite3Insert( u8 useTempTable = 0; /* Store SELECT results in intermediate table */ u8 appendFlag = 0; /* True if the insert is likely to be an append */ u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ - u8 bIdListInOrder = 1; /* True if IDLIST is in table order */ + u8 bIdListInOrder; /* True if IDLIST is in table order */ ExprList *pList = 0; /* List of VALUES() to be inserted */ /* Register allocations */ @@ -99038,8 +100120,8 @@ SQLITE_PRIVATE void sqlite3Insert( } /* If the Select object is really just a simple VALUES() list with a - ** single row values (the common case) then keep that one row of values - ** and go ahead and discard the Select object + ** single row (the common case) then keep that one row of values + ** and discard the other (unused) parts of the pSelect object */ if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){ pList = pSelect->pEList; @@ -99147,6 +100229,7 @@ SQLITE_PRIVATE void sqlite3Insert( ** is appears in the original table. (The index of the INTEGER ** PRIMARY KEY in the original table is pTab->iPKey.) */ + bIdListInOrder = (pTab->tabFlags & TF_OOOHidden)==0; if( pColumn ){ for(i=0; i<pColumn->nId; i++){ pColumn->a[i].idx = -1; @@ -99182,7 +100265,8 @@ SQLITE_PRIVATE void sqlite3Insert( ** co-routine is the common header to the 3rd and 4th templates. */ if( pSelect ){ - /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */ + /* Data is coming from a SELECT or from a multi-row VALUES clause. + ** Generate a co-routine to run the SELECT. */ int regYield; /* Register holding co-routine entry-point */ int addrTop; /* Top of the co-routine */ int rc; /* Result code */ @@ -99195,8 +100279,7 @@ SQLITE_PRIVATE void sqlite3Insert( dest.nSdst = pTab->nCol; rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - assert( pParse->nErr==0 || rc ); - if( rc || db->mallocFailed ) goto insert_cleanup; + if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); @@ -99244,8 +100327,8 @@ SQLITE_PRIVATE void sqlite3Insert( sqlite3ReleaseTempReg(pParse, regTempRowid); } }else{ - /* This is the case if the data for the INSERT is coming from a VALUES - ** clause + /* This is the case if the data for the INSERT is coming from a + ** single-row VALUES clause */ NameContext sNC; memset(&sNC, 0, sizeof(sNC)); @@ -100316,6 +101399,7 @@ static int xferOptimization( int onError, /* How to handle constraint errors */ int iDbDest /* The database of pDest */ ){ + sqlite3 *db = pParse->db; ExprList *pEList; /* The result set of the SELECT */ Table *pSrc; /* The table in the FROM clause of SELECT */ Index *pSrcIdx, *pDestIdx; /* Source and destination indices */ @@ -100463,11 +101547,11 @@ static int xferOptimization( ** the extra complication to make this rule less restrictive is probably ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] */ - if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ + if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ return 0; } #endif - if( (pParse->db->flags & SQLITE_CountRows)!=0 ){ + if( (db->flags & SQLITE_CountRows)!=0 ){ return 0; /* xfer opt does not play well with PRAGMA count_changes */ } @@ -100478,7 +101562,7 @@ static int xferOptimization( #ifdef SQLITE_TEST sqlite3_xferopt_count++; #endif - iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema); + iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema); v = sqlite3GetVdbe(pParse); sqlite3CodeVerifySchema(pParse, iDbSrc); iSrc = pParse->nTab++; @@ -100488,14 +101572,18 @@ static int xferOptimization( regRowid = sqlite3GetTempReg(pParse); sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite); assert( HasRowid(pDest) || destHasUniqueIdx ); - if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ + if( (db->flags & SQLITE_Vacuum)==0 && ( + (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */ || destHasUniqueIdx /* (2) */ || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */ - ){ + )){ /* In some circumstances, we are able to run the xfer optimization - ** only if the destination table is initially empty. This code makes - ** that determination. Conditions under which the destination must - ** be empty: + ** only if the destination table is initially empty. Unless the + ** SQLITE_Vacuum flag is set, this block generates code to make + ** that determination. If SQLITE_Vacuum is set, then the destination + ** table is always empty. + ** + ** Conditions under which the destination must be empty: ** ** (1) There is no INTEGER PRIMARY KEY but there are indices. ** (If the destination is not initially empty, the rowid fields @@ -100538,6 +101626,7 @@ static int xferOptimization( sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ + u8 useSeekResult = 0; for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } @@ -100551,7 +101640,33 @@ static int xferOptimization( VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); + if( db->flags & SQLITE_Vacuum ){ + /* This INSERT command is part of a VACUUM operation, which guarantees + ** that the destination table is empty. If all indexed columns use + ** collation sequence BINARY, then it can also be assumed that the + ** index will be populated by inserting keys in strictly sorted + ** order. In this case, instead of seeking within the b-tree as part + ** of every OP_IdxInsert opcode, an OP_Last is added before the + ** OP_IdxInsert to seek to the point within the b-tree where each key + ** should be inserted. This is faster. + ** + ** If any of the indexed columns use a collation sequence other than + ** BINARY, this optimization is disabled. This is because the user + ** might change the definition of a collation sequence and then run + ** a VACUUM command. In that case keys may not be written in strictly + ** sorted order. */ + for(i=0; i<pSrcIdx->nColumn; i++){ + char *zColl = pSrcIdx->azColl[i]; + assert( zColl!=0 ); + if( sqlite3_stricmp("BINARY", zColl) ) break; + } + if( i==pSrcIdx->nColumn ){ + useSeekResult = OPFLAG_USESEEKRESULT; + sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1); + } + } sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); + sqlite3VdbeChangeP5(v, useSeekResult); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); @@ -100601,7 +101716,7 @@ static int xferOptimization( ** argument to xCallback(). If xCallback=NULL then no callback ** is invoked, even for queries. */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3_callback xCallback, /* Invoke this callback routine */ @@ -101670,7 +102785,7 @@ static int sqlite3LoadExtension( const char *zEntry; char *zAltEntry = 0; void **aHandle; - int nMsg = 300 + sqlite3Strlen30(zFile); + u64 nMsg = 300 + sqlite3Strlen30(zFile); int ii; /* Shared library endings to try if zFile cannot be loaded as written */ @@ -101713,7 +102828,7 @@ static int sqlite3LoadExtension( #endif if( handle==0 ){ if( pzErrMsg ){ - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); + *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, "unable to open shared library [%s]", zFile); @@ -101739,7 +102854,7 @@ static int sqlite3LoadExtension( if( xInit==0 && zProc==0 ){ int iFile, iEntry, c; int ncFile = sqlite3Strlen30(zFile); - zAltEntry = sqlite3_malloc(ncFile+30); + zAltEntry = sqlite3_malloc64(ncFile+30); if( zAltEntry==0 ){ sqlite3OsDlClose(pVfs, handle); return SQLITE_NOMEM; @@ -101761,7 +102876,7 @@ static int sqlite3LoadExtension( if( xInit==0 ){ if( pzErrMsg ){ nMsg += sqlite3Strlen30(zEntry); - *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); + *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, "no entry point [%s] in shared library [%s]", zEntry, zFile); @@ -101796,7 +102911,7 @@ static int sqlite3LoadExtension( db->aExtension[db->nExtension++] = handle; return SQLITE_OK; } -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Use "sqlite3_extension_init" if 0 */ @@ -101827,7 +102942,7 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ ** Enable or disable extension loading. Extension loading is disabled by ** default so as not to open security holes in older applications. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff){ sqlite3_mutex_enter(db->mutex); if( onoff ){ db->flags |= SQLITE_LoadExtension; @@ -101860,7 +102975,7 @@ static const sqlite3_api_routines sqlite3Apis = { 0 }; */ typedef struct sqlite3AutoExtList sqlite3AutoExtList; static SQLITE_WSD struct sqlite3AutoExtList { - int nExt; /* Number of entries in aExt[] */ + u32 nExt; /* Number of entries in aExt[] */ void (**aExt)(void); /* Pointers to the extension init functions */ } sqlite3Autoext = { 0, 0 }; @@ -101884,7 +102999,7 @@ static SQLITE_WSD struct sqlite3AutoExtList { ** Register a statically linked extension that is automatically ** loaded by every new database connection. */ -SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xInit)(void)){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -101893,7 +103008,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ }else #endif { - int i; + u32 i; #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -101903,9 +103018,9 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ if( wsdAutoext.aExt[i]==xInit ) break; } if( i==wsdAutoext.nExt ){ - int nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]); + u64 nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]); void (**aNew)(void); - aNew = sqlite3_realloc(wsdAutoext.aExt, nByte); + aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte); if( aNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -101929,7 +103044,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){ ** Return 1 if xInit was found on the list and removed. Return 0 if xInit ** was not on the list. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xInit)(void)){ #if SQLITE_THREADSAFE sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif @@ -101937,7 +103052,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ int n = 0; wsdAutoextInit; sqlite3_mutex_enter(mutex); - for(i=wsdAutoext.nExt-1; i>=0; i--){ + for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ if( wsdAutoext.aExt[i]==xInit ){ wsdAutoext.nExt--; wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt]; @@ -101952,7 +103067,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xInit)(void)){ /* ** Reset the automatic extension loading mechanism. */ -SQLITE_API void sqlite3_reset_auto_extension(void){ +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize()==SQLITE_OK ) #endif @@ -101975,7 +103090,7 @@ SQLITE_API void sqlite3_reset_auto_extension(void){ ** If anything goes wrong, set an error in the database connection. */ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ - int i; + u32 i; int go = 1; int rc; int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); @@ -102034,11 +103149,18 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #endif /*************************************************************************** -** The next block of code, including the PragTyp_XXXX macro definitions and -** the aPragmaName[] object is composed of generated code. DO NOT EDIT. -** -** To add new pragmas, edit the code in ../tool/mkpragmatab.tcl and rerun -** that script. Then copy/paste the output in place of the following: +** The "pragma.h" include file is an automatically generated file that +** that includes the PragType_XXXX macro definitions and the aPragmaName[] +** object. This ensures that the aPragmaName[] table is arranged in +** lexicographical order to facility a binary search of the pragma name. +** Do not edit pragma.h directly. Edit and rerun the script in at +** ../tool/mkpragmatab.tcl. */ +/************** Include pragma.h in the middle of pragma.c *******************/ +/************** Begin file pragma.h ******************************************/ +/* DO NOT EDIT! +** This file is automatically generated by the script at +** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit +** that script and rerun it. */ #define PragTyp_HEADER_VALUE 0 #define PragTyp_AUTO_VACUUM 1 @@ -102273,6 +103395,10 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_INDEX_LIST, /* ePragFlag: */ PragFlag_NeedSchema, /* iArg: */ 0 }, + { /* zName: */ "index_xinfo", + /* ePragTyp: */ PragTyp_INDEX_INFO, + /* ePragFlag: */ PragFlag_NeedSchema, + /* iArg: */ 1 }, #endif #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) { /* zName: */ "integrity_check", @@ -102489,9 +103615,10 @@ static const struct sPragmaNames { /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, #endif }; -/* Number of pragmas: 58 on by default, 71 total. */ -/* End of the automatically generated pragma table. -***************************************************************************/ +/* Number of pragmas: 59 on by default, 72 total. */ + +/************** End of pragma.h **********************************************/ +/************** Continuing where we left off in pragma.c *********************/ /* ** Interpret the given string as a safety level. Return 0 for OFF, @@ -102627,15 +103754,15 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){ */ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){ Vdbe *v = sqlite3GetVdbe(pParse); - int mem = ++pParse->nMem; + int nMem = ++pParse->nMem; i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value)); if( pI64 ){ memcpy(pI64, &value, sizeof(value)); } - sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64); + sqlite3VdbeAddOp4(v, OP_Int64, 0, nMem, 0, (char*)pI64, P4_INT64); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, nMem, 1); } @@ -102744,6 +103871,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ + const struct sPragmaNames *pPragma; if( v==0 ) return; sqlite3VdbeRunOnlyOnce(v); @@ -102779,6 +103907,17 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS ** connection. If it returns SQLITE_OK, then assume that the VFS ** handled the pragma and generate a no-op prepared statement. + ** + ** IMPLEMENTATION-OF: R-12238-55120 Whenever a PRAGMA statement is parsed, + ** an SQLITE_FCNTL_PRAGMA file control is sent to the open sqlite3_file + ** object corresponding to the database file to which the pragma + ** statement refers. + ** + ** IMPLEMENTATION-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA + ** file control is an array of pointers to strings (char**) in which the + ** second element of the array is the name of the pragma and the third + ** element is the argument to the pragma or NULL if the pragma has no + ** argument. */ aFcntl[0] = 0; aFcntl[1] = zLeft; @@ -102788,11 +103927,11 @@ SQLITE_PRIVATE void sqlite3Pragma( rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); if( rc==SQLITE_OK ){ if( aFcntl[0] ){ - int mem = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0); + int nMem = ++pParse->nMem; + sqlite3VdbeAddOp4(v, OP_String8, 0, nMem, 0, aFcntl[0], 0); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC); - sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, nMem, 1); sqlite3_free(aFcntl[0]); } goto pragma_out; @@ -102821,14 +103960,15 @@ SQLITE_PRIVATE void sqlite3Pragma( } } if( lwr>upr ) goto pragma_out; + pPragma = &aPragmaNames[mid]; /* Make sure the database schema is loaded if the pragma requires that */ - if( (aPragmaNames[mid].mPragFlag & PragFlag_NeedSchema)!=0 ){ + if( (pPragma->mPragFlag & PragFlag_NeedSchema)!=0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; } /* Jump to the appropriate pragma handler */ - switch( aPragmaNames[mid].ePragTyp ){ + switch( pPragma->ePragTyp ){ #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) /* @@ -103396,7 +104536,9 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3ErrorMsg(pParse, "Safety level may not be changed inside a transaction"); }else{ - pDb->safety_level = getSafetyLevel(zRight,0,1)+1; + int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK; + if( iLevel==0 ) iLevel = 1; + pDb->safety_level = iLevel; setAllPagerFlags(db); } } @@ -103407,10 +104549,9 @@ SQLITE_PRIVATE void sqlite3Pragma( #ifndef SQLITE_OMIT_FLAG_PRAGMAS case PragTyp_FLAG: { if( zRight==0 ){ - returnSingleInt(pParse, aPragmaNames[mid].zName, - (db->flags & aPragmaNames[mid].iArg)!=0 ); + returnSingleInt(pParse, pPragma->zName, (db->flags & pPragma->iArg)!=0 ); }else{ - int mask = aPragmaNames[mid].iArg; /* Mask of bits to set or clear. */ + int mask = pPragma->iArg; /* Mask of bits to set or clear. */ if( db->autoCommit==0 ){ /* Foreign key support may not be enabled or disabled while not ** in auto-commit mode. */ @@ -103492,7 +104633,7 @@ SQLITE_PRIVATE void sqlite3Pragma( }else if( pPk==0 ){ k = 1; }else{ - for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} + for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){} } sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); @@ -103539,20 +104680,42 @@ SQLITE_PRIVATE void sqlite3Pragma( pIdx = sqlite3FindIndex(db, zRight, zDb); if( pIdx ){ int i; + int mx; + if( pPragma->iArg ){ + /* PRAGMA index_xinfo (newer version with more rows and columns) */ + mx = pIdx->nColumn; + pParse->nMem = 6; + }else{ + /* PRAGMA index_info (legacy version) */ + mx = pIdx->nKeyCol; + pParse->nMem = 3; + } pTab = pIdx->pTable; - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; + sqlite3VdbeSetNumCols(v, pParse->nMem); sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC); - for(i=0; i<pIdx->nKeyCol; i++){ + if( pPragma->iArg ){ + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "desc", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "coll", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "key", SQLITE_STATIC); + } + for(i=0; i<mx; i++){ i16 cnum = pIdx->aiColumn[i]; sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2); - assert( pTab->nCol>cnum ); - sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); + if( cnum<0 ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, 3); + }else{ + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0); + } + if( pPragma->iArg ){ + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->aSortOrder[i], 4); + sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, pIdx->azColl[i], 0); + sqlite3VdbeAddOp2(v, OP_Integer, i<pIdx->nKeyCol, 6); + } + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, pParse->nMem); } } } @@ -103565,17 +104728,22 @@ SQLITE_PRIVATE void sqlite3Pragma( pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ v = sqlite3GetVdbe(pParse); - sqlite3VdbeSetNumCols(v, 3); - pParse->nMem = 3; + sqlite3VdbeSetNumCols(v, 5); + pParse->nMem = 5; sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "origin", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "partial", SQLITE_STATIC); for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){ + const char *azOrigin[] = { "c", "u", "pk" }; sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0); sqlite3VdbeAddOp2(v, OP_Integer, IsUniqueIndex(pIdx), 3); - sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); + sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, azOrigin[pIdx->idxType], 0); + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->pPartIdxWhere!=0, 5); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5); } } } @@ -104145,9 +105313,9 @@ SQLITE_PRIVATE void sqlite3Pragma( ** applications for any purpose. */ case PragTyp_HEADER_VALUE: { - int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */ + int iCookie = pPragma->iArg; /* Which cookie to read or write */ sqlite3VdbeUsesBtree(v, iDb); - if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){ + if( zRight && (pPragma->mPragFlag & PragFlag_ReadOnly)==0 ){ /* Write the specified cookie value */ static const VdbeOpList setCookie[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ @@ -104249,8 +105417,9 @@ SQLITE_PRIVATE void sqlite3Pragma( /* ** PRAGMA shrink_memory ** - ** This pragma attempts to free as much memory as possible from the - ** current database connection. + ** IMPLEMENTATION-OF: R-23445-46109 This pragma causes the database + ** connection on which it is invoked to free up as much memory as it + ** can, by calling sqlite3_db_release_memory(). */ case PragTyp_SHRINK_MEMORY: { sqlite3_db_release_memory(db); @@ -104267,7 +105436,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** disables the timeout. */ /*case PragTyp_BUSY_TIMEOUT*/ default: { - assert( aPragmaNames[mid].ePragTyp==PragTyp_BUSY_TIMEOUT ); + assert( pPragma->ePragTyp==PragTyp_BUSY_TIMEOUT ); if( zRight ){ sqlite3_busy_timeout(db, sqlite3Atoi(zRight)); } @@ -104279,8 +105448,12 @@ SQLITE_PRIVATE void sqlite3Pragma( ** PRAGMA soft_heap_limit ** PRAGMA soft_heap_limit = N ** - ** Call sqlite3_soft_heap_limit64(N). Return the result. If N is omitted, - ** use -1. + ** IMPLEMENTATION-OF: R-26343-45930 This pragma invokes the + ** sqlite3_soft_heap_limit64() interface with the argument N, if N is + ** specified and is a non-negative integer. + ** IMPLEMENTATION-OF: R-64451-07163 The soft_heap_limit pragma always + ** returns the same integer that would be returned by the + ** sqlite3_soft_heap_limit64(-1) C-language function. */ case PragTyp_SOFT_HEAP_LIMIT: { sqlite3_int64 N; @@ -104466,7 +105639,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */ if( argv[1]==0 ){ corruptSchema(pData, argv[0], 0); - }else if( argv[2] && argv[2][0] ){ + }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){ /* Call the parser to process a CREATE TABLE, INDEX or VIEW. ** But because db->init.busy is set to 1, no VDBE code is generated ** or executed. All the parser does is build the internal data @@ -104497,8 +105670,8 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char } } sqlite3_finalize(pStmt); - }else if( argv[0]==0 ){ - corruptSchema(pData, 0, 0); + }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){ + corruptSchema(pData, argv[0], 0); }else{ /* If the SQL column is blank it means this is an index that ** was created to be the PRIMARY KEY or to fulfill a UNIQUE @@ -105176,7 +106349,7 @@ SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){ ** and the statement is automatically recompiled if an schema change ** occurs. */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105188,7 +106361,7 @@ SQLITE_API int sqlite3_prepare( assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle. */ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105264,7 +106437,7 @@ static int sqlite3Prepare16( ** and the statement is automatically recompiled if an schema change ** occurs. */ -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105276,7 +106449,7 @@ SQLITE_API int sqlite3_prepare16( assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle. */ const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ @@ -105405,7 +106578,6 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( Select standin; sqlite3 *db = pParse->db; pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); - assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */ if( pNew==0 ){ assert( db->mallocFailed ); pNew = &standin; @@ -105425,7 +106597,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( pNew->op = TK_SELECT; pNew->pLimit = pLimit; pNew->pOffset = pOffset; - assert( pOffset==0 || pLimit!=0 ); + assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 ); pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; if( db->mallocFailed ) { @@ -105857,20 +107029,17 @@ static void pushOntoSorter( } sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord); if( pSelect->iLimit ){ - int addr1, addr2; + int addr; int iLimit; if( pSelect->iOffset ){ iLimit = pSelect->iOffset+1; }else{ iLimit = pSelect->iLimit; } - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); - addr2 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, addr1); + addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); - sqlite3VdbeJumpHere(v, addr2); + sqlite3VdbeJumpHere(v, addr); } } @@ -106267,7 +107436,7 @@ static void selectInnerLoop( ** the output for us. */ if( pSort==0 && p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v); } } @@ -106678,7 +107847,7 @@ static const char *columnTypeImpl( ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ - if( iCol>=0 && ALWAYS(iCol<pS->pEList->nExpr) ){ + if( iCol>=0 && iCol<pS->pEList->nExpr ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. @@ -106998,12 +108167,14 @@ static void selectAddColumnTypeAndCollation( a = pSelect->pEList->a; for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){ p = a[i].pExpr; - pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst)); + if( pCol->zType==0 ){ + pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst)); + } szAll += pCol->szEst; pCol->affinity = sqlite3ExprAffinity(p); if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE; pColl = sqlite3ExprCollSeq(pParse, p); - if( pColl ){ + if( pColl && pCol->zColl==0 ){ pCol->zColl = sqlite3DbStrDup(db, pColl->zName); } } @@ -107120,7 +108291,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v); VdbeComment((v, "LIMIT counter")); - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, iBreak); VdbeCoverage(v); } if( p->pOffset ){ p->iOffset = iOffset = ++pParse->nMem; @@ -107339,7 +108510,7 @@ static void generateWithRecursiveQuery( selectInnerLoop(pParse, p, p->pEList, iCurrent, 0, 0, pDest, addrCont, addrBreak); if( regLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak); VdbeCoverage(v); } sqlite3VdbeResolveLabel(v, addrCont); @@ -107405,8 +108576,7 @@ static int multiSelectValues( int nExpr = p->pEList->nExpr; int nRow = 1; int rc = 0; - assert( p->pNext==0 ); - assert( p->selFlags & SF_AllValues ); + assert( p->selFlags & SF_MultiValue ); do{ assert( p->selFlags & SF_Values ); assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); @@ -107515,7 +108685,7 @@ static int multiSelect( /* Special handling for a compound-select that originates as a VALUES clause. */ - if( p->selFlags & SF_AllValues ){ + if( p->selFlags & SF_MultiValue ){ rc = multiSelectValues(pParse, p, &dest); goto multi_select_end; } @@ -107564,7 +108734,7 @@ static int multiSelect( p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ - addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeCoverage(v); + addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); VdbeComment((v, "Jump ahead if LIMIT reached")); } explainSetInteger(iSub2, pParse->iNextSelectId); @@ -107900,7 +109070,7 @@ static int generateOutputSubroutine( */ case SRT_Set: { int r1; - assert( pIn->nSdst==1 ); + assert( pIn->nSdst==1 || pParse->nErr>0 ); pDest->affSdst = sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affSdst); r1 = sqlite3GetTempReg(pParse); @@ -107926,7 +109096,7 @@ static int generateOutputSubroutine( ** of the scan loop. */ case SRT_Mem: { - assert( pIn->nSdst==1 ); + assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 ); sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1); /* The LIMIT clause will jump out of the loop for us */ break; @@ -107941,7 +109111,7 @@ static int generateOutputSubroutine( pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst); pDest->nSdst = pIn->nSdst; } - sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pDest->nSdst); + sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst); sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm); break; } @@ -107965,7 +109135,7 @@ static int generateOutputSubroutine( /* Jump to the end of the loop if the LIMIT is reached. */ if( p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v); } /* Generate the subroutine return @@ -108157,8 +109327,10 @@ static int multiSelectOrderBy( if( aPermute ){ struct ExprList_item *pItem; for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){ - assert( pItem->u.x.iOrderByCol>0 - && pItem->u.x.iOrderByCol<=p->pEList->nExpr ); + assert( pItem->u.x.iOrderByCol>0 ); + /* assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ) is also true + ** but only for well-formed SELECT statements. */ + testcase( pItem->u.x.iOrderByCol > p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; } pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); @@ -108368,7 +109540,7 @@ static int multiSelectOrderBy( /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ explainComposite(pParse, p->op, iSub1, iSub2, 0); - return SQLITE_OK; + return pParse->nErr!=0; } #endif @@ -108488,7 +109660,10 @@ static void substSelect( ** ** (1) The subquery and the outer query do not both use aggregates. ** -** (2) The subquery is not an aggregate or the outer query is not a join. +** (2) The subquery is not an aggregate or (2a) the outer query is not a join +** and (2b) the outer query does not use subqueries other than the one +** FROM-clause subquery that is a candidate for flattening. (2b is +** due to ticket [2f7170d73bf9abf80] from 2015-02-09.) ** ** (3) The subquery is not the right operand of a left outer join ** (Originally ticket #306. Strengthened by ticket #3300) @@ -108625,8 +109800,17 @@ static int flattenSubquery( iParent = pSubitem->iCursor; pSub = pSubitem->pSelect; assert( pSub!=0 ); - if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */ - if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */ + if( subqueryIsAgg ){ + if( isAgg ) return 0; /* Restriction (1) */ + if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */ + if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery)) + || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0 + || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0 + ){ + return 0; /* Restriction (2b) */ + } + } + pSubSrc = pSub->pSrc; assert( pSubSrc ); /* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants, @@ -109168,7 +110352,10 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew->pOrderBy = 0; p->pPrior = 0; p->pNext = 0; + p->pWith = 0; p->selFlags &= ~SF_Compound; + assert( (p->selFlags & SF_Converted)==0 ); + p->selFlags |= SF_Converted; assert( pNew->pPrior!=0 ); pNew->pPrior->pNext = pNew; pNew->pLimit = 0; @@ -109320,7 +110507,7 @@ static int withExpand( for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior); pEList = pLeft->pEList; if( pCte->pCols ){ - if( pEList->nExpr!=pCte->pCols->nExpr ){ + if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){ sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns", pCte->zName, pEList->nExpr, pCte->pCols->nExpr ); @@ -109447,7 +110634,7 @@ static int selectExpander(Walker *pWalker, Select *p){ /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pTab==0 ); - sqlite3WalkSelect(pWalker, pSel); + if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; @@ -109704,7 +110891,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - if( (pSelect->selFlags & SF_AllValues)==0 ){ + if( (pSelect->selFlags & SF_MultiValue)==0 ){ w.xSelectCallback2 = selectPopWith; } sqlite3WalkSelect(&w, pSelect); @@ -109890,7 +111077,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ } if( pF->iDistinct>=0 ){ addrNext = sqlite3VdbeMakeLabel(v); - assert( nArg==1 ); + testcase( nArg==0 ); /* Error condition */ + testcase( nArg>1 ); /* Also an error */ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); } if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ @@ -110046,6 +111234,13 @@ SQLITE_PRIVATE int sqlite3Select( } isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); +#if SELECTTRACE_ENABLED + if( sqlite3SelectTrace & 0x100 ){ + SELECTTRACE(0x100,pParse,p, ("after name resolution:\n")); + sqlite3TreeViewSelect(0, p, 0); + } +#endif + /* Begin generating code. */ @@ -110758,10 +111953,9 @@ SQLITE_PRIVATE int sqlite3Select( */ sqlite3VdbeResolveLabel(v, iEnd); - /* The SELECT was successfully coded. Set the return code to 0 - ** to indicate no errors. - */ - rc = 0; + /* The SELECT has been coded. If there is an error in the Parse structure, + ** set the return code to 1. Otherwise 0. */ + rc = (pParse->nErr>0); /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. @@ -110791,9 +111985,9 @@ select_end: SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ int n = 0; pView = sqlite3TreeViewPush(pView, moreToFollow); - sqlite3TreeViewLine(pView, "SELECT%s%s", + sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p)", ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), - ((p->selFlags & SF_Aggregate) ? " agg_flag" : "") + ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p ); if( p->pSrc && p->pSrc->nSrc ) n++; if( p->pWhere ) n++; @@ -110812,7 +112006,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m struct SrcList_item *pItem = &p->pSrc->a[i]; StrAccum x; char zLine[100]; - sqlite3StrAccumInit(&x, zLine, sizeof(zLine), 0); + sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); sqlite3XPrintf(&x, 0, "{%d,*}", pItem->iCursor); if( pItem->zDatabase ){ sqlite3XPrintf(&x, 0, " %s.%s", pItem->zDatabase, pItem->zName); @@ -110971,7 +112165,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ z = 0; }else{ int n = sqlite3Strlen30(argv[i])+1; - z = sqlite3_malloc( n ); + z = sqlite3_malloc64( n ); if( z==0 ) goto malloc_failed; memcpy(z, argv[i], n); } @@ -110996,7 +112190,7 @@ malloc_failed: ** Instead, the entire table should be passed to sqlite3_free_table() when ** the calling procedure is finished using it. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ char ***pazResult, /* Write the result table here */ @@ -111020,7 +112214,7 @@ SQLITE_API int sqlite3_get_table( res.nData = 1; res.nAlloc = 20; res.rc = SQLITE_OK; - res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc ); + res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc ); if( res.azResult==0 ){ db->errCode = SQLITE_NOMEM; return SQLITE_NOMEM; @@ -111048,7 +112242,7 @@ SQLITE_API int sqlite3_get_table( } if( res.nAlloc>res.nData ){ char **azNew; - azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData ); + azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData ); if( azNew==0 ){ sqlite3_free_table(&res.azResult[1]); db->errCode = SQLITE_NOMEM; @@ -111065,7 +112259,7 @@ SQLITE_API int sqlite3_get_table( /* ** This routine frees the space the sqlite3_get_table() malloced. */ -SQLITE_API void sqlite3_free_table( +SQLITE_API void SQLITE_STDCALL sqlite3_free_table( char **azResult /* Result returned from sqlite3_get_table() */ ){ if( azResult ){ @@ -111276,7 +112470,6 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( /* Do not create a trigger on a system table */ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); - pParse->nErr++; goto trigger_cleanup; } @@ -111456,12 +112649,12 @@ static TriggerStep *triggerStepAllocate( ){ TriggerStep *pTriggerStep; - pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n); + pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); if( pTriggerStep ){ char *z = (char*)&pTriggerStep[1]; memcpy(z, pName->z, pName->n); - pTriggerStep->target.z = z; - pTriggerStep->target.n = pName->n; + sqlite3Dequote(z); + pTriggerStep->zTarget = z; pTriggerStep->op = op; } return pTriggerStep; @@ -111744,7 +112937,7 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( } /* -** Convert the pStep->target token into a SrcList and return a pointer +** Convert the pStep->zTarget string into a SrcList and return a pointer ** to that SrcList. ** ** This routine adds a specific database name, if needed, to the target when @@ -111757,17 +112950,17 @@ static SrcList *targetSrcList( Parse *pParse, /* The parsing context */ TriggerStep *pStep /* The trigger containing the target token */ ){ + sqlite3 *db = pParse->db; int iDb; /* Index of the database to use */ SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0); + pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc ){ assert( pSrc->nSrc>0 ); - assert( pSrc->a!=0 ); - iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema); + pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget); + iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema); if( iDb==0 || iDb>=2 ){ - sqlite3 *db = pParse->db; - assert( iDb<pParse->db->nDb ); + assert( iDb<db->nDb ); pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName); } } @@ -111879,6 +113072,7 @@ static void transferParseError(Parse *pTo, Parse *pFrom){ if( pTo->nErr==0 ){ pTo->zErrMsg = pFrom->zErrMsg; pTo->nErr = pFrom->nErr; + pTo->rc = pFrom->rc; }else{ sqlite3DbFree(pFrom->db, pFrom->zErrMsg); } @@ -113163,7 +114357,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** cause problems for the call to BtreeSetPageSize() below. */ sqlite3BtreeCommit(pTemp); - nRes = sqlite3BtreeGetReserve(pMain); + nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC @@ -113229,6 +114423,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ + assert( (db->flags & SQLITE_Vacuum)==0 ); + db->flags |= SQLITE_Vacuum; rc = execExecSql(db, pzErrMsg, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " "|| ' SELECT * FROM main.' || quote(name) || ';'" @@ -113236,6 +114432,8 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ "WHERE type = 'table' AND name!='sqlite_sequence' " " AND coalesce(rootpage,1)>0" ); + assert( (db->flags & SQLITE_Vacuum)!=0 ); + db->flags &= ~SQLITE_Vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy over the sequence table @@ -113374,6 +114572,8 @@ end_of_vacuum: struct VtabCtx { VTable *pVTable; /* The virtual table being constructed */ Table *pTab; /* The Table object to which the virtual table belongs */ + VtabCtx *pPrior; /* Parent context (if any) */ + int bDeclared; /* True after sqlite3_declare_vtab() is called */ }; /* @@ -113425,7 +114625,7 @@ static int createModule( /* ** External API function used to create a new virtual-table module. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ @@ -113440,7 +114640,7 @@ SQLITE_API int sqlite3_create_module( /* ** External API function used to create a new virtual-table module. */ -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* Database in which module is registered */ const char *zName, /* Name assigned to this module */ const sqlite3_module *pModule, /* The definition of the module */ @@ -113739,6 +114939,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ char *zStmt; char *zWhere; int iDb; + int iReg; Vdbe *v; /* Compute the complete text of the CREATE VIRTUAL TABLE statement */ @@ -113773,8 +114974,10 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); - sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, - pTab->zName, sqlite3Strlen30(pTab->zName) + 1); + + iReg = ++pParse->nMem; + sqlite3VdbeAddOp4(v, OP_String8, 0, iReg, 0, pTab->zName, 0); + sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg); } /* If we are rereading the sqlite_master table create the in-memory @@ -113817,7 +115020,7 @@ SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse *pParse, Token *p){ pArg->z = p->z; pArg->n = p->n; }else{ - assert(pArg->z < p->z); + assert(pArg->z <= p->z); pArg->n = (int)(&p->z[p->n] - pArg->z); } } @@ -113834,15 +115037,27 @@ static int vtabCallConstructor( int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ - VtabCtx sCtx, *pPriorCtx; + VtabCtx sCtx; VTable *pVTable; int rc; const char *const*azArg = (const char *const*)pTab->azModuleArg; int nArg = pTab->nModuleArg; char *zErr = 0; - char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); + char *zModuleName; int iDb; + VtabCtx *pCtx; + + /* Check that the virtual-table is not already being initialized */ + for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){ + if( pCtx->pTab==pTab ){ + *pzErr = sqlite3MPrintf(db, + "vtable constructor called recursively: %s", pTab->zName + ); + return SQLITE_LOCKED; + } + } + zModuleName = sqlite3MPrintf(db, "%s", pTab->zName); if( !zModuleName ){ return SQLITE_NOMEM; } @@ -113863,11 +115078,13 @@ static int vtabCallConstructor( assert( xConstruct ); sCtx.pTab = pTab; sCtx.pVTable = pVTable; - pPriorCtx = db->pVtabCtx; + sCtx.pPrior = db->pVtabCtx; + sCtx.bDeclared = 0; db->pVtabCtx = &sCtx; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); - db->pVtabCtx = pPriorCtx; + db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + assert( sCtx.pTab==pTab ); if( SQLITE_OK!=rc ){ if( zErr==0 ){ @@ -113883,13 +115100,14 @@ static int vtabCallConstructor( memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); pVTable->pVtab->pModule = pMod->pModule; pVTable->nRef = 1; - if( sCtx.pTab ){ + if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ int iCol; + u8 oooHidden = 0; /* If everything went according to plan, link the new VTable structure ** into the linked list headed by pTab->pVTable. Then loop through the ** columns of the table to see if any of them contain the token "hidden". @@ -113902,7 +115120,10 @@ static int vtabCallConstructor( char *zType = pTab->aCol[iCol].zType; int nType; int i = 0; - if( !zType ) continue; + if( !zType ){ + pTab->tabFlags |= oooHidden; + continue; + } nType = sqlite3Strlen30(zType); if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ for(i=0; i<nType; i++){ @@ -113925,6 +115146,9 @@ static int vtabCallConstructor( zType[i-1] = '\0'; } pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN; + oooHidden = TF_OOOHidden; + }else{ + pTab->tabFlags |= oooHidden; } } } @@ -114052,22 +115276,26 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, ** valid to call this function from within the xCreate() or xConnect() of a ** virtual table module. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ + VtabCtx *pCtx; Parse *pParse; - int rc = SQLITE_OK; Table *pTab; char *zErr = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; + if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ + return SQLITE_MISUSE_BKPT; + } #endif sqlite3_mutex_enter(db->mutex); - if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){ + pCtx = db->pVtabCtx; + if( !pCtx || pCtx->bDeclared ){ sqlite3Error(db, SQLITE_MISUSE); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } + pTab = pCtx->pTab; assert( (pTab->tabFlags & TF_Virtual)!=0 ); pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); @@ -114090,7 +115318,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } - db->pVtabCtx->pTab = 0; + pCtx->bDeclared = 1; }else{ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); @@ -114125,11 +115353,15 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ - VTable *p = vtabDisconnectAll(db, pTab); - - assert( rc==SQLITE_OK ); + VTable *p; + for(p=pTab->pVTable; p; p=p->pNext){ + assert( p->pVtab ); + if( p->pVtab->nRef>0 ){ + return SQLITE_LOCKED; + } + } + p = vtabDisconnectAll(db, pTab); rc = p->pMod->pModule->xDestroy(p->pVtab); - /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ assert( pTab->pVTable==p && p->pNext==0 ); @@ -114280,7 +115512,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ int rc = SQLITE_OK; assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); - assert( iSavepoint>=0 ); + assert( iSavepoint>=-1 ); if( db->aVTrans ){ int i; for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ @@ -114398,7 +115630,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ if( pTab==pToplevel->apVtabLock[i] ) return; } n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); + apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n); if( apVtabLock ){ pToplevel->apVtabLock = apVtabLock; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; @@ -114414,7 +115646,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ ** The results of this routine are undefined unless it is called from ** within an xUpdate method. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *db){ static const unsigned char aMap[] = { SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE }; @@ -114432,7 +115664,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ ** the SQLite core with additional information about the behavior ** of the virtual table being implemented. */ -SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3 *db, int op, ...){ va_list ap; int rc = SQLITE_OK; @@ -114558,6 +115790,8 @@ struct WhereLevel { int addrCont; /* Jump here to continue with the next loop cycle */ int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ + int iLikeRepCntr; /* LIKE range processing counter register */ + int addrLikeRep; /* LIKE range processing address */ u8 iFrom; /* Which entry in the FROM clause */ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to ends the loop */ @@ -114742,7 +115976,7 @@ struct WhereTerm { } u; LogEst truthProb; /* Probability of truth for this expression */ u16 eOperator; /* A WO_xx value describing <op> */ - u8 wtFlags; /* TERM_xxx bit flags. See below */ + u16 wtFlags; /* TERM_xxx bit flags. See below */ u8 nChild; /* Number of children that must disable us */ WhereClause *pWC; /* The clause this term is part of */ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ @@ -114764,6 +115998,9 @@ struct WhereTerm { #else # define TERM_VNULL 0x00 /* Disabled if not using stat3 */ #endif +#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */ +#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */ +#define TERM_LIKE 0x400 /* The original LIKE operator */ /* ** An instance of the WhereScan object is used as an iterator for locating @@ -115139,7 +116376,7 @@ static void whereClauseClear(WhereClause *pWC){ ** calling this routine. Such pointers may be reinitialized by referencing ** the pWC->a[] array. */ -static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ +static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){ WhereTerm *pTerm; int idx; testcase( wtFlags & TERM_VIRTUAL ); @@ -115192,13 +116429,14 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){ ** all terms of the WHERE clause. */ static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){ + Expr *pE2 = sqlite3ExprSkipCollate(pExpr); pWC->op = op; - if( pExpr==0 ) return; - if( pExpr->op!=op ){ + if( pE2==0 ) return; + if( pE2->op!=op ){ whereClauseInsert(pWC, pExpr, 0); }else{ - whereSplit(pWC, pExpr->pLeft, op); - whereSplit(pWC, pExpr->pRight, op); + whereSplit(pWC, pE2->pLeft, op); + whereSplit(pWC, pE2->pRight, op); } } @@ -115564,7 +116802,11 @@ static void exprAnalyzeAll( ** so and false if not. ** ** In order for the operator to be optimizible, the RHS must be a string -** literal that does not begin with a wildcard. +** literal that does not begin with a wildcard. The LHS must be a column +** that may only be NULL, a string, or a BLOB, never a number. (This means +** that virtual tables cannot participate in the LIKE optimization.) If the +** collating sequence for the column on the LHS must be appropriate for +** the operator. */ static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ @@ -115593,7 +116835,7 @@ static int isLikeOrGlob( pLeft = pList->a[1].pExpr; if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT - || IsVirtual(pLeft->pTab) + || IsVirtual(pLeft->pTab) /* Value might be numeric */ ){ /* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must ** be the name of an indexed column with TEXT affinity. */ @@ -115703,6 +116945,79 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ pWC->a[iParent].nChild++; } +/* +** Return the N-th AND-connected subterm of pTerm. Or if pTerm is not +** a conjunction, then return just pTerm when N==0. If N is exceeds +** the number of available subterms, return NULL. +*/ +static WhereTerm *whereNthSubterm(WhereTerm *pTerm, int N){ + if( pTerm->eOperator!=WO_AND ){ + return N==0 ? pTerm : 0; + } + if( N<pTerm->u.pAndInfo->wc.nTerm ){ + return &pTerm->u.pAndInfo->wc.a[N]; + } + return 0; +} + +/* +** Subterms pOne and pTwo are contained within WHERE clause pWC. The +** two subterms are in disjunction - they are OR-ed together. +** +** If these two terms are both of the form: "A op B" with the same +** A and B values but different operators and if the operators are +** compatible (if one is = and the other is <, for example) then +** add a new virtual AND term to pWC that is the combination of the +** two. +** +** Some examples: +** +** x<y OR x=y --> x<=y +** x=y OR x=y --> x=y +** x<=y OR x<y --> x<=y +** +** The following is NOT generated: +** +** x<y OR x>y --> x!=y +*/ +static void whereCombineDisjuncts( + SrcList *pSrc, /* the FROM clause */ + WhereClause *pWC, /* The complete WHERE clause */ + WhereTerm *pOne, /* First disjunct */ + WhereTerm *pTwo /* Second disjunct */ +){ + u16 eOp = pOne->eOperator | pTwo->eOperator; + sqlite3 *db; /* Database connection (for malloc) */ + Expr *pNew; /* New virtual expression */ + int op; /* Operator for the combined expression */ + int idxNew; /* Index in pWC of the next virtual term */ + + if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; + if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp + && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; + assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); + assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); + if( sqlite3ExprCompare(pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; + if( sqlite3ExprCompare(pOne->pExpr->pRight, pTwo->pExpr->pRight, -1) )return; + /* If we reach this point, it means the two subterms can be combined */ + if( (eOp & (eOp-1))!=0 ){ + if( eOp & (WO_LT|WO_LE) ){ + eOp = WO_LE; + }else{ + assert( eOp & (WO_GT|WO_GE) ); + eOp = WO_GE; + } + } + db = pWC->pWInfo->pParse->db; + pNew = sqlite3ExprDup(db, pOne->pExpr, 0); + if( pNew==0 ) return; + for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); } + pNew->op = op; + idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC); + exprAnalyze(pSrc, pWC, idxNew); +} + #if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY) /* ** Analyze a term that consists of two or more OR-connected @@ -115727,6 +117042,7 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ ** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15) ** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*') ** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6) +** (F) x>A OR (x=A AND y>=B) ** ** CASE 1: ** @@ -115743,6 +117059,16 @@ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ ** ** CASE 2: ** +** If there are exactly two disjuncts one side has x>A and the other side +** has x=A (for the same x and A) then add a new virtual conjunct term to the +** WHERE clause of the form "x>=A". Example: +** +** x>A OR (x=A AND y>B) adds: x>=A +** +** The added conjunct can sometimes be helpful in query planning. +** +** CASE 3: +** ** If all subterms are indexable by a single table T, then set ** ** WhereTerm.eOperator = WO_OR @@ -115869,12 +117195,26 @@ static void exprAnalyzeOrTerm( } /* - ** Record the set of tables that satisfy case 2. The set might be + ** Record the set of tables that satisfy case 3. The set might be ** empty. */ pOrInfo->indexable = indexable; pTerm->eOperator = indexable==0 ? 0 : WO_OR; + /* For a two-way OR, attempt to implementation case 2. + */ + if( indexable && pOrWc->nTerm==2 ){ + int iOne = 0; + WhereTerm *pOne; + while( (pOne = whereNthSubterm(&pOrWc->a[0],iOne++))!=0 ){ + int iTwo = 0; + WhereTerm *pTwo; + while( (pTwo = whereNthSubterm(&pOrWc->a[1],iTwo++))!=0 ){ + whereCombineDisjuncts(pSrc, pWC, pOne, pTwo); + } + } + } + /* ** chngToIN holds a set of tables that *might* satisfy case 1. But ** we have to do some additional checking to see if case 1 really @@ -116004,7 +117344,7 @@ static void exprAnalyzeOrTerm( }else{ sqlite3ExprListDelete(db, pList); } - pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ + pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */ } } } @@ -116042,7 +117382,7 @@ static void exprAnalyze( Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ - int noCase = 0; /* LIKE/GLOB distinguishes case */ + int noCase = 0; /* uppercase equivalent to lowercase */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWInfo->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ @@ -116180,12 +117520,15 @@ static void exprAnalyze( /* Add constraints to reduce the search space on a LIKE or GLOB ** operator. ** - ** A like pattern of the form "x LIKE 'abc%'" is changed into constraints + ** A like pattern of the form "x LIKE 'aBc%'" is changed into constraints ** - ** x>='abc' AND x<'abd' AND x LIKE 'abc%' + ** x>='ABC' AND x<'abd' AND x LIKE 'aBc%' ** ** The last character of the prefix "abc" is incremented to form the - ** termination condition "abd". + ** termination condition "abd". If case is not significant (the default + ** for LIKE) then the lower-bound is made all uppercase and the upper- + ** bound is made all lowercase so that the bounds also work when comparing + ** BLOBs. */ if( pWC->op==TK_AND && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) @@ -116196,10 +117539,26 @@ static void exprAnalyze( Expr *pNewExpr2; int idxNew1; int idxNew2; - Token sCollSeqName; /* Name of collating sequence */ + const char *zCollSeqName; /* Name of collating sequence */ + const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC; pLeft = pExpr->x.pList->a[1].pExpr; pStr2 = sqlite3ExprDup(db, pStr1, 0); + + /* Convert the lower bound to upper-case and the upper bound to + ** lower-case (upper-case is less than lower-case in ASCII) so that + ** the range constraints also work for BLOBs + */ + if( noCase && !pParse->db->mallocFailed ){ + int i; + char c; + pTerm->wtFlags |= TERM_LIKE; + for(i=0; (c = pStr1->u.zToken[i])!=0; i++){ + pStr1->u.zToken[i] = sqlite3Toupper(c); + pStr2->u.zToken[i] = sqlite3Tolower(c); + } + } + if( !db->mallocFailed ){ u8 c, *pC; /* Last character before the first wildcard */ pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; @@ -116216,22 +117575,21 @@ static void exprAnalyze( } *pC = c + 1; } - sCollSeqName.z = noCase ? "NOCASE" : "BINARY"; - sCollSeqName.n = 6; + zCollSeqName = noCase ? "NOCASE" : "BINARY"; pNewExpr1 = sqlite3ExprDup(db, pLeft, 0); - pNewExpr1 = sqlite3PExpr(pParse, TK_GE, - sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName), + pNewExpr1 = sqlite3PExpr(pParse, TK_GE, + sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName), pStr1, 0); transferJoinMarkings(pNewExpr1, pExpr); - idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); + idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags); testcase( idxNew1==0 ); exprAnalyze(pSrc, pWC, idxNew1); pNewExpr2 = sqlite3ExprDup(db, pLeft, 0); pNewExpr2 = sqlite3PExpr(pParse, TK_LT, - sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName), + sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName), pStr2, 0); transferJoinMarkings(pNewExpr2, pExpr); - idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); + idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags); testcase( idxNew2==0 ); exprAnalyze(pSrc, pWC, idxNew2); pTerm = &pWC->a[idxTerm]; @@ -116349,7 +117707,7 @@ static int findIndexCol( && p->iTable==iBase ){ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr); - if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){ + if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){ return i; } } @@ -116549,12 +117907,16 @@ static void constructAutomaticIndex( pLoop = pLevel->pWLoop; idxCols = 0; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ + Expr *pExpr = pTerm->pExpr; + assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */ + || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */ + || pLoop->prereq!=0 ); /* table of a LEFT JOIN */ if( pLoop->prereq==0 && (pTerm->wtFlags & TERM_VIRTUAL)==0 - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) - && sqlite3ExprIsTableConstant(pTerm->pExpr, pSrc->iCursor) ){ + && !ExprHasProperty(pExpr, EP_FromJoin) + && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){ pPartial = sqlite3ExprAnd(pParse->db, pPartial, - sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); + sqlite3ExprDup(pParse->db, pExpr, 0)); } if( termCanDriveIndex(pTerm, pSrc, notReady) ){ int iCol = pTerm->u.leftColumn; @@ -116619,7 +117981,7 @@ static void constructAutomaticIndex( idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; + pIdx->azColl[n] = pColl ? pColl->zName : "BINARY"; n++; } } @@ -116841,11 +118203,14 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ ** Estimate the location of a particular key among all keys in an ** index. Store the results in aStat as follows: ** -** aStat[0] Est. number of rows less than pVal -** aStat[1] Est. number of rows equal to pVal +** aStat[0] Est. number of rows less than pRec +** aStat[1] Est. number of rows equal to pRec ** ** Return the index of the sample that is the smallest sample that -** is greater than or equal to pRec. +** is greater than or equal to pRec. Note that this index is not an index +** into the aSample[] array - it is an index into a virtual set of samples +** based on the contents of aSample[] and the number of fields in record +** pRec. */ static int whereKeyStats( Parse *pParse, /* Database connection */ @@ -116856,67 +118221,158 @@ static int whereKeyStats( ){ IndexSample *aSample = pIdx->aSample; int iCol; /* Index of required stats in anEq[] etc. */ + int i; /* Index of first sample >= pRec */ + int iSample; /* Smallest sample larger than or equal to pRec */ int iMin = 0; /* Smallest sample not yet tested */ - int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */ int iTest; /* Next sample to test */ int res; /* Result of comparison operation */ + int nField; /* Number of fields in pRec */ + tRowcnt iLower = 0; /* anLt[] + anEq[] of largest sample pRec is > */ #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif assert( pRec!=0 ); - iCol = pRec->nField - 1; assert( pIdx->nSample>0 ); - assert( pRec->nField>0 && iCol<pIdx->nSampleCol ); + assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol ); + + /* Do a binary search to find the first sample greater than or equal + ** to pRec. If pRec contains a single field, the set of samples to search + ** is simply the aSample[] array. If the samples in aSample[] contain more + ** than one fields, all fields following the first are ignored. + ** + ** If pRec contains N fields, where N is more than one, then as well as the + ** samples in aSample[] (truncated to N fields), the search also has to + ** consider prefixes of those samples. For example, if the set of samples + ** in aSample is: + ** + ** aSample[0] = (a, 5) + ** aSample[1] = (a, 10) + ** aSample[2] = (b, 5) + ** aSample[3] = (c, 100) + ** aSample[4] = (c, 105) + ** + ** Then the search space should ideally be the samples above and the + ** unique prefixes [a], [b] and [c]. But since that is hard to organize, + ** the code actually searches this set: + ** + ** 0: (a) + ** 1: (a, 5) + ** 2: (a, 10) + ** 3: (a, 10) + ** 4: (b) + ** 5: (b, 5) + ** 6: (c) + ** 7: (c, 100) + ** 8: (c, 105) + ** 9: (c, 105) + ** + ** For each sample in the aSample[] array, N samples are present in the + ** effective sample array. In the above, samples 0 and 1 are based on + ** sample aSample[0]. Samples 2 and 3 on aSample[1] etc. + ** + ** Often, sample i of each block of N effective samples has (i+1) fields. + ** Except, each sample may be extended to ensure that it is greater than or + ** equal to the previous sample in the array. For example, in the above, + ** sample 2 is the first sample of a block of N samples, so at first it + ** appears that it should be 1 field in size. However, that would make it + ** smaller than sample 1, so the binary search would not work. As a result, + ** it is extended to two fields. The duplicates that this creates do not + ** cause any problems. + */ + nField = pRec->nField; + iCol = 0; + iSample = pIdx->nSample * nField; do{ - iTest = (iMin+i)/2; - res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec); + int iSamp; /* Index in aSample[] of test sample */ + int n; /* Number of fields in test sample */ + + iTest = (iMin+iSample)/2; + iSamp = iTest / nField; + if( iSamp>0 ){ + /* The proposed effective sample is a prefix of sample aSample[iSamp]. + ** Specifically, the shortest prefix of at least (1 + iTest%nField) + ** fields that is greater than the previous effective sample. */ + for(n=(iTest % nField) + 1; n<nField; n++){ + if( aSample[iSamp-1].anLt[n-1]!=aSample[iSamp].anLt[n-1] ) break; + } + }else{ + n = iTest + 1; + } + + pRec->nField = n; + res = sqlite3VdbeRecordCompare(aSample[iSamp].n, aSample[iSamp].p, pRec); if( res<0 ){ + iLower = aSample[iSamp].anLt[n-1] + aSample[iSamp].anEq[n-1]; + iMin = iTest+1; + }else if( res==0 && n<nField ){ + iLower = aSample[iSamp].anLt[n-1]; iMin = iTest+1; + res = -1; }else{ - i = iTest; + iSample = iTest; + iCol = n-1; } - }while( res && iMin<i ); + }while( res && iMin<iSample ); + i = iSample / nField; #ifdef SQLITE_DEBUG /* The following assert statements check that the binary search code ** above found the right answer. This block serves no purpose other ** than to invoke the asserts. */ - if( res==0 ){ - /* If (res==0) is true, then sample $i must be equal to pRec */ - assert( i<pIdx->nSample ); - assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) - || pParse->db->mallocFailed ); - }else{ - /* Otherwise, pRec must be smaller than sample $i and larger than - ** sample ($i-1). */ - assert( i==pIdx->nSample - || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 - || pParse->db->mallocFailed ); - assert( i==0 - || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 - || pParse->db->mallocFailed ); + if( pParse->db->mallocFailed==0 ){ + if( res==0 ){ + /* If (res==0) is true, then pRec must be equal to sample i. */ + assert( i<pIdx->nSample ); + assert( iCol==nField-1 ); + pRec->nField = nField; + assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) + || pParse->db->mallocFailed + ); + }else{ + /* Unless i==pIdx->nSample, indicating that pRec is larger than + ** all samples in the aSample[] array, pRec must be smaller than the + ** (iCol+1) field prefix of sample i. */ + assert( i<=pIdx->nSample && i>=0 ); + pRec->nField = iCol+1; + assert( i==pIdx->nSample + || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 + || pParse->db->mallocFailed ); + + /* if i==0 and iCol==0, then record pRec is smaller than all samples + ** in the aSample[] array. Otherwise, if (iCol>0) then pRec must + ** be greater than or equal to the (iCol) field prefix of sample i. + ** If (i>0), then pRec must also be greater than sample (i-1). */ + if( iCol>0 ){ + pRec->nField = iCol; + assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0 + || pParse->db->mallocFailed ); + } + if( i>0 ){ + pRec->nField = nField; + assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 + || pParse->db->mallocFailed ); + } + } } #endif /* ifdef SQLITE_DEBUG */ - /* At this point, aSample[i] is the first sample that is greater than - ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less - ** than pVal. If aSample[i]==pVal, then res==0. - */ if( res==0 ){ + /* Record pRec is equal to sample i */ + assert( iCol==nField-1 ); aStat[0] = aSample[i].anLt[iCol]; aStat[1] = aSample[i].anEq[iCol]; }else{ - tRowcnt iLower, iUpper, iGap; - if( i==0 ){ - iLower = 0; - iUpper = aSample[0].anLt[iCol]; + /* At this point, the (iCol+1) field prefix of aSample[i] is the first + ** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec + ** is larger than all samples in the array. */ + tRowcnt iUpper, iGap; + if( i>=pIdx->nSample ){ + iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); }else{ - i64 nRow0 = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]); - iUpper = i>=pIdx->nSample ? nRow0 : aSample[i].anLt[iCol]; - iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol]; + iUpper = aSample[i].anLt[iCol]; } - aStat[1] = pIdx->aAvgEq[iCol]; + if( iLower>=iUpper ){ iGap = 0; }else{ @@ -116928,7 +118384,11 @@ static int whereKeyStats( iGap = iGap/3; } aStat[0] = iLower + iGap; + aStat[1] = pIdx->aAvgEq[iCol]; } + + /* Restore the pRec->nField value before returning. */ + pRec->nField = nField; return i; } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ @@ -117402,20 +118862,43 @@ static int whereInScanEst( ** but joins might run a little slower. The trick is to disable as much ** as we can without disabling too much. If we disabled in (1), we'd get ** the wrong answer. See ticket #813. +** +** If all the children of a term are disabled, then that term is also +** automatically disabled. In this way, terms get disabled if derived +** virtual terms are tested first. For example: +** +** x GLOB 'abc*' AND x>='abc' AND x<'acd' +** \___________/ \______/ \_____/ +** parent child1 child2 +** +** Only the parent term was in the original WHERE clause. The child1 +** and child2 terms were added by the LIKE optimization. If both of +** the virtual child terms are valid, then testing of the parent can be +** skipped. +** +** Usually the parent term is marked as TERM_CODED. But if the parent +** term was originally TERM_LIKE, then the parent gets TERM_LIKECOND instead. +** The TERM_LIKECOND marking indicates that the term should be coded inside +** a conditional such that is only evaluated on the second pass of a +** LIKE-optimization loop, when scanning BLOBs instead of strings. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ - if( pTerm + int nLoop = 0; + while( pTerm && (pTerm->wtFlags & TERM_CODED)==0 && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) && (pLevel->notReady & pTerm->prereqAll)==0 ){ - pTerm->wtFlags |= TERM_CODED; - if( pTerm->iParent>=0 ){ - WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent]; - if( (--pOther->nChild)==0 ){ - disableTerm(pLevel, pOther); - } + if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ + pTerm->wtFlags |= TERM_LIKECOND; + }else{ + pTerm->wtFlags |= TERM_CODED; } + if( pTerm->iParent<0 ) break; + pTerm = &pTerm->pWC->a[pTerm->iParent]; + pTerm->nChild--; + if( pTerm->nChild!=0 ) break; + nLoop++; } } @@ -117794,8 +119277,7 @@ static int explainOneScan( || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); - sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); - str.db = db; + sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN"); if( pItem->pSelect ){ sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId); @@ -117899,7 +119381,34 @@ static void addScanStatus( # define addScanStatus(a, b, c, d) ((void)d) #endif - +/* +** If the most recently coded instruction is a constant range contraint +** that originated from the LIKE optimization, then change the P3 to be +** pLoop->iLikeRepCntr and set P5. +** +** The LIKE optimization trys to evaluate "x LIKE 'abc%'" as a range +** expression: "x>='ABC' AND x<'abd'". But this requires that the range +** scan loop run twice, once for strings and a second time for BLOBs. +** The OP_String opcodes on the second pass convert the upper and lower +** bound string contants to blobs. This routine makes the necessary changes +** to the OP_String opcodes for that to happen. +*/ +static void whereLikeOptimizationStringFixup( + Vdbe *v, /* prepared statement under construction */ + WhereLevel *pLevel, /* The loop that contains the LIKE operator */ + WhereTerm *pTerm /* The upper or lower bound just coded */ +){ + if( pTerm->wtFlags & TERM_LIKEOPT ){ + VdbeOp *pOp; + assert( pLevel->iLikeRepCntr>0 ); + pOp = sqlite3VdbeGetOp(v, -1); + assert( pOp!=0 ); + assert( pOp->opcode==OP_String8 + || pTerm->pWC->pWInfo->pParse->db->mallocFailed ); + pOp->p3 = pLevel->iLikeRepCntr; + pOp->p5 = 1; + } +} /* ** Generate code for the start of the iLevel-th loop in the WHERE clause @@ -118229,10 +119738,25 @@ static Bitmask codeOneLoopStart( if( pLoop->wsFlags & WHERE_BTM_LIMIT ){ pRangeStart = pLoop->aLTerm[j++]; nExtraReg = 1; + /* Like optimization range constraints always occur in pairs */ + assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 || + (pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 ); } if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ pRangeEnd = pLoop->aLTerm[j++]; nExtraReg = 1; + if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){ + assert( pRangeStart!=0 ); /* LIKE opt constraints */ + assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */ + pLevel->iLikeRepCntr = ++pParse->nMem; + testcase( bRev ); + testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC ); + sqlite3VdbeAddOp2(v, OP_Integer, + bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC), + pLevel->iLikeRepCntr); + VdbeComment((v, "LIKE loop counter")); + pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v); + } if( pRangeStart==0 && (j = pIdx->aiColumn[nEq])>=0 && pIdx->pTable->aCol[j].notNull==0 @@ -118275,6 +119799,7 @@ static Bitmask codeOneLoopStart( if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); + whereLikeOptimizationStringFixup(v, pLevel, pRangeStart); if( (pRangeStart->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -118320,6 +119845,7 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); + whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 && sqlite3ExprCanBeNull(pRight) ){ @@ -118547,7 +120073,8 @@ static Bitmask codeOneLoopStart( */ wctrlFlags = WHERE_OMIT_OPEN_CLOSE | WHERE_FORCE_TABLE - | WHERE_ONETABLE_ONLY; + | WHERE_ONETABLE_ONLY + | WHERE_NO_AUTOINDEX; for(ii=0; ii<pOrWc->nTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ @@ -118709,6 +120236,7 @@ static Bitmask codeOneLoopStart( */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; + int skipLikeAddr = 0; testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; @@ -118723,7 +120251,13 @@ static Bitmask codeOneLoopStart( if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ continue; } + if( pTerm->wtFlags & TERM_LIKECOND ){ + assert( pLevel->iLikeRepCntr>0 ); + skipLikeAddr = sqlite3VdbeAddOp1(v, OP_IfNot, pLevel->iLikeRepCntr); + VdbeCoverage(v); + } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); + if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr); pTerm->wtFlags |= TERM_CODED; } @@ -118942,6 +120476,13 @@ static void whereLoopDelete(sqlite3 *db, WhereLoop *p){ */ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ if( ALWAYS(pWInfo) ){ + int i; + for(i=0; i<pWInfo->nLevel; i++){ + WhereLevel *pLevel = &pWInfo->a[i]; + if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){ + sqlite3DbFree(db, pLevel->u.in.aInLoop); + } + } whereClauseClear(&pWInfo->sWC); while( pWInfo->pLoops ){ WhereLoop *p = pWInfo->pLoops; @@ -119388,6 +120929,10 @@ static int whereLoopAddBtreeIndex( } if( pTerm->prereqRight & pNew->maskSelf ) continue; + /* Do not allow the upper bound of a LIKE optimization range constraint + ** to mix with a lower range bound from some other source */ + if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; + pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->nLTerm = saved_nLTerm; @@ -119417,7 +120962,7 @@ static int whereLoopAddBtreeIndex( }else if( eOp & (WO_EQ) ){ pNew->wsFlags |= WHERE_COLUMN_EQ; if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){ - if( iCol>=0 && !IsUniqueIndex(pProbe) ){ + if( iCol>=0 && pProbe->uniqNotNull==0 ){ pNew->wsFlags |= WHERE_UNQ_WANTED; }else{ pNew->wsFlags |= WHERE_ONEROW; @@ -119431,6 +120976,17 @@ static int whereLoopAddBtreeIndex( pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT; pBtm = pTerm; pTop = 0; + if( pTerm->wtFlags & TERM_LIKEOPT ){ + /* Range contraints that come from the LIKE optimization are + ** always used in pairs. */ + pTop = &pTerm[1]; + assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm ); + assert( pTop->wtFlags & TERM_LIKEOPT ); + assert( pTop->eOperator==WO_LT ); + if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */ + pNew->aLTerm[pNew->nLTerm++] = pTop; + pNew->wsFlags |= WHERE_TOP_LIMIT; + } }else{ assert( eOp & (WO_LT|WO_LE) ); testcase( eOp & WO_LT ); @@ -119632,8 +121188,9 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ - if( sqlite3ExprImpliesExpr(pTerm->pExpr, pWhere, iTab) - && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) + Expr *pExpr = pTerm->pExpr; + if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab) + && (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) ){ return 1; } @@ -119740,6 +121297,7 @@ static int whereLoopAddBtree( #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet + && (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIndex==0 && !pSrc->viaCoroutine @@ -120623,10 +122181,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ /* Seed the search with a single WherePath containing zero WhereLoops. ** - ** TUNING: Do not let the number of iterations go above 25. If the cost - ** of computing an automatic index is not paid back within the first 25 + ** TUNING: Do not let the number of iterations go above 28. If the cost + ** of computing an automatic index is not paid back within the first 28 ** rows, then do not use the automatic index. */ - aFrom[0].nRow = MIN(pParse->nQueryLoop, 46); assert( 46==sqlite3LogEst(25) ); + aFrom[0].nRow = MIN(pParse->nQueryLoop, 48); assert( 48==sqlite3LogEst(28) ); nFrom = 1; assert( aFrom[0].isOrdered==0 ); if( nOrderBy ){ @@ -120864,7 +122422,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ pWInfo->revMask = pFrom->revLoop; } if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) - && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr + && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0 ){ Bitmask revMask = 0; int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, @@ -121269,7 +122827,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( } #ifdef WHERETRACE_ENABLED /* !=0 */ if( sqlite3WhereTrace ){ - int ii; sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); if( pWInfo->nOBSat>0 ){ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask); @@ -121424,6 +122981,12 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); + if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 + && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 + && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 + ){ + sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */ + } VdbeComment((v, "%s", pIx->zName)); } } @@ -121516,7 +123079,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen); sqlite3VdbeJumpHere(v, pIn->addrInTop-1); } - sqlite3DbFree(db, pLevel->u.in.aInLoop); } sqlite3VdbeResolveLabel(v, pLevel->addrBrk); if( pLevel->addrSkip ){ @@ -121525,6 +123087,16 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeJumpHere(v, pLevel->addrSkip); sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); } + if( pLevel->addrLikeRep ){ + int op; + if( sqlite3VdbeGetOp(v, pLevel->addrLikeRep-1)->p1 ){ + op = OP_DecrJumpZero; + }else{ + op = OP_JumpZeroIncr; + } + sqlite3VdbeAddOp2(v, op, pLevel->iLikeRepCntr, pLevel->addrLikeRep); + VdbeCoverage(v); + } if( pLevel->iLeftJoin ){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 @@ -121718,6 +123290,28 @@ struct TrigEvent { int a; IdList * b; }; struct AttachKey { int type; Token key; }; + /* + ** For a compound SELECT statement, make sure p->pPrior->pNext==p for + ** all elements in the list. And make sure list length does not exceed + ** SQLITE_LIMIT_COMPOUND_SELECT. + */ + static void parserDoubleLinkSelect(Parse *pParse, Select *p){ + if( p->pPrior ){ + Select *pNext = 0, *pLoop; + int mxSelect, cnt = 0; + for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ + pLoop->pNext = pNext; + pLoop->selFlags |= SF_Compound; + } + if( (p->selFlags & SF_MultiValue)==0 && + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 && + cnt>mxSelect + ){ + sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); + } + } + } + /* This is a utility routine used to set the ExprSpan.zStart and ** ExprSpan.zEnd values of pOut so that the span covers the complete ** range of text beginning with pStart and going to the end of pEnd. @@ -124034,27 +125628,10 @@ static void yy_reduce( break; case 112: /* select ::= with selectnowith */ { - Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; + Select *p = yymsp[0].minor.yy3; if( p ){ - int cnt = 0, mxSelect; p->pWith = yymsp[-1].minor.yy59; - if( p->pPrior ){ - u16 allValues = SF_Values; - pNext = 0; - for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ - pLoop->pNext = pNext; - pLoop->selFlags |= SF_Compound; - allValues &= pLoop->selFlags; - } - if( allValues ){ - p->selFlags |= SF_AllValues; - }else if( - (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 - && cnt>mxSelect - ){ - sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); - } - } + parserDoubleLinkSelect(pParse, p); }else{ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } @@ -124072,12 +125649,14 @@ static void yy_reduce( SrcList *pFrom; Token x; x.n = 0; + parserDoubleLinkSelect(pParse, pRhs); pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0); } if( pRhs ){ pRhs->op = (u8)yymsp[-1].minor.yy328; pRhs->pPrior = yymsp[-2].minor.yy3; + pRhs->selFlags &= ~SF_MultiValue; if( yymsp[-1].minor.yy328!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3); @@ -124124,13 +125703,16 @@ static void yy_reduce( break; case 121: /* values ::= values COMMA LP exprlist RP */ { - Select *pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy3; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values|SF_MultiValue,0,0); + if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; - pRight->pPrior = yymsp[-4].minor.yy3; + pLeft = yymsp[-4].minor.yy3; + pRight->pPrior = pLeft; yygotominor.yy3 = pRight; }else{ - yygotominor.yy3 = yymsp[-4].minor.yy3; + yygotominor.yy3 = pLeft; } } break; @@ -124415,7 +125997,7 @@ static void yy_reduce( break; case 193: /* expr ::= expr COLLATE ID|STRING */ { - yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0, 1); yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart; yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } @@ -124578,7 +126160,7 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy14; - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); } @@ -124593,8 +126175,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124607,8 +126189,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124623,8 +126205,8 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy346.pExpr, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); - ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SrcListDelete(pParse->db, pSrc); } @@ -124638,8 +126220,8 @@ static void yy_reduce( Expr *p = yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ p->x.pSelect = yymsp[-1].minor.yy3; - ExprSetProperty(p, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, p); + ExprSetProperty(p, EP_xIsSelect|EP_Subquery); + sqlite3ExprSetHeightAndFlags(pParse, p); }else{ sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } @@ -124652,7 +126234,7 @@ static void yy_reduce( yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy132, 0, 0); if( yygotominor.yy346.pExpr ){ yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy132 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy132) : yymsp[-2].minor.yy14; - sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); + sqlite3ExprSetHeightAndFlags(pParse, yygotominor.yy346.pExpr); }else{ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy132); @@ -124695,7 +126277,7 @@ static void yy_reduce( break; case 244: /* idxlist ::= idxlist COMMA nm collate sortorder */ { - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); + Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1); yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, p); sqlite3ExprListSetName(pParse,yygotominor.yy14,&yymsp[-2].minor.yy0,1); sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); @@ -124704,7 +126286,7 @@ static void yy_reduce( break; case 245: /* idxlist ::= nm collate sortorder */ { - Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0); + Expr *p = sqlite3ExprAddCollateToken(pParse, 0, &yymsp[-1].minor.yy0, 1); yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, p); sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); @@ -125894,10 +127476,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string */ - -#ifdef SQLITE_ENABLE_API_ARMOR - if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT; -#endif + assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; if( db->nVdbeActive==0 ){ db->u1.isInterrupted = 0; @@ -125937,10 +127516,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr break; } case TK_ILLEGAL: { - sqlite3DbFree(db, *pzErrMsg); - *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"", + sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"", &pParse->sLastToken); - nErr++; goto abort_parse; } case TK_SEMI: { @@ -125958,17 +127535,22 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr } } abort_parse: - if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){ + assert( nErr==0 ); + if( zSql[i]==0 && pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ if( lastTokenParsed!=TK_SEMI ){ sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse); pParse->zTail = &zSql[i]; } - sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); + if( pParse->rc==SQLITE_OK && db->mallocFailed==0 ){ + sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse); + } } #ifdef YYTRACKMAXSTACKDEPTH + sqlite3_mutex_enter(sqlite3MallocMutex()); sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK, sqlite3ParserStackPeak(pEngine) ); + sqlite3_mutex_leave(sqlite3MallocMutex()); #endif /* YYDEBUG */ sqlite3ParserFree(pEngine, sqlite3_free); db->lookaside.bEnabled = enableLookaside; @@ -126022,9 +127604,7 @@ abort_parse: pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } - if( nErr>0 && pParse->rc==SQLITE_OK ){ - pParse->rc = SQLITE_ERROR; - } + assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; } @@ -126132,7 +127712,7 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[]; ** to recognize the end of a trigger can be omitted. All we have to do ** is look for a semicolon that is not part of an string or comment. */ -SQLITE_API int sqlite3_complete(const char *zSql){ +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *zSql){ u8 state = 0; /* Current state, using numbers defined in header comment */ u8 token; /* Value of the next token */ @@ -126297,10 +127877,10 @@ SQLITE_API int sqlite3_complete(const char *zSql){ ** above, except that the parameter is required to be UTF-16 encoded, not ** UTF-8. */ -SQLITE_API int sqlite3_complete16(const void *zSql){ +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *zSql){ sqlite3_value *pVal; char const *zSql8; - int rc = SQLITE_NOMEM; + int rc; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); @@ -126447,24 +128027,36 @@ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; /* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns ** a pointer to the to the sqlite3_version[] string constant. */ -SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; } +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void){ return sqlite3_version; } /* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a ** pointer to a string constant whose value is the same as the ** SQLITE_SOURCE_ID C preprocessor macro. */ -SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function ** returns an integer equal to SQLITE_VERSION_NUMBER. */ -SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } /* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns ** zero if and only if SQLite was compiled with mutexing code omitted due to ** the SQLITE_THREADSAFE compile-time option being set to 0. */ -SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } + +/* +** When compiling the test fixture or with debugging enabled (on Win32), +** this variable being set to non-zero will cause OSTRACE macros to emit +** extra diagnostic information. +*/ +#ifdef SQLITE_HAVE_OS_TRACE +# ifndef SQLITE_DEBUG_OS_TRACE +# define SQLITE_DEBUG_OS_TRACE 0 +# endif + int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE; +#endif #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) /* @@ -126473,7 +128065,7 @@ SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } ** I/O active are written using this function. These messages ** are intended for debugging activity only. */ -/* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0; +SQLITE_API void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...) = 0; #endif /* @@ -126525,7 +128117,7 @@ SQLITE_API char *sqlite3_data_directory = 0; ** * Recursive calls to this routine from thread X return immediately ** without blocking. */ -SQLITE_API int sqlite3_initialize(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void){ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ int rc; /* Result code */ #ifdef SQLITE_EXTRA_INIT @@ -126539,6 +128131,11 @@ SQLITE_API int sqlite3_initialize(void){ } #endif + /* If the following assert() fails on some obscure processor/compiler + ** combination, the work-around is to set the correct pointer + ** size at compile-time using -DSQLITE_PTRSIZE=n compile-time option */ + assert( SQLITE_PTRSIZE==sizeof(char*) ); + /* If SQLite is already completely initialized, then this call ** to sqlite3_initialize() should be a no-op. But the initialization ** must be complete. So isInit must not be set until the very end @@ -126681,7 +128278,7 @@ SQLITE_API int sqlite3_initialize(void){ ** on when SQLite is already shut down. If SQLite is already shut down ** when this routine is invoked, then this routine is a harmless no-op. */ -SQLITE_API int sqlite3_shutdown(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void){ #ifdef SQLITE_OMIT_WSD int rc = sqlite3_wsd_init(4096, 24); if( rc!=SQLITE_OK ){ @@ -126735,7 +128332,7 @@ SQLITE_API int sqlite3_shutdown(void){ ** threadsafe. Failure to heed these warnings can lead to unpredictable ** behavior. */ -SQLITE_API int sqlite3_config(int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_config(int op, ...){ va_list ap; int rc = SQLITE_OK; @@ -126751,26 +128348,28 @@ SQLITE_API int sqlite3_config(int op, ...){ */ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-54466-46756 */ case SQLITE_CONFIG_SINGLETHREAD: { - /* Disable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 0; - sqlite3GlobalConfig.bFullMutex = 0; + /* EVIDENCE-OF: R-02748-19096 This option sets the threading mode to + ** Single-thread. */ + sqlite3GlobalConfig.bCoreMutex = 0; /* Disable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */ break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */ case SQLITE_CONFIG_MULTITHREAD: { - /* Disable mutexing of database connections */ - /* Enable mutexing of core data structures */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 0; + /* EVIDENCE-OF: R-14374-42468 This option sets the threading mode to + ** Multi-thread. */ + sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */ break; } #endif #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */ case SQLITE_CONFIG_SERIALIZED: { - /* Enable all mutexing */ - sqlite3GlobalConfig.bCoreMutex = 1; - sqlite3GlobalConfig.bFullMutex = 1; + /* EVIDENCE-OF: R-41220-51800 This option sets the threading mode to + ** Serialized. */ + sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */ + sqlite3GlobalConfig.bFullMutex = 1; /* Enable mutex on connections */ break; } #endif @@ -126882,7 +128481,8 @@ SQLITE_API int sqlite3_config(int op, ...){ case SQLITE_CONFIG_HEAP: { /* EVIDENCE-OF: R-19854-42126 There are three arguments to ** SQLITE_CONFIG_HEAP: An 8-byte aligned pointer to the memory, the - ** number of bytes in the memory buffer, and the minimum allocation size. */ + ** number of bytes in the memory buffer, and the minimum allocation size. + */ sqlite3GlobalConfig.pHeap = va_arg(ap, void*); sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); @@ -126987,7 +128587,9 @@ SQLITE_API int sqlite3_config(int op, ...){ ** compile-time maximum mmap size set by the SQLITE_MAX_MMAP_SIZE ** compile-time option. */ - if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ) mxMmap = SQLITE_MAX_MMAP_SIZE; + if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ){ + mxMmap = SQLITE_MAX_MMAP_SIZE; + } if( szMmap<0 ) szMmap = SQLITE_DEFAULT_MMAP_SIZE; if( szMmap>mxMmap) szMmap = mxMmap; sqlite3GlobalConfig.mxMmap = mxMmap; @@ -127087,7 +128689,7 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ /* ** Return the mutex associated with a database connection. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127101,7 +128703,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){ ** Free up as much memory as we can from the given database ** connection. */ -SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3 *db){ int i; #ifdef SQLITE_ENABLE_API_ARMOR @@ -127124,7 +128726,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){ /* ** Configuration settings for an individual database connection */ -SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); @@ -127243,7 +128845,7 @@ static int nocaseCollatingFunc( /* ** Return the ROWID of the most recent insert */ -SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ +SQLITE_API sqlite_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127256,7 +128858,7 @@ SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ /* ** Return the number of changes in the most recent call to sqlite3_exec(). */ -SQLITE_API int sqlite3_changes(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127269,7 +128871,7 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){ /* ** Return the number of changes since the database handle was opened. */ -SQLITE_API int sqlite3_total_changes(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -127411,8 +129013,8 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ ** unclosed resources, and arranges for deallocation when the last ** prepare statement or sqlite3_backup closes. */ -SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); } -SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); } +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); } +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); } /* @@ -127595,7 +129197,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ ** Return a static string containing the name corresponding to the error code ** specified in the argument. */ -#if (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) || defined(SQLITE_TEST) +#if defined(SQLITE_NEED_ERR_NAME) SQLITE_PRIVATE const char *sqlite3ErrName(int rc){ const char *zName = 0; int i, origRc = rc; @@ -127819,13 +129421,13 @@ SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){ ** This routine sets the busy callback for an Sqlite database to the ** given callback function with the given argument. */ -SQLITE_API int sqlite3_busy_handler( +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler( sqlite3 *db, int (*xBusy)(void*,int), void *pArg ){ #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE; + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif sqlite3_mutex_enter(db->mutex); db->busyHandler.xFunc = xBusy; @@ -127842,7 +129444,7 @@ SQLITE_API int sqlite3_busy_handler( ** given callback function with the given argument. The progress callback will ** be invoked every nOps opcodes. */ -SQLITE_API void sqlite3_progress_handler( +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler( sqlite3 *db, int nOps, int (*xProgress)(void*), @@ -127873,7 +129475,7 @@ SQLITE_API void sqlite3_progress_handler( ** This routine installs a default busy handler that waits for the ** specified number of milliseconds before returning 0. */ -SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3 *db, int ms){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif @@ -127889,7 +129491,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ /* ** Cause any pending operation to stop at its earliest opportunity. */ -SQLITE_API void sqlite3_interrupt(sqlite3 *db){ +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -128006,7 +129608,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( /* ** Create new user functions. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, @@ -128020,7 +129622,7 @@ SQLITE_API int sqlite3_create_function( xFinal, 0); } -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunc, int nArg, @@ -128063,7 +129665,7 @@ SQLITE_API int sqlite3_create_function_v2( } #ifndef SQLITE_OMIT_UTF16 -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -128103,7 +129705,7 @@ SQLITE_API int sqlite3_create_function16( ** A global function must exist in order for name resolution to work ** properly. */ -SQLITE_API int sqlite3_overload_function( +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function( sqlite3 *db, const char *zName, int nArg @@ -128135,7 +129737,7 @@ SQLITE_API int sqlite3_overload_function( ** trace is a pointer to a function that is invoked at the start of each ** SQL statement. */ -SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){ void *pOld; #ifdef SQLITE_ENABLE_API_ARMOR @@ -128159,7 +129761,7 @@ SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), v ** profile is a pointer to a function that is invoked at the conclusion of ** each SQL statement that is run. */ -SQLITE_API void *sqlite3_profile( +SQLITE_API void *SQLITE_STDCALL sqlite3_profile( sqlite3 *db, void (*xProfile)(void*,const char*,sqlite_uint64), void *pArg @@ -128186,7 +129788,7 @@ SQLITE_API void *sqlite3_profile( ** If the invoked function returns non-zero, then the commit becomes a ** rollback. */ -SQLITE_API void *sqlite3_commit_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook( sqlite3 *db, /* Attach the hook to this database */ int (*xCallback)(void*), /* Function to invoke on each commit */ void *pArg /* Argument to the function */ @@ -128211,7 +129813,7 @@ SQLITE_API void *sqlite3_commit_hook( ** Register a callback to be invoked each time a row is updated, ** inserted or deleted using this database connection. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3 *db, /* Attach the hook to this database */ void (*xCallback)(void*,int,char const *,char const *,sqlite_int64), void *pArg /* Argument to the function */ @@ -128236,7 +129838,7 @@ SQLITE_API void *sqlite3_update_hook( ** Register a callback to be invoked each time a transaction is rolled ** back by this database connection. */ -SQLITE_API void *sqlite3_rollback_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook( sqlite3 *db, /* Attach the hook to this database */ void (*xCallback)(void*), /* Callback function */ void *pArg /* Argument to the function */ @@ -128290,7 +129892,7 @@ SQLITE_PRIVATE int sqlite3WalDefaultHook( ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ #ifdef SQLITE_OMIT_WAL UNUSED_PARAMETER(db); UNUSED_PARAMETER(nFrame); @@ -128311,7 +129913,7 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ ** Register a callback to be invoked each time a transaction is written ** into the write-ahead-log by this database connection. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3 *db, /* Attach the hook to this db handle */ int(*xCallback)(void *, sqlite3*, const char*, int), void *pArg /* First argument passed to xCallback() */ @@ -128338,7 +129940,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** Checkpoint database zDb. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -128393,7 +129995,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** to contains a zero-length string, all attached databases are ** checkpointed. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ /* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to ** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */ return sqlite3_wal_checkpoint_v2(db,zDb,SQLITE_CHECKPOINT_PASSIVE,0,0); @@ -128482,7 +130084,7 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){ ** Return UTF-8 encoded English language explanation of the most recent ** error. */ -SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3 *db){ const char *z; if( !db ){ return sqlite3ErrStr(SQLITE_NOMEM); @@ -128510,7 +130112,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ ** Return UTF-16 encoded English language explanation of the most recent ** error. */ -SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3 *db){ static const u16 outOfMem[] = { 'o', 'u', 't', ' ', 'o', 'f', ' ', 'm', 'e', 'm', 'o', 'r', 'y', 0 }; @@ -128555,7 +130157,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ ** Return the most recent error code generated by an SQLite routine. If NULL is ** passed to this function, we assume a malloc() failed during sqlite3_open(). */ -SQLITE_API int sqlite3_errcode(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } @@ -128564,7 +130166,7 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db){ } return db->errCode & db->errMask; } -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db){ if( db && !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } @@ -128579,7 +130181,7 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ ** argument. For now, this simply calls the internal sqlite3ErrStr() ** function. */ -SQLITE_API const char *sqlite3_errstr(int rc){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int rc){ return sqlite3ErrStr(rc); } @@ -128727,7 +130329,7 @@ static const int aHardLimit[] = { ** It merely prevents new constructs that exceed the limit ** from forming. */ -SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; #ifdef SQLITE_ENABLE_API_ARMOR @@ -128820,18 +130422,30 @@ SQLITE_PRIVATE int sqlite3ParseUri( int eState; /* Parser state when parsing URI */ int iIn; /* Input character index */ int iOut = 0; /* Output character index */ - int nByte = nUri+2; /* Bytes of space to allocate */ + u64 nByte = nUri+2; /* Bytes of space to allocate */ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen ** method that there may be extra parameters following the file-name. */ flags |= SQLITE_OPEN_URI; for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); - zFile = sqlite3_malloc(nByte); + zFile = sqlite3_malloc64(nByte); if( !zFile ) return SQLITE_NOMEM; iIn = 5; -#ifndef SQLITE_ALLOW_URI_AUTHORITY +#ifdef SQLITE_ALLOW_URI_AUTHORITY + if( strncmp(zUri+5, "///", 3)==0 ){ + iIn = 7; + /* The following condition causes URIs with five leading / characters + ** like file://///host/path to be converted into UNCs like //host/path. + ** The correct URI for that UNC has only two or four leading / characters + ** file://host/path or file:////host/path. But 5 leading slashes is a + ** common error, we are told, so we handle it as a special case. */ + if( strncmp(zUri+7, "///", 3)==0 ){ iIn++; } + }else if( strncmp(zUri+5, "//localhost/", 12)==0 ){ + iIn = 16; + } +#else /* Discard the scheme and authority segments of the URI. */ if( zUri[5]=='/' && zUri[6]=='/' ){ iIn = 7; @@ -128981,7 +130595,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( } }else{ - zFile = sqlite3_malloc(nUri+2); + zFile = sqlite3_malloc64(nUri+2); if( !zFile ) return SQLITE_NOMEM; memcpy(zFile, zUri, nUri); zFile[nUri] = '\0'; @@ -129118,6 +130732,9 @@ static int openDatabase( #if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX | SQLITE_AutoIndex #endif +#if SQLITE_DEFAULT_CKPTFULLFSYNC + | SQLITE_CkptFullFSync +#endif #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif @@ -129250,6 +130867,13 @@ static int openDatabase( } #endif +#ifdef SQLITE_ENABLE_DBSTAT_VTAB + if( !db->mallocFailed && rc==SQLITE_OK){ + int sqlite3_dbstat_register(sqlite3*); + rc = sqlite3_dbstat_register(db); + } +#endif + /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking ** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking ** mode. Doing nothing at all also makes NORMAL the default. @@ -129271,7 +130895,8 @@ static int openDatabase( opendb_out: sqlite3_free(zOpen); if( db ){ - assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); + assert( db->mutex!=0 || isThreadsafe==0 + || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); } rc = sqlite3_errcode(db); @@ -129296,14 +130921,14 @@ opendb_out: /* ** Open a new database handle. */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *zFilename, sqlite3 **ppDb ){ return openDatabase(zFilename, ppDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); } -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -129316,7 +130941,7 @@ SQLITE_API int sqlite3_open_v2( /* ** Open a new database handle. */ -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *zFilename, sqlite3 **ppDb ){ @@ -129355,7 +130980,7 @@ SQLITE_API int sqlite3_open16( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3* db, const char *zName, int enc, @@ -129368,7 +130993,7 @@ SQLITE_API int sqlite3_create_collation( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3* db, const char *zName, int enc, @@ -129393,7 +131018,7 @@ SQLITE_API int sqlite3_create_collation_v2( /* ** Register a new collation sequence with the database handle db. */ -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3* db, const void *zName, int enc, @@ -129423,7 +131048,7 @@ SQLITE_API int sqlite3_create_collation16( ** Register a collation sequence factory callback with the database handle ** db. Replace any previously installed collation sequence factory. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3 *db, void *pCollNeededArg, void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) @@ -129444,7 +131069,7 @@ SQLITE_API int sqlite3_collation_needed( ** Register a collation sequence factory callback with the database handle ** db. Replace any previously installed collation sequence factory. */ -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3 *db, void *pCollNeededArg, void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) @@ -129466,7 +131091,7 @@ SQLITE_API int sqlite3_collation_needed16( ** This function is now an anachronism. It used to be used to recover from a ** malloc() failure, but SQLite now does this automatically. */ -SQLITE_API int sqlite3_global_recover(void){ +SQLITE_API int SQLITE_STDCALL sqlite3_global_recover(void){ return SQLITE_OK; } #endif @@ -129477,7 +131102,7 @@ SQLITE_API int sqlite3_global_recover(void){ ** by default. Autocommit is disabled by a BEGIN statement and reenabled ** by the next COMMIT or ROLLBACK. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){ +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3 *db){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; @@ -129529,7 +131154,7 @@ SQLITE_PRIVATE int sqlite3CantopenError(int lineno){ ** SQLite no longer uses thread-specific data so this routine is now a ** no-op. It is retained for historical compatibility. */ -SQLITE_API void sqlite3_thread_cleanup(void){ +SQLITE_API void SQLITE_STDCALL sqlite3_thread_cleanup(void){ } #endif @@ -129537,7 +131162,7 @@ SQLITE_API void sqlite3_thread_cleanup(void){ ** Return meta information about a specific column of a database table. ** See comment in sqlite3.h (sqlite.h.in) for details. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -129553,13 +131178,19 @@ SQLITE_API int sqlite3_table_column_metadata( Table *pTab = 0; Column *pCol = 0; int iCol = 0; - char const *zDataType = 0; char const *zCollSeq = 0; int notnull = 0; int primarykey = 0; int autoinc = 0; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) || zTableName==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + /* Ensure the database schema has been loaded */ sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); @@ -129649,7 +131280,7 @@ error_out: /* ** Sleep for a little while. Return the amount of time slept. */ -SQLITE_API int sqlite3_sleep(int ms){ +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int ms){ sqlite3_vfs *pVfs; int rc; pVfs = sqlite3_vfs_find(0); @@ -129665,7 +131296,7 @@ SQLITE_API int sqlite3_sleep(int ms){ /* ** Enable or disable the extended result codes. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3 *db, int onoff){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif @@ -129678,7 +131309,7 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){ /* ** Invoke the xFileControl method on a particular database. */ -SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ int rc = SQLITE_ERROR; Btree *pBtree; @@ -129706,13 +131337,13 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo sqlite3BtreeLeave(pBtree); } sqlite3_mutex_leave(db->mutex); - return rc; + return rc; } /* ** Interface to the testing logic. */ -SQLITE_API int sqlite3_test_control(int op, ...){ +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...){ int rc = 0; #ifndef SQLITE_OMIT_BUILTIN_TEST va_list ap; @@ -130009,6 +131640,35 @@ SQLITE_API int sqlite3_test_control(int op, ...){ if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR; break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum); + ** + ** This test control is used to create imposter tables. "db" is a pointer + ** to the database connection. dbName is the database name (ex: "main" or + ** "temp") which will receive the imposter. "onOff" turns imposter mode on + ** or off. "tnum" is the root page of the b-tree to which the imposter + ** table should connect. + ** + ** Enable imposter mode only when the schema has already been parsed. Then + ** run a single CREATE TABLE statement to construct the imposter table in + ** the parsed schema. Then turn imposter mode back off again. + ** + ** If onOff==0 and tnum>0 then reset the schema for all databases, causing + ** the schema to be reparsed the next time it is needed. This has the + ** effect of erasing all imposter tables. + */ + case SQLITE_TESTCTRL_IMPOSTER: { + sqlite3 *db = va_arg(ap, sqlite3*); + sqlite3_mutex_enter(db->mutex); + db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlite3ResetAllSchemasOfConnection(db); + } + sqlite3_mutex_leave(db->mutex); + break; + } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ @@ -130026,7 +131686,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** parameter if it exists. If the parameter does not exist, this routine ** returns a NULL pointer. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam){ if( zFilename==0 || zParam==0 ) return 0; zFilename += sqlite3Strlen30(zFilename) + 1; while( zFilename[0] ){ @@ -130041,7 +131701,7 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char * /* ** Return a boolean value for a query parameter. */ -SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ const char *z = sqlite3_uri_parameter(zFilename, zParam); bDflt = bDflt!=0; return z ? sqlite3GetBoolean(z, bDflt) : bDflt; @@ -130050,7 +131710,7 @@ SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, in /* ** Return a 64-bit integer value for a query parameter. */ -SQLITE_API sqlite3_int64 sqlite3_uri_int64( +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64( const char *zFilename, /* Filename as passed to xOpen */ const char *zParam, /* URI parameter sought */ sqlite3_int64 bDflt /* return if parameter is missing */ @@ -130082,7 +131742,7 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ ** Return the filename of the database associated with a database ** connection. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName){ Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ @@ -130098,7 +131758,7 @@ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ ** Return 1 if database is read-only or 0 if read/write. Return -1 if ** no such database exists. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ Btree *pBt; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ @@ -130257,7 +131917,7 @@ static void leaveMutex(void){ ** on the same "db". If xNotify==0 then any prior callbacks are immediately ** cancelled. */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *db, void (*xNotify)(void **, int), void *pArg @@ -131151,6 +132811,11 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi #ifdef SQLITE_COVERAGE_TEST # define ALWAYS(x) (1) # define NEVER(X) (0) +#elif defined(SQLITE_DEBUG) +# define ALWAYS(x) sqlite3Fts3Always((x)!=0) +# define NEVER(x) sqlite3Fts3Never((x)!=0) +SQLITE_PRIVATE int sqlite3Fts3Always(int b); +SQLITE_PRIVATE int sqlite3Fts3Never(int b); #else # define ALWAYS(x) (x) # define NEVER(x) (x) @@ -131551,6 +133216,7 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); ) /* fts3.c */ +SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); @@ -131640,6 +133306,13 @@ static int fts3EvalStart(Fts3Cursor *pCsr); static int fts3TermSegReaderCursor( Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **); +#ifndef SQLITE_AMALGAMATION +# if defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; } +SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; } +# endif +#endif + /* ** Write a 64-bit variable-length integer to memory starting at p[0]. ** The length of data written will be between 1 and FTS3_VARINT_MAX bytes. @@ -131749,7 +133422,7 @@ SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){ /* If the first byte was a '[', then the close-quote character is a ']' */ if( quote=='[' ) quote = ']'; - while( ALWAYS(z[iIn]) ){ + while( z[iIn] ){ if( z[iIn]==quote ){ if( z[iIn+1]!=quote ) break; z[iOut++] = quote; @@ -131829,6 +133502,17 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ } /* +** Write an error message into *pzErr +*/ +SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char **pzErr, const char *zFormat, ...){ + va_list ap; + sqlite3_free(*pzErr); + va_start(ap, zFormat); + *pzErr = sqlite3_vmprintf(zFormat, ap); + va_end(ap); +} + +/* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is written ** into *pRc. @@ -132237,11 +133921,16 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ ** This function is used when parsing the "prefix=" FTS4 parameter. */ static int fts3GobbleInt(const char **pp, int *pnOut){ + const int MAX_NPREFIX = 10000000; const char *p; /* Iterator pointer */ int nInt = 0; /* Output value */ for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ nInt = nInt * 10 + (p[0] - '0'); + if( nInt>MAX_NPREFIX ){ + nInt = 0; + break; + } } if( p==*pp ) return SQLITE_ERROR; *pnOut = nInt; @@ -132284,7 +133973,6 @@ static int fts3PrefixParameter( aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); *apIndex = aIndex; - *pnIndex = nIndex; if( !aIndex ){ return SQLITE_NOMEM; } @@ -132294,13 +133982,20 @@ static int fts3PrefixParameter( const char *p = zParam; int i; for(i=1; i<nIndex; i++){ - int nPrefix; + int nPrefix = 0; if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR; - aIndex[i].nPrefix = nPrefix; + assert( nPrefix>=0 ); + if( nPrefix==0 ){ + nIndex--; + i--; + }else{ + aIndex[i].nPrefix = nPrefix; + } p++; } } + *pnIndex = nIndex; return SQLITE_OK; } @@ -132335,7 +134030,8 @@ static int fts3ContentColumns( const char *zTbl, /* Name of content table */ const char ***pazCol, /* OUT: Malloc'd array of column names */ int *pnCol, /* OUT: Size of array *pazCol */ - int *pnStr /* OUT: Bytes of string content */ + int *pnStr, /* OUT: Bytes of string content */ + char **pzErr /* OUT: error message */ ){ int rc = SQLITE_OK; /* Return code */ char *zSql; /* "SELECT *" statement on zTbl */ @@ -132346,6 +134042,9 @@ static int fts3ContentColumns( rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + sqlite3Fts3ErrMsg(pzErr, "%s", sqlite3_errmsg(db)); + } } sqlite3_free(zSql); @@ -132424,7 +134123,7 @@ static int fts3InitVtab( const char **aCol; /* Array of column names */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ - int nIndex; /* Size of aIndex[] array */ + int nIndex = 0; /* Size of aIndex[] array */ struct Fts3Index *aIndex = 0; /* Array of indexes for this table */ /* The results of parsing supported FTS4 key=value options: */ @@ -132512,13 +134211,13 @@ static int fts3InitVtab( } } if( iOpt==SizeofArray(aFts4Opt) ){ - *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); + sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z); rc = SQLITE_ERROR; }else{ switch( iOpt ){ case 0: /* MATCHINFO */ if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ - *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); + sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal); rc = SQLITE_ERROR; } bNoDocsize = 1; @@ -132546,7 +134245,7 @@ static int fts3InitVtab( if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4)) ){ - *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); + sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal); rc = SQLITE_ERROR; } bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); @@ -132597,7 +134296,7 @@ static int fts3InitVtab( if( nCol==0 ){ sqlite3_free((void*)aCol); aCol = 0; - rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); + rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr); /* If a languageid= option was specified, remove the language id ** column from the aCol[] array. */ @@ -132632,7 +134331,7 @@ static int fts3InitVtab( rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex); if( rc==SQLITE_ERROR ){ assert( zPrefix ); - *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix); + sqlite3Fts3ErrMsg(pzErr, "error parsing prefix parameter: %s", zPrefix); } if( rc!=SQLITE_OK ) goto fts3_init_out; @@ -132714,7 +134413,7 @@ static int fts3InitVtab( } for(i=0; i<nNotindexed; i++){ if( azNotindexed[i] ){ - *pzErr = sqlite3_mprintf("no such column: %s", azNotindexed[i]); + sqlite3Fts3ErrMsg(pzErr, "no such column: %s", azNotindexed[i]); rc = SQLITE_ERROR; } } @@ -132722,7 +134421,7 @@ static int fts3InitVtab( if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){ char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); rc = SQLITE_ERROR; - *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); + sqlite3Fts3ErrMsg(pzErr, "missing %s parameter in fts4 constructor", zMiss); } p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); @@ -133818,26 +135517,33 @@ static int fts3DoclistOrMerge( ** ** The right-hand input doclist is overwritten by this function. */ -static void fts3DoclistPhraseMerge( +static int fts3DoclistPhraseMerge( int bDescDoclist, /* True if arguments are desc */ int nDist, /* Distance from left to right (1=adjacent) */ char *aLeft, int nLeft, /* Left doclist */ - char *aRight, int *pnRight /* IN/OUT: Right/output doclist */ + char **paRight, int *pnRight /* IN/OUT: Right/output doclist */ ){ sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; + char *aRight = *paRight; char *pEnd1 = &aLeft[nLeft]; char *pEnd2 = &aRight[*pnRight]; char *p1 = aLeft; char *p2 = aRight; char *p; int bFirstOut = 0; - char *aOut = aRight; + char *aOut; assert( nDist>0 ); - + if( bDescDoclist ){ + aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX); + if( aOut==0 ) return SQLITE_NOMEM; + }else{ + aOut = aRight; + } p = aOut; + fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); @@ -133866,6 +135572,12 @@ static void fts3DoclistPhraseMerge( } *pnRight = (int)(p - aOut); + if( bDescDoclist ){ + sqlite3_free(aRight); + *paRight = aOut; + } + + return SQLITE_OK; } /* @@ -133990,8 +135702,22 @@ static int fts3TermSelectMerge( ){ if( pTS->aaOutput[0]==0 ){ /* If this is the first term selected, copy the doclist to the output - ** buffer using memcpy(). */ - pTS->aaOutput[0] = sqlite3_malloc(nDoclist); + ** buffer using memcpy(). + ** + ** Add FTS3_VARINT_MAX bytes of unused space to the end of the + ** allocation. This is so as to ensure that the buffer is big enough + ** to hold the current doclist AND'd with any other doclist. If the + ** doclists are stored in order=ASC order, this padding would not be + ** required (since the size of [doclistA AND doclistB] is always less + ** than or equal to the size of [doclistA] in that case). But this is + ** not true for order=DESC. For example, a doclist containing (1, -1) + ** may be smaller than (-1), as in the first example the -1 may be stored + ** as a single-byte delta, whereas in the second it must be stored as a + ** FTS3_VARINT_MAX byte varint. + ** + ** Similar padding is added in the fts3DoclistOrMerge() function. + */ + pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); @@ -134088,7 +135814,7 @@ static int fts3SegReaderCursor( ** calls out here. */ if( iLevel<0 && p->aIndex ){ Fts3SegReader *pSeg = 0; - rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); + rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg); if( rc==SQLITE_OK && pSeg ){ rc = fts3SegReaderCursorAppend(pCsr, pSeg); } @@ -134491,10 +136217,17 @@ static int fts3FilterMethod( ** row by docid. */ if( eSearch==FTS3_FULLSCAN_SEARCH ){ - zSql = sqlite3_mprintf( - "SELECT %s ORDER BY rowid %s", - p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") - ); + if( pDocidGe || pDocidLe ){ + zSql = sqlite3_mprintf( + "SELECT %s WHERE rowid BETWEEN %lld AND %lld ORDER BY rowid %s", + p->zReadExprlist, pCsr->iMinDocid, pCsr->iMaxDocid, + (pCsr->bDesc ? "DESC" : "ASC") + ); + }else{ + zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s", + p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC") + ); + } if( zSql ){ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); @@ -134730,11 +136463,31 @@ static void fts3ReversePoslist(char *pStart, char **ppPoslist){ char *p = &(*ppPoslist)[-2]; char c = 0; + /* Skip backwards passed any trailing 0x00 bytes added by NearTrim() */ while( p>pStart && (c=*p--)==0 ); + + /* Search backwards for a varint with value zero (the end of the previous + ** poslist). This is an 0x00 byte preceded by some byte that does not + ** have the 0x80 bit set. */ while( p>pStart && (*p & 0x80) | c ){ c = *p--; } - if( p>pStart ){ p = &p[2]; } + assert( p==pStart || c==0 ); + + /* At this point p points to that preceding byte without the 0x80 bit + ** set. So to find the start of the poslist, skip forward 2 bytes then + ** over a varint. + ** + ** Normally. The other case is that p==pStart and the poslist to return + ** is the first in the doclist. In this case do not skip forward 2 bytes. + ** The second part of the if condition (c==0 && *ppPoslist>&p[2]) + ** is required for cases where the first byte of a doclist and the + ** doclist is empty. For example, if the first docid is 10, a doclist + ** that begins with: + ** + ** 0x0A 0x00 <next docid delta varint> + */ + if( p>pStart || (c==0 && *ppPoslist>&p[2]) ){ p = &p[2]; } while( *p++&0x80 ); *ppPoslist = p; } @@ -134805,6 +136558,8 @@ static void fts3SnippetFunc( } if( !zEllipsis || !zEnd || !zStart ){ sqlite3_result_error_nomem(pContext); + }else if( nToken==0 ){ + sqlite3_result_text(pContext, "", -1, SQLITE_STATIC); }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken); } @@ -135240,14 +136995,17 @@ static void fts3EvalAllocateReaders( ** This function assumes that pList points to a buffer allocated using ** sqlite3_malloc(). This function takes responsibility for eventually ** freeing the buffer. +** +** SQLITE_OK is returned if successful, or SQLITE_NOMEM if an error occurs. */ -static void fts3EvalPhraseMergeToken( +static int fts3EvalPhraseMergeToken( Fts3Table *pTab, /* FTS Table pointer */ Fts3Phrase *p, /* Phrase to merge pList/nList into */ int iToken, /* Token pList/nList corresponds to */ char *pList, /* Pointer to doclist */ int nList /* Number of bytes in pList */ ){ + int rc = SQLITE_OK; assert( iToken!=p->iDoclistToken ); if( pList==0 ){ @@ -135286,13 +137044,16 @@ static void fts3EvalPhraseMergeToken( nDiff = p->iDoclistToken - iToken; } - fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight); + rc = fts3DoclistPhraseMerge( + pTab->bDescIdx, nDiff, pLeft, nLeft, &pRight, &nRight + ); sqlite3_free(pLeft); p->doclist.aAll = pRight; p->doclist.nAll = nRight; } if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken; + return rc; } /* @@ -135318,7 +137079,7 @@ static int fts3EvalPhraseLoad( char *pThis = 0; rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis); if( rc==SQLITE_OK ){ - fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); + rc = fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); } } assert( pToken->pSegcsr==0 ); @@ -135860,12 +137621,14 @@ static void fts3EvalStartReaders( ){ if( pExpr && SQLITE_OK==*pRc ){ if( pExpr->eType==FTSQUERY_PHRASE ){ - int i; int nToken = pExpr->pPhrase->nToken; - for(i=0; i<nToken; i++){ - if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; + if( nToken ){ + int i; + for(i=0; i<nToken; i++){ + if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; + } + pExpr->bDeferred = (i==nToken); } - pExpr->bDeferred = (i==nToken); *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase); }else{ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc); @@ -136121,8 +137884,12 @@ static int fts3EvalSelectDeferred( rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList); assert( rc==SQLITE_OK || pList==0 ); if( rc==SQLITE_OK ){ + rc = fts3EvalPhraseMergeToken( + pTab, pTC->pPhrase, pTC->iToken,pList,nList + ); + } + if( rc==SQLITE_OK ){ int nCount; - fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList); nCount = fts3DoclistCountDocids( pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll ); @@ -136983,7 +138750,6 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( if( iDocid!=pCsr->iPrevId || pExpr->bEof ){ int rc = SQLITE_OK; int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */ - int iMul; /* +1 if csr dir matches index dir, else -1 */ int bOr = 0; u8 bEof = 0; u8 bTreeEof = 0; @@ -137025,7 +138791,8 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist( pIter = pPhrase->pOrPoslist; iDocid = pPhrase->iOrDocid; if( pCsr->bDesc==bDescDoclist ){ - bEof = (pIter >= (pPhrase->doclist.aAll + pPhrase->doclist.nAll)); + bEof = !pPhrase->doclist.nAll || + (pIter >= (pPhrase->doclist.aAll + pPhrase->doclist.nAll)); while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){ sqlite3Fts3DoclistNext( bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll, @@ -137106,7 +138873,7 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){ #ifdef _WIN32 __declspec(dllexport) #endif -SQLITE_API int sqlite3_fts3_init( +SQLITE_API int SQLITE_STDCALL sqlite3_fts3_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi @@ -137237,7 +139004,7 @@ static int fts3auxConnectMethod( return SQLITE_OK; bad_args: - *pzErr = sqlite3_mprintf("invalid arguments to fts4aux constructor"); + sqlite3Fts3ErrMsg(pzErr, "invalid arguments to fts4aux constructor"); return SQLITE_ERROR; } @@ -138695,13 +140462,13 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse( sqlite3Fts3ExprFree(*ppExpr); *ppExpr = 0; if( rc==SQLITE_TOOBIG ){ - *pzErr = sqlite3_mprintf( + sqlite3Fts3ErrMsg(pzErr, "FTS expression tree is too large (maximum depth %d)", SQLITE_FTS3_MAX_EXPR_DEPTH ); rc = SQLITE_ERROR; }else if( rc==SQLITE_ERROR ){ - *pzErr = sqlite3_mprintf("malformed MATCH expression: [%s]", z); + sqlite3Fts3ErrMsg(pzErr, "malformed MATCH expression: [%s]", z); } } @@ -140074,7 +141841,7 @@ static void scalarFunc( if( argc==2 ){ void *pOld; int n = sqlite3_value_bytes(argv[1]); - if( n!=sizeof(pPtr) ){ + if( zName==0 || n!=sizeof(pPtr) ){ sqlite3_result_error(context, "argument type mismatch", -1); return; } @@ -140085,7 +141852,9 @@ static void scalarFunc( return; } }else{ - pPtr = sqlite3Fts3HashFind(pHash, zName, nName); + if( zName ){ + pPtr = sqlite3Fts3HashFind(pHash, zName, nName); + } if( !pPtr ){ char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); sqlite3_result_error(context, zErr, -1); @@ -140166,12 +141935,16 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( zEnd = &zCopy[strlen(zCopy)]; z = (char *)sqlite3Fts3NextToken(zCopy, &n); + if( z==0 ){ + assert( n==0 ); + z = zCopy; + } z[n] = '\0'; sqlite3Fts3Dequote(z); m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1); if( !m ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z); + sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", z); rc = SQLITE_ERROR; }else{ char const **aArg = 0; @@ -140194,7 +141967,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( rc = m->xCreate(iArg, aArg, ppTok); assert( rc!=SQLITE_OK || *ppTok ); if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("unknown tokenizer"); + sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer"); }else{ (*ppTok)->pModule = m; } @@ -140278,9 +142051,9 @@ static void testFunc( p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); if( !p ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); + char *zErr2 = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3_result_error(context, zErr2, -1); + sqlite3_free(zErr2); return; } @@ -140815,7 +142588,7 @@ static int fts3tokQueryTokenizer( p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); if( !p ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", zName); return SQLITE_ERROR; } @@ -141512,7 +143285,7 @@ static int fts3SqlStmt( /* 25 */ "", /* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", -/* 27 */ "SELECT DISTINCT level / (1024 * ?) FROM %Q.'%q_segdir'", +/* 27 */ "SELECT ? UNION SELECT level / (1024 * ?) FROM %Q.'%q_segdir'", /* This statement is used to determine which level to read the input from ** when performing an incremental merge. It returns the absolute level number @@ -142811,7 +144584,10 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( ** an array of pending terms by term. This occurs as part of flushing ** the contents of the pending-terms hash table to the database. */ -static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ +static int SQLITE_CDECL fts3CompareElemByTerm( + const void *lhs, + const void *rhs +){ char *z1 = fts3HashKey(*(Fts3HashElem **)lhs); char *z2 = fts3HashKey(*(Fts3HashElem **)rhs); int n1 = fts3HashKeysize(*(Fts3HashElem **)lhs); @@ -144627,7 +146403,8 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ int rc2; - sqlite3_bind_int(pAllLangid, 1, p->nIndex); + sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid); + sqlite3_bind_int(pAllLangid, 2, p->nIndex); while( sqlite3_step(pAllLangid)==SQLITE_ROW ){ int i; int iLangid = sqlite3_column_int(pAllLangid, 0); @@ -145959,7 +147736,7 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){ pHint->n = i; i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel); i += fts3GetVarint32(&pHint->a[i], pnInput); - if( i!=nHint ) return SQLITE_CORRUPT_VTAB; + if( i!=nHint ) return FTS_CORRUPT_VTAB; return SQLITE_OK; } @@ -146327,7 +148104,8 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ int rc2; - sqlite3_bind_int(pAllLangid, 1, p->nIndex); + sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid); + sqlite3_bind_int(pAllLangid, 2, p->nIndex); while( rc==SQLITE_OK && sqlite3_step(pAllLangid)==SQLITE_ROW ){ int iLangid = sqlite3_column_int(pAllLangid, 0); int i; @@ -146340,7 +148118,6 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ } /* This block calculates the checksum according to the %_content table */ - rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0); if( rc==SQLITE_OK ){ sqlite3_tokenizer_module const *pModule = p->pTokenizer->pModule; sqlite3_stmt *pStmt = 0; @@ -146437,7 +148214,7 @@ static int fts3DoIntegrityCheck( int rc; int bOk = 0; rc = fts3IntegrityCheck(p, &bOk); - if( rc==SQLITE_OK && bOk==0 ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; return rc; } @@ -146875,6 +148652,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ #define FTS3_MATCHINFO_LENGTH 'l' /* nCol values */ #define FTS3_MATCHINFO_LCS 's' /* nCol values */ #define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */ +#define FTS3_MATCHINFO_LHITS 'y' /* nCol*nPhrase values */ /* ** The default value for the second argument to matchinfo(). @@ -147530,8 +149308,12 @@ static int fts3SnippetText( ** required. They are required if (a) this is not the first fragment, ** or (b) this fragment does not begin at position 0 of its column. */ - if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){ - rc = fts3StringAppend(pOut, zEllipsis, -1); + if( rc==SQLITE_OK ){ + if( iPos>0 || iFragment>0 ){ + rc = fts3StringAppend(pOut, zEllipsis, -1); + }else if( iBegin ){ + rc = fts3StringAppend(pOut, zDoc, iBegin); + } } if( rc!=SQLITE_OK || iCurrent<iPos ) continue; } @@ -147653,6 +149435,51 @@ static int fts3ExprLocalHitsCb( return rc; } +/* +** fts3ExprIterate() callback used to gather information for the matchinfo +** directive 'y'. +*/ +static int fts3ExprLHitsCb( + Fts3Expr *pExpr, /* Phrase expression node */ + int iPhrase, /* Phrase number */ + void *pCtx /* Pointer to MatchInfo structure */ +){ + MatchInfo *p = (MatchInfo *)pCtx; + Fts3Table *pTab = (Fts3Table *)p->pCursor->base.pVtab; + int rc = SQLITE_OK; + int iStart = iPhrase * p->nCol; + Fts3Expr *pEof; /* Ancestor node already at EOF */ + + /* This must be a phrase */ + assert( pExpr->pPhrase ); + + /* Initialize all output integers to zero. */ + memset(&p->aMatchinfo[iStart], 0, sizeof(u32) * p->nCol); + + /* Check if this or any parent node is at EOF. If so, then all output + ** values are zero. */ + for(pEof=pExpr; pEof && pEof->bEof==0; pEof=pEof->pParent); + + if( pEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + char *pIter = pPhrase->doclist.pList; + int iCol = 0; + + while( 1 ){ + int nHit = fts3ColumnlistCount(&pIter); + if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){ + p->aMatchinfo[iStart + iCol] = (u32)nHit; + } + assert( *pIter==0x00 || *pIter==0x01 ); + if( *pIter!=0x01 ) break; + pIter++; + pIter += fts3GetVarint32(pIter, &iCol); + } + } + + return rc; +} + static int fts3MatchinfoCheck( Fts3Table *pTab, char cArg, @@ -147665,10 +149492,11 @@ static int fts3MatchinfoCheck( || (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize) || (cArg==FTS3_MATCHINFO_LCS) || (cArg==FTS3_MATCHINFO_HITS) + || (cArg==FTS3_MATCHINFO_LHITS) ){ return SQLITE_OK; } - *pzErr = sqlite3_mprintf("unrecognized matchinfo request: %c", cArg); + sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo request: %c", cArg); return SQLITE_ERROR; } @@ -147688,6 +149516,10 @@ static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ nVal = pInfo->nCol; break; + case FTS3_MATCHINFO_LHITS: + nVal = pInfo->nCol * pInfo->nPhrase; + break; + default: assert( cArg==FTS3_MATCHINFO_HITS ); nVal = pInfo->nCol * pInfo->nPhrase * 3; @@ -147942,6 +149774,10 @@ static int fts3MatchinfoValues( } break; + case FTS3_MATCHINFO_LHITS: + (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLHitsCb, (void*)pInfo); + break; + default: { Fts3Expr *pExpr; assert( zArg[i]==FTS3_MATCHINFO_HITS ); @@ -148097,7 +149933,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet( */ for(iRead=0; iRead<pTab->nColumn; iRead++){ SnippetFragment sF = {0, 0, 0, 0}; - int iS; + int iS = 0; if( iCol>=0 && iRead!=iCol ) continue; /* Find the best snippet of nFToken tokens in column iRead. */ @@ -151954,11 +153790,19 @@ static int rtreeUpdate( if( nData>1 ){ int ii; - /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ - assert( nData==(pRtree->nDim*2 + 3) ); + /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. + ** + ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared + ** with "column" that are interpreted as table constraints. + ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5)); + ** This problem was discovered after years of use, so we silently ignore + ** these kinds of misdeclared tables to avoid breaking any legacy. + */ + assert( nData<=(pRtree->nDim*2 + 3) ); + #ifndef SQLITE_RTREE_INT_ONLY if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ + for(ii=0; ii<nData-4; ii+=2){ cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]); cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]); if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ @@ -151969,7 +153813,7 @@ static int rtreeUpdate( }else #endif { - for(ii=0; ii<(pRtree->nDim*2); ii+=2){ + for(ii=0; ii<nData-4; ii+=2){ cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]); cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]); if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){ @@ -152540,7 +154384,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ /* ** Register a new geometry function for use with the r-tree MATCH operator. */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, /* Register SQL function on this connection */ const char *zGeom, /* Name of the new SQL function */ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), /* Callback */ @@ -152564,7 +154408,7 @@ SQLITE_API int sqlite3_rtree_geometry_callback( ** Register a new 2nd-generation geometry function for use with the ** r-tree MATCH operator. */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, /* Register SQL function on this connection */ const char *zQueryFunc, /* Name of new SQL function */ int (*xQueryFunc)(sqlite3_rtree_query_info*), /* Callback */ @@ -152589,7 +154433,7 @@ SQLITE_API int sqlite3_rtree_query_callback( #ifdef _WIN32 __declspec(dllexport) #endif -SQLITE_API int sqlite3_rtree_init( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi @@ -153094,7 +154938,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){ #ifdef _WIN32 __declspec(dllexport) #endif -SQLITE_API int sqlite3_icu_init( +SQLITE_API int SQLITE_STDCALL sqlite3_icu_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi @@ -153369,3 +155213,654 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ /************** End of fts3_icu.c ********************************************/ +/************** Begin file dbstat.c ******************************************/ +/* +** 2010 July 12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains an implementation of the "dbstat" virtual table. +** +** The dbstat virtual table is used to extract low-level formatting +** information from an SQLite database in order to implement the +** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script +** for an example implementation. +*/ + +#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ + && !defined(SQLITE_OMIT_VIRTUALTABLE) + +/* +** Page paths: +** +** The value of the 'path' column describes the path taken from the +** root-node of the b-tree structure to each page. The value of the +** root-node path is '/'. +** +** The value of the path for the left-most child page of the root of +** a b-tree is '/000/'. (Btrees store content ordered from left to right +** so the pages to the left have smaller keys than the pages to the right.) +** The next to left-most child of the root page is +** '/001', and so on, each sibling page identified by a 3-digit hex +** value. The children of the 451st left-most sibling have paths such +** as '/1c2/000/, '/1c2/001/' etc. +** +** Overflow pages are specified by appending a '+' character and a +** six-digit hexadecimal value to the path to the cell they are linked +** from. For example, the three overflow pages in a chain linked from +** the left-most cell of the 450th child of the root page are identified +** by the paths: +** +** '/1c2/000+000000' // First page in overflow chain +** '/1c2/000+000001' // Second page in overflow chain +** '/1c2/000+000002' // Third page in overflow chain +** +** If the paths are sorted using the BINARY collation sequence, then +** the overflow pages associated with a cell will appear earlier in the +** sort-order than its child page: +** +** '/1c2/000/' // Left-most child of 451st child of root +*/ +#define VTAB_SCHEMA \ + "CREATE TABLE xx( " \ + " name STRING, /* Name of table or index */" \ + " path INTEGER, /* Path to page from root */" \ + " pageno INTEGER, /* Page number */" \ + " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */" \ + " ncell INTEGER, /* Cells on page (0 for overflow) */" \ + " payload INTEGER, /* Bytes of payload on this page */" \ + " unused INTEGER, /* Bytes of unused space on this page */" \ + " mx_payload INTEGER, /* Largest payload size of all cells */" \ + " pgoffset INTEGER, /* Offset of page in file */" \ + " pgsize INTEGER /* Size of the page */" \ + ");" + + +typedef struct StatTable StatTable; +typedef struct StatCursor StatCursor; +typedef struct StatPage StatPage; +typedef struct StatCell StatCell; + +struct StatCell { + int nLocal; /* Bytes of local payload */ + u32 iChildPg; /* Child node (or 0 if this is a leaf) */ + int nOvfl; /* Entries in aOvfl[] */ + u32 *aOvfl; /* Array of overflow page numbers */ + int nLastOvfl; /* Bytes of payload on final overflow page */ + int iOvfl; /* Iterates through aOvfl[] */ +}; + +struct StatPage { + u32 iPgno; + DbPage *pPg; + int iCell; + + char *zPath; /* Path to this page */ + + /* Variables populated by statDecodePage(): */ + u8 flags; /* Copy of flags byte */ + int nCell; /* Number of cells on page */ + int nUnused; /* Number of unused bytes on page */ + StatCell *aCell; /* Array of parsed cells */ + u32 iRightChildPg; /* Right-child page number (or 0) */ + int nMxPayload; /* Largest payload of any cell on this page */ +}; + +struct StatCursor { + sqlite3_vtab_cursor base; + sqlite3_stmt *pStmt; /* Iterates through set of root pages */ + int isEof; /* After pStmt has returned SQLITE_DONE */ + + StatPage aPage[32]; + int iPage; /* Current entry in aPage[] */ + + /* Values to return. */ + char *zName; /* Value of 'name' column */ + char *zPath; /* Value of 'path' column */ + u32 iPageno; /* Value of 'pageno' column */ + char *zPagetype; /* Value of 'pagetype' column */ + int nCell; /* Value of 'ncell' column */ + int nPayload; /* Value of 'payload' column */ + int nUnused; /* Value of 'unused' column */ + int nMxPayload; /* Value of 'mx_payload' column */ + i64 iOffset; /* Value of 'pgOffset' column */ + int szPage; /* Value of 'pgSize' column */ +}; + +struct StatTable { + sqlite3_vtab base; + sqlite3 *db; + int iDb; /* Index of database to analyze */ +}; + +#ifndef get2byte +# define get2byte(x) ((x)[0]<<8 | (x)[1]) +#endif + +/* +** Connect to or create a statvfs virtual table. +*/ +static int statConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + StatTable *pTab = 0; + int rc = SQLITE_OK; + int iDb; + + if( argc>=4 ){ + iDb = sqlite3FindDbName(db, argv[3]); + if( iDb<0 ){ + *pzErr = sqlite3_mprintf("no such database: %s", argv[3]); + return SQLITE_ERROR; + } + }else{ + iDb = 0; + } + rc = sqlite3_declare_vtab(db, VTAB_SCHEMA); + if( rc==SQLITE_OK ){ + pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable)); + if( pTab==0 ) rc = SQLITE_NOMEM; + } + + assert( rc==SQLITE_OK || pTab==0 ); + if( rc==SQLITE_OK ){ + memset(pTab, 0, sizeof(StatTable)); + pTab->db = db; + pTab->iDb = iDb; + } + + *ppVtab = (sqlite3_vtab*)pTab; + return rc; +} + +/* +** Disconnect from or destroy a statvfs virtual table. +*/ +static int statDisconnect(sqlite3_vtab *pVtab){ + sqlite3_free(pVtab); + return SQLITE_OK; +} + +/* +** There is no "best-index". This virtual table always does a linear +** scan of the binary VFS log file. +*/ +static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + + /* Records are always returned in ascending order of (name, path). + ** If this will satisfy the client, set the orderByConsumed flag so that + ** SQLite does not do an external sort. + */ + if( ( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].iColumn==0 + && pIdxInfo->aOrderBy[0].desc==0 + ) || + ( pIdxInfo->nOrderBy==2 + && pIdxInfo->aOrderBy[0].iColumn==0 + && pIdxInfo->aOrderBy[0].desc==0 + && pIdxInfo->aOrderBy[1].iColumn==1 + && pIdxInfo->aOrderBy[1].desc==0 + ) + ){ + pIdxInfo->orderByConsumed = 1; + } + + pIdxInfo->estimatedCost = 10.0; + return SQLITE_OK; +} + +/* +** Open a new statvfs cursor. +*/ +static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + StatTable *pTab = (StatTable *)pVTab; + StatCursor *pCsr; + int rc; + + pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor)); + if( pCsr==0 ){ + rc = SQLITE_NOMEM; + }else{ + char *zSql; + memset(pCsr, 0, sizeof(StatCursor)); + pCsr->base.pVtab = pVTab; + + zSql = sqlite3_mprintf( + "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" + " UNION ALL " + "SELECT name, rootpage, type" + " FROM \"%w\".sqlite_master WHERE rootpage!=0" + " ORDER BY name", pTab->db->aDb[pTab->iDb].zName); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + } + if( rc!=SQLITE_OK ){ + sqlite3_free(pCsr); + pCsr = 0; + } + } + + *ppCursor = (sqlite3_vtab_cursor *)pCsr; + return rc; +} + +static void statClearPage(StatPage *p){ + int i; + if( p->aCell ){ + for(i=0; i<p->nCell; i++){ + sqlite3_free(p->aCell[i].aOvfl); + } + sqlite3_free(p->aCell); + } + sqlite3PagerUnref(p->pPg); + sqlite3_free(p->zPath); + memset(p, 0, sizeof(StatPage)); +} + +static void statResetCsr(StatCursor *pCsr){ + int i; + sqlite3_reset(pCsr->pStmt); + for(i=0; i<ArraySize(pCsr->aPage); i++){ + statClearPage(&pCsr->aPage[i]); + } + pCsr->iPage = 0; + sqlite3_free(pCsr->zPath); + pCsr->zPath = 0; +} + +/* +** Close a statvfs cursor. +*/ +static int statClose(sqlite3_vtab_cursor *pCursor){ + StatCursor *pCsr = (StatCursor *)pCursor; + statResetCsr(pCsr); + sqlite3_finalize(pCsr->pStmt); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +static void getLocalPayload( + int nUsable, /* Usable bytes per page */ + u8 flags, /* Page flags */ + int nTotal, /* Total record (payload) size */ + int *pnLocal /* OUT: Bytes stored locally */ +){ + int nLocal; + int nMinLocal; + int nMaxLocal; + + if( flags==0x0D ){ /* Table leaf node */ + nMinLocal = (nUsable - 12) * 32 / 255 - 23; + nMaxLocal = nUsable - 35; + }else{ /* Index interior and leaf nodes */ + nMinLocal = (nUsable - 12) * 32 / 255 - 23; + nMaxLocal = (nUsable - 12) * 64 / 255 - 23; + } + + nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4); + if( nLocal>nMaxLocal ) nLocal = nMinLocal; + *pnLocal = nLocal; +} + +static int statDecodePage(Btree *pBt, StatPage *p){ + int nUnused; + int iOff; + int nHdr; + int isLeaf; + int szPage; + + u8 *aData = sqlite3PagerGetData(p->pPg); + u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0]; + + p->flags = aHdr[0]; + p->nCell = get2byte(&aHdr[3]); + p->nMxPayload = 0; + + isLeaf = (p->flags==0x0A || p->flags==0x0D); + nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100; + + nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell; + nUnused += (int)aHdr[7]; + iOff = get2byte(&aHdr[1]); + while( iOff ){ + nUnused += get2byte(&aData[iOff+2]); + iOff = get2byte(&aData[iOff]); + } + p->nUnused = nUnused; + p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]); + szPage = sqlite3BtreeGetPageSize(pBt); + + if( p->nCell ){ + int i; /* Used to iterate through cells */ + int nUsable; /* Usable bytes per page */ + + sqlite3BtreeEnter(pBt); + nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt); + sqlite3BtreeLeave(pBt); + p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell)); + if( p->aCell==0 ) return SQLITE_NOMEM; + memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell)); + + for(i=0; i<p->nCell; i++){ + StatCell *pCell = &p->aCell[i]; + + iOff = get2byte(&aData[nHdr+i*2]); + if( !isLeaf ){ + pCell->iChildPg = sqlite3Get4byte(&aData[iOff]); + iOff += 4; + } + if( p->flags==0x05 ){ + /* A table interior node. nPayload==0. */ + }else{ + u32 nPayload; /* Bytes of payload total (local+overflow) */ + int nLocal; /* Bytes of payload stored locally */ + iOff += getVarint32(&aData[iOff], nPayload); + if( p->flags==0x0D ){ + u64 dummy; + iOff += sqlite3GetVarint(&aData[iOff], &dummy); + } + if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload; + getLocalPayload(nUsable, p->flags, nPayload, &nLocal); + pCell->nLocal = nLocal; + assert( nLocal>=0 ); + assert( nPayload>=(u32)nLocal ); + assert( nLocal<=(nUsable-35) ); + if( nPayload>(u32)nLocal ){ + int j; + int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); + pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); + pCell->nOvfl = nOvfl; + pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); + if( pCell->aOvfl==0 ) return SQLITE_NOMEM; + pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); + for(j=1; j<nOvfl; j++){ + int rc; + u32 iPrev = pCell->aOvfl[j-1]; + DbPage *pPg = 0; + rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg); + if( rc!=SQLITE_OK ){ + assert( pPg==0 ); + return rc; + } + pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg)); + sqlite3PagerUnref(pPg); + } + } + } + } + } + + return SQLITE_OK; +} + +/* +** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on +** the current value of pCsr->iPageno. +*/ +static void statSizeAndOffset(StatCursor *pCsr){ + StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab; + Btree *pBt = pTab->db->aDb[pTab->iDb].pBt; + Pager *pPager = sqlite3BtreePager(pBt); + sqlite3_file *fd; + sqlite3_int64 x[2]; + + /* The default page size and offset */ + pCsr->szPage = sqlite3BtreeGetPageSize(pBt); + pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1); + + /* If connected to a ZIPVFS backend, override the page size and + ** offset with actual values obtained from ZIPVFS. + */ + fd = sqlite3PagerFile(pPager); + x[0] = pCsr->iPageno; + if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ + pCsr->iOffset = x[0]; + pCsr->szPage = (int)x[1]; + } +} + +/* +** Move a statvfs cursor to the next entry in the file. +*/ +static int statNext(sqlite3_vtab_cursor *pCursor){ + int rc; + int nPayload; + char *z; + StatCursor *pCsr = (StatCursor *)pCursor; + StatTable *pTab = (StatTable *)pCursor->pVtab; + Btree *pBt = pTab->db->aDb[pTab->iDb].pBt; + Pager *pPager = sqlite3BtreePager(pBt); + + sqlite3_free(pCsr->zPath); + pCsr->zPath = 0; + +statNextRestart: + if( pCsr->aPage[0].pPg==0 ){ + rc = sqlite3_step(pCsr->pStmt); + if( rc==SQLITE_ROW ){ + int nPage; + u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1); + sqlite3PagerPagecount(pPager, &nPage); + if( nPage==0 ){ + pCsr->isEof = 1; + return sqlite3_reset(pCsr->pStmt); + } + rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg); + pCsr->aPage[0].iPgno = iRoot; + pCsr->aPage[0].iCell = 0; + pCsr->aPage[0].zPath = z = sqlite3_mprintf("/"); + pCsr->iPage = 0; + if( z==0 ) rc = SQLITE_NOMEM; + }else{ + pCsr->isEof = 1; + return sqlite3_reset(pCsr->pStmt); + } + }else{ + + /* Page p itself has already been visited. */ + StatPage *p = &pCsr->aPage[pCsr->iPage]; + + while( p->iCell<p->nCell ){ + StatCell *pCell = &p->aCell[p->iCell]; + if( pCell->iOvfl<pCell->nOvfl ){ + int nUsable; + sqlite3BtreeEnter(pBt); + nUsable = sqlite3BtreeGetPageSize(pBt) - + sqlite3BtreeGetReserveNoMutex(pBt); + sqlite3BtreeLeave(pBt); + pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); + pCsr->iPageno = pCell->aOvfl[pCell->iOvfl]; + pCsr->zPagetype = "overflow"; + pCsr->nCell = 0; + pCsr->nMxPayload = 0; + pCsr->zPath = z = sqlite3_mprintf( + "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl + ); + if( pCell->iOvfl<pCell->nOvfl-1 ){ + pCsr->nUnused = 0; + pCsr->nPayload = nUsable - 4; + }else{ + pCsr->nPayload = pCell->nLastOvfl; + pCsr->nUnused = nUsable - 4 - pCsr->nPayload; + } + pCell->iOvfl++; + statSizeAndOffset(pCsr); + return z==0 ? SQLITE_NOMEM : SQLITE_OK; + } + if( p->iRightChildPg ) break; + p->iCell++; + } + + if( !p->iRightChildPg || p->iCell>p->nCell ){ + statClearPage(p); + if( pCsr->iPage==0 ) return statNext(pCursor); + pCsr->iPage--; + goto statNextRestart; /* Tail recursion */ + } + pCsr->iPage++; + assert( p==&pCsr->aPage[pCsr->iPage-1] ); + + if( p->iCell==p->nCell ){ + p[1].iPgno = p->iRightChildPg; + }else{ + p[1].iPgno = p->aCell[p->iCell].iChildPg; + } + rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg); + p[1].iCell = 0; + p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); + p->iCell++; + if( z==0 ) rc = SQLITE_NOMEM; + } + + + /* Populate the StatCursor fields with the values to be returned + ** by the xColumn() and xRowid() methods. + */ + if( rc==SQLITE_OK ){ + int i; + StatPage *p = &pCsr->aPage[pCsr->iPage]; + pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); + pCsr->iPageno = p->iPgno; + + rc = statDecodePage(pBt, p); + if( rc==SQLITE_OK ){ + statSizeAndOffset(pCsr); + + switch( p->flags ){ + case 0x05: /* table internal */ + case 0x02: /* index internal */ + pCsr->zPagetype = "internal"; + break; + case 0x0D: /* table leaf */ + case 0x0A: /* index leaf */ + pCsr->zPagetype = "leaf"; + break; + default: + pCsr->zPagetype = "corrupted"; + break; + } + pCsr->nCell = p->nCell; + pCsr->nUnused = p->nUnused; + pCsr->nMxPayload = p->nMxPayload; + pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath); + if( z==0 ) rc = SQLITE_NOMEM; + nPayload = 0; + for(i=0; i<p->nCell; i++){ + nPayload += p->aCell[i].nLocal; + } + pCsr->nPayload = nPayload; + } + } + + return rc; +} + +static int statEof(sqlite3_vtab_cursor *pCursor){ + StatCursor *pCsr = (StatCursor *)pCursor; + return pCsr->isEof; +} + +static int statFilter( + sqlite3_vtab_cursor *pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + StatCursor *pCsr = (StatCursor *)pCursor; + + statResetCsr(pCsr); + return statNext(pCursor); +} + +static int statColumn( + sqlite3_vtab_cursor *pCursor, + sqlite3_context *ctx, + int i +){ + StatCursor *pCsr = (StatCursor *)pCursor; + switch( i ){ + case 0: /* name */ + sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_TRANSIENT); + break; + case 1: /* path */ + sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT); + break; + case 2: /* pageno */ + sqlite3_result_int64(ctx, pCsr->iPageno); + break; + case 3: /* pagetype */ + sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC); + break; + case 4: /* ncell */ + sqlite3_result_int(ctx, pCsr->nCell); + break; + case 5: /* payload */ + sqlite3_result_int(ctx, pCsr->nPayload); + break; + case 6: /* unused */ + sqlite3_result_int(ctx, pCsr->nUnused); + break; + case 7: /* mx_payload */ + sqlite3_result_int(ctx, pCsr->nMxPayload); + break; + case 8: /* pgoffset */ + sqlite3_result_int64(ctx, pCsr->iOffset); + break; + default: /* pgsize */ + assert( i==9 ); + sqlite3_result_int(ctx, pCsr->szPage); + break; + } + return SQLITE_OK; +} + +static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + StatCursor *pCsr = (StatCursor *)pCursor; + *pRowid = pCsr->iPageno; + return SQLITE_OK; +} + +/* +** Invoke this routine to register the "dbstat" virtual table module +*/ +SQLITE_API int SQLITE_STDCALL sqlite3_dbstat_register(sqlite3 *db){ + static sqlite3_module dbstat_module = { + 0, /* iVersion */ + statConnect, /* xCreate */ + statConnect, /* xConnect */ + statBestIndex, /* xBestIndex */ + statDisconnect, /* xDisconnect */ + statDisconnect, /* xDestroy */ + statOpen, /* xOpen - open a cursor */ + statClose, /* xClose - close a cursor */ + statFilter, /* xFilter - configure scan constraints */ + statNext, /* xNext - advance a cursor */ + statEof, /* xEof - check for end of scan */ + statColumn, /* xColumn - read data */ + statRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + }; + return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); +} +#endif /* SQLITE_ENABLE_DBSTAT_VTAB */ + +/************** End of dbstat.c **********************************************/ diff --git a/ext/sqlite3/libsqlite/sqlite3.h b/ext/sqlite3/libsqlite/sqlite3.h index e13073a3c6..d43b63c107 100644 --- a/ext/sqlite3/libsqlite/sqlite3.h +++ b/ext/sqlite3/libsqlite/sqlite3.h @@ -43,16 +43,20 @@ extern "C" { /* -** Add the ability to override 'extern' +** Provide the ability to override linkage features of the interface. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif - #ifndef SQLITE_API # define SQLITE_API #endif - +#ifndef SQLITE_CDECL +# define SQLITE_CDECL +#endif +#ifndef SQLITE_STDCALL +# define SQLITE_STDCALL +#endif /* ** These no-op macros are used in front of interfaces to mark those @@ -107,9 +111,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.8.3" -#define SQLITE_VERSION_NUMBER 3008008 -#define SQLITE_SOURCE_ID "2015-02-25 13:29:11 9d6c1880fb75660bbabd693175579529785f8a6b" +#define SQLITE_VERSION "3.8.10.2" +#define SQLITE_VERSION_NUMBER 3008010 +#define SQLITE_SOURCE_ID "2015-05-20 18:17:19 2ef4f3a5b1d1d0c4338f8243d40a2452cc1f7fe4" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -142,9 +146,9 @@ extern "C" { ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; -SQLITE_API const char *sqlite3_libversion(void); -SQLITE_API const char *sqlite3_sourceid(void); -SQLITE_API int sqlite3_libversion_number(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void); +SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics @@ -169,8 +173,8 @@ SQLITE_API int sqlite3_libversion_number(void); ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS -SQLITE_API int sqlite3_compileoption_used(const char *zOptName); -SQLITE_API const char *sqlite3_compileoption_get(int N); +SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N); #endif /* @@ -209,7 +213,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N); ** ** See the [threading mode] documentation for additional information. */ -SQLITE_API int sqlite3_threadsafe(void); +SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle @@ -266,6 +270,7 @@ typedef sqlite_uint64 sqlite3_uint64; /* ** CAPI3REF: Closing A Database Connection +** DESTRUCTOR: sqlite3 ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. @@ -305,8 +310,8 @@ typedef sqlite_uint64 sqlite3_uint64; ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer ** argument is a harmless no-op. */ -SQLITE_API int sqlite3_close(sqlite3*); -SQLITE_API int sqlite3_close_v2(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. @@ -317,6 +322,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* ** CAPI3REF: One-Step Query Execution Interface +** METHOD: sqlite3 ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], @@ -376,7 +382,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** </ul> */ -SQLITE_API int sqlite3_exec( +SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ @@ -756,14 +762,16 @@ struct sqlite3_io_methods { ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** +** <ul> +** <li>[[SQLITE_FCNTL_LOCKSTATE]] ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability -** is used during testing and only needs to be supported when SQLITE_TEST -** is defined. -** <ul> +** is used during testing and is only available when the SQLITE_TEST +** compile-time option is used. +** ** <li>[[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the @@ -888,7 +896,9 @@ struct sqlite3_io_methods { ** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] ** file control returns [SQLITE_OK], then the parser assumes that the ** VFS has handled the PRAGMA itself and the parser generates a no-op -** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns +** prepared statement if result string is NULL, or that returns a copy +** of the result string if the string is non-NULL. +** ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] @@ -946,12 +956,19 @@ struct sqlite3_io_methods { ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** +** <li>[[SQLITE_FCNTL_WAL_BLOCK]] +** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might +** be advantageous to block on the next WAL lock if the lock is not immediately +** available. The WAL subsystem issues this signal during rare +** circumstances in order to fix a problem with priority inversion. +** Applications should <em>not</em> use this file-control. +** ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 +#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 +#define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 @@ -970,6 +987,13 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_SYNC 21 #define SQLITE_FCNTL_COMMIT_PHASETWO 22 #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 +#define SQLITE_FCNTL_WAL_BLOCK 24 + +/* deprecated names */ +#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE +#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE +#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO + /* ** CAPI3REF: Mutex Handle @@ -1318,10 +1342,10 @@ struct sqlite3_vfs { ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ -SQLITE_API int sqlite3_initialize(void); -SQLITE_API int sqlite3_shutdown(void); -SQLITE_API int sqlite3_os_init(void); -SQLITE_API int sqlite3_os_end(void); +SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void); +SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void); +SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library @@ -1352,10 +1376,11 @@ SQLITE_API int sqlite3_os_end(void); ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ -SQLITE_API int sqlite3_config(int, ...); +SQLITE_API int SQLITE_CDECL sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections +** METHOD: sqlite3 ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to @@ -1370,7 +1395,7 @@ SQLITE_API int sqlite3_config(int, ...); ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ -SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines @@ -1530,7 +1555,7 @@ struct sqlite3_mem_methods { ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] -** <li> [sqlite3_status()] +** <li> [sqlite3_status64()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory @@ -1741,7 +1766,6 @@ struct sqlite3_mem_methods { ** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro ** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value ** that specifies the maximum size of the created heap. -** </dl> ** ** [[SQLITE_CONFIG_PCACHE_HDRSZ]] ** <dt>SQLITE_CONFIG_PCACHE_HDRSZ @@ -1854,15 +1878,17 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Enable Or Disable Extended Result Codes +** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ -SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid +** METHOD: sqlite3 ** ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) ** has a unique 64-bit signed @@ -1910,10 +1936,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ -SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE @@ -1962,10 +1989,11 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified +** METHOD: sqlite3 ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed @@ -1985,10 +2013,11 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ -SQLITE_API int sqlite3_total_changes(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query +** METHOD: sqlite3 ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically @@ -2024,7 +2053,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ -SQLITE_API void sqlite3_interrupt(sqlite3*); +SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -2059,12 +2088,13 @@ SQLITE_API void sqlite3_interrupt(sqlite3*); ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ -SQLITE_API int sqlite3_complete(const char *sql); -SQLITE_API int sqlite3_complete16(const void *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *sql); +SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** KEYWORDS: {busy-handler callback} {busy handler} +** METHOD: sqlite3 ** ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X ** that might be invoked with argument P whenever @@ -2120,10 +2150,11 @@ SQLITE_API int sqlite3_complete16(const void *sql); ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout +** METHOD: sqlite3 ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler @@ -2142,10 +2173,11 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); ** ** See also: [PRAGMA busy_timeout] */ -SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); +SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries +** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. @@ -2216,7 +2248,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ -SQLITE_API int sqlite3_get_table( +SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ @@ -2224,13 +2256,17 @@ SQLITE_API int sqlite3_get_table( int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); -SQLITE_API void sqlite3_free_table(char **result); +SQLITE_API void SQLITE_STDCALL sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. +** These routines understand most of the common K&R formatting options, +** plus some additional non-standard formats, detailed below. +** Note that some of the more obscure formatting options from recent +** C-library standards are omitted from this implementation. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. @@ -2263,7 +2299,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there -** is are "%q", "%Q", and "%z" options. +** is are "%q", "%Q", "%w" and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. @@ -2316,14 +2352,20 @@ SQLITE_API void sqlite3_free_table(char **result); ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** +** ^(The "%w" formatting option is like "%q" except that it expects to +** be contained within double-quotes instead of single quotes, and it +** escapes the double-quote character instead of the single-quote +** character.)^ The "%w" formatting option is intended for safely inserting +** table and column names into a constructed SQL statement. +** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ -SQLITE_API char *sqlite3_mprintf(const char*,...); -SQLITE_API char *sqlite3_vmprintf(const char*, va_list); -SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); -SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char*,...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char*, va_list); +SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2413,12 +2455,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); ** a block of memory after it has been released using ** [sqlite3_free()] or [sqlite3_realloc()]. */ -SQLITE_API void *sqlite3_malloc(int); -SQLITE_API void *sqlite3_malloc64(sqlite3_uint64); -SQLITE_API void *sqlite3_realloc(void*, int); -SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64); -SQLITE_API void sqlite3_free(void*); -SQLITE_API sqlite3_uint64 sqlite3_msize(void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int); +SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void*, int); +SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void*, sqlite3_uint64); +SQLITE_API void SQLITE_STDCALL sqlite3_free(void*); +SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void*); /* ** CAPI3REF: Memory Allocator Statistics @@ -2443,8 +2485,8 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void*); ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ -SQLITE_API sqlite3_int64 sqlite3_memory_used(void); -SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator @@ -2467,10 +2509,11 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ -SQLITE_API void sqlite3_randomness(int N, void *P); +SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks +** METHOD: sqlite3 ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. @@ -2549,7 +2592,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ -SQLITE_API int sqlite3_set_authorizer( +SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData @@ -2627,6 +2670,7 @@ SQLITE_API int sqlite3_set_authorizer( /* ** CAPI3REF: Tracing And Profiling Functions +** METHOD: sqlite3 ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. @@ -2653,12 +2697,13 @@ SQLITE_API int sqlite3_set_authorizer( ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ -SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, +SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to @@ -2688,10 +2733,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, ** database connections for the meaning of "modify" in this paragraph. ** */ -SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); +SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection +** CONSTRUCTOR: sqlite3 ** ** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for @@ -2916,15 +2962,15 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** ** See also: [sqlite3_temp_directory] */ -SQLITE_API int sqlite3_open( +SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open16( +SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -SQLITE_API int sqlite3_open_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -2970,19 +3016,22 @@ SQLITE_API int sqlite3_open_v2( ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ -SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); -SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); -SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); +SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages -** -** ^The sqlite3_errcode() interface returns the numeric [result code] or -** [extended result code] for the most recent failed sqlite3_* API call -** associated with a [database connection]. If a prior API call failed -** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() +** METHOD: sqlite3 +** +** ^If the most recent sqlite3_* API call associated with +** [database connection] D failed, then the sqlite3_errcode(D) interface +** returns the numeric [result code] or [extended result code] for that +** API call. +** If the most recent API call was successful, +** then the return value from sqlite3_errcode() is undefined. +** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. @@ -3013,40 +3062,41 @@ SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ -SQLITE_API int sqlite3_errcode(sqlite3 *db); -SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); -SQLITE_API const char *sqlite3_errmsg(sqlite3*); -SQLITE_API const void *sqlite3_errmsg16(sqlite3*); -SQLITE_API const char *sqlite3_errstr(int); +SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db); +SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int); /* -** CAPI3REF: SQL Statement Object +** CAPI3REF: Prepared Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** -** An instance of this object represents a single SQL statement. -** This object is variously known as a "prepared statement" or a -** "compiled SQL statement" or simply as a "statement". +** An instance of this object represents a single SQL statement that +** has been compiled into binary form and is ready to be evaluated. ** -** The life of a statement object goes something like this: +** Think of each SQL statement as a separate computer program. The +** original SQL text is source code. A prepared statement object +** is the compiled object code. All SQL must be converted into a +** prepared statement before it can be run. +** +** The life-cycle of a prepared statement object usually goes like this: ** ** <ol> -** <li> Create the object using [sqlite3_prepare_v2()] or a related -** function. -** <li> Bind values to [host parameters] using the sqlite3_bind_*() +** <li> Create the prepared statement object using [sqlite3_prepare_v2()]. +** <li> Bind values to [parameters] using the sqlite3_bind_*() ** interfaces. ** <li> Run the SQL by calling [sqlite3_step()] one or more times. -** <li> Reset the statement using [sqlite3_reset()] then go back +** <li> Reset the prepared statement using [sqlite3_reset()] then go back ** to step 2. Do this zero or more times. ** <li> Destroy the object using [sqlite3_finalize()]. ** </ol> -** -** Refer to documentation on individual methods above for additional -** information. */ typedef struct sqlite3_stmt sqlite3_stmt; /* ** CAPI3REF: Run-time Limits +** METHOD: sqlite3 ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the @@ -3084,7 +3134,7 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** ** New run-time limit categories may be added in future releases. */ -SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); +SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories @@ -3158,6 +3208,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_stmt ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. @@ -3171,16 +3223,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** ^If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. ^If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. ^When nByte is non-negative, the -** zSql string ends at either the first '\000' or '\u0000' character or -** the nByte-th byte, whichever comes first. If the caller knows -** that the supplied string is nul-terminated, then there is a small -** performance advantage to be gained by passing an nByte parameter that -** is equal to the number of bytes in the input string <i>including</i> -** the nul-terminator bytes as this saves SQLite from having to -** make a copy of the input string. +** ^If the nByte argument is negative, then zSql is read up to the +** first zero terminator. ^If nByte is positive, then it is the +** number of bytes read from zSql. ^If nByte is zero, then no prepared +** statement is generated. +** If the caller knows that the supplied string is nul-terminated, then +** there is a small performance advantage to passing an nByte parameter that +** is the number of bytes in the input string <i>including</i> +** the nul-terminator. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -3236,28 +3286,28 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** </li> ** </ol> */ -SQLITE_API int sqlite3_prepare( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); -SQLITE_API int sqlite3_prepare16_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ @@ -3267,15 +3317,17 @@ SQLITE_API int sqlite3_prepare16_v2( /* ** CAPI3REF: Retrieving Statement SQL +** METHOD: sqlite3_stmt ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ -SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to @@ -3303,10 +3355,11 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ -SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using @@ -3322,7 +3375,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** for example, in diagnostic routines to search for prepared ** statements that are holding a transaction open. */ -SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*); /* ** CAPI3REF: Dynamically Typed Value Object @@ -3381,6 +3434,7 @@ typedef struct sqlite3_context sqlite3_context; ** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} +** METHOD: sqlite3_stmt ** ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, ** literals may be replaced by a [parameter] that matches one of following @@ -3483,22 +3537,23 @@ typedef struct sqlite3_context sqlite3_context; ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, void(*)(void*)); -SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); -SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); -SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); -SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); -SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, +SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void(*)(void*), unsigned char encoding); -SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters +** METHOD: sqlite3_stmt ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the @@ -3515,10 +3570,11 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter +** METHOD: sqlite3_stmt ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. @@ -3542,10 +3598,11 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name +** METHOD: sqlite3_stmt ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second @@ -3558,19 +3615,21 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); +SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ -SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set +** METHOD: sqlite3_stmt ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL @@ -3578,10 +3637,11 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); ** ** See also: [sqlite3_data_count()] */ -SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set +** METHOD: sqlite3_stmt ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() @@ -3606,11 +3666,12 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ -SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); -SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result +** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in @@ -3654,15 +3715,16 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ -SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result +** METHOD: sqlite3_stmt ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the @@ -3690,11 +3752,12 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); ** is associated with individual values, not with the containers ** used to hold those values. */ -SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); -SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement +** METHOD: sqlite3_stmt ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -3770,10 +3833,11 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ -SQLITE_API int sqlite3_step(sqlite3_stmt*); +SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set +** METHOD: sqlite3_stmt ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. @@ -3790,7 +3854,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); ** ** See also: [sqlite3_column_count()] */ -SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes @@ -3827,6 +3891,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} +** METHOD: sqlite3_stmt ** ** These routines form the "result set" interface. ** @@ -3986,19 +4051,20 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ -SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); -SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object +** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors @@ -4022,10 +4088,11 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ -SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object +** METHOD: sqlite3_stmt ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. @@ -4048,13 +4115,14 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ -SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); +SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} +** METHOD: sqlite3 ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior @@ -4147,7 +4215,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ -SQLITE_API int sqlite3_create_function( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4157,7 +4225,7 @@ SQLITE_API int sqlite3_create_function( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -4167,7 +4235,7 @@ SQLITE_API int sqlite3_create_function16( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -SQLITE_API int sqlite3_create_function_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, @@ -4209,21 +4277,22 @@ SQLITE_API int sqlite3_create_function_v2( ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid -** the use of these functions. To help encourage people to avoid -** using these functions, we are not going to tell you what they do. +** the use of these functions. To encourage programmers to avoid +** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values +** METHOD: sqlite3_value ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -4267,21 +4336,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ -SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes(sqlite3_value*); -SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); -SQLITE_API double sqlite3_value_double(sqlite3_value*); -SQLITE_API int sqlite3_value_int(sqlite3_value*); -SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); -SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); -SQLITE_API int sqlite3_value_type(sqlite3_value*); -SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*); +SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context +** METHOD: sqlite3_context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. @@ -4322,10 +4392,11 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ -SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); +SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) @@ -4336,10 +4407,11 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** This routine must be called from the same thread in which ** the application-defined function is running. */ -SQLITE_API void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions +** METHOD: sqlite3_context ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) @@ -4347,10 +4419,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*); ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ -SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data +** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -4399,8 +4472,8 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** These routines must be called from the same thread in which ** the SQL function is running. */ -SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); -SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* @@ -4423,6 +4496,7 @@ typedef void (*sqlite3_destructor_type)(void*); /* ** CAPI3REF: Setting The Result Of An SQL Function +** METHOD: sqlite3_context ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -4535,29 +4609,30 @@ typedef void (*sqlite3_destructor_type)(void*); ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ -SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*, +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); -SQLITE_API void sqlite3_result_double(sqlite3_context*, double); -SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); -SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); -SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); -SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); -SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int(sqlite3_context*, int); -SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -SQLITE_API void sqlite3_result_null(sqlite3_context*); -SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, +SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); -SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences +** METHOD: sqlite3 ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. @@ -4635,14 +4710,14 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ -SQLITE_API int sqlite3_create_collation( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); -SQLITE_API int sqlite3_create_collation_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -4650,7 +4725,7 @@ SQLITE_API int sqlite3_create_collation_v2( int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); -SQLITE_API int sqlite3_create_collation16( +SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, @@ -4660,6 +4735,7 @@ SQLITE_API int sqlite3_create_collation16( /* ** CAPI3REF: Collation Needed Callbacks +** METHOD: sqlite3 ** ** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the @@ -4684,12 +4760,12 @@ SQLITE_API int sqlite3_create_collation16( ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ -SQLITE_API int sqlite3_collation_needed( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); -SQLITE_API int sqlite3_collation_needed16( +SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) @@ -4703,11 +4779,11 @@ SQLITE_API int sqlite3_collation_needed16( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_key( +SQLITE_API int SQLITE_STDCALL sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); -SQLITE_API int sqlite3_key_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_key_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The key */ @@ -4721,11 +4797,11 @@ SQLITE_API int sqlite3_key_v2( ** The code to implement this API is not available in the public release ** of SQLite. */ -SQLITE_API int sqlite3_rekey( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); -SQLITE_API int sqlite3_rekey_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_rekey_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The new key */ @@ -4735,7 +4811,7 @@ SQLITE_API int sqlite3_rekey_v2( ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ -SQLITE_API void sqlite3_activate_see( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4745,7 +4821,7 @@ SQLITE_API void sqlite3_activate_see( ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ -SQLITE_API void sqlite3_activate_cerod( +SQLITE_API void SQLITE_STDCALL sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif @@ -4767,7 +4843,7 @@ SQLITE_API void sqlite3_activate_cerod( ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ -SQLITE_API int sqlite3_sleep(int); +SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files @@ -4867,6 +4943,7 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} +** METHOD: sqlite3 ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, @@ -4885,10 +4962,11 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory; ** connection while this routine is running, then the return value ** is undefined. */ -SQLITE_API int sqlite3_get_autocommit(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement +** METHOD: sqlite3_stmt ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] @@ -4897,10 +4975,11 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ -SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file @@ -4913,19 +4992,21 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ -SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); +SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only +** METHOD: sqlite3 ** ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N ** of connection D is read-only, 0 if it is read/write, or -1 if N is not ** the name of a database on connection D. */ -SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); +SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Find the next prepared statement +** METHOD: sqlite3 ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL @@ -4937,10 +5018,11 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ -SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); +SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. @@ -4985,11 +5067,12 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** ** See also the [sqlite3_update_hook()] interface. */ -SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks +** METHOD: sqlite3 ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument @@ -5036,7 +5119,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ -SQLITE_API void *sqlite3_update_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* @@ -5066,12 +5149,17 @@ SQLITE_API void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 +** and will always return SQLITE_MISUSE. On those systems, +** shared cache mode should be enabled per-database connection via +** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. +** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ -SQLITE_API int sqlite3_enable_shared_cache(int); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory @@ -5087,10 +5175,11 @@ SQLITE_API int sqlite3_enable_shared_cache(int); ** ** See also: [sqlite3_db_release_memory()] */ -SQLITE_API int sqlite3_release_memory(int); +SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int); /* ** CAPI3REF: Free Memory Used By A Database Connection +** METHOD: sqlite3 ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the @@ -5100,7 +5189,7 @@ SQLITE_API int sqlite3_release_memory(int); ** ** See also: [sqlite3_release_memory()] */ -SQLITE_API int sqlite3_db_release_memory(sqlite3*); +SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size @@ -5152,7 +5241,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*); ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ -SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); +SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface @@ -5163,11 +5252,12 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ -SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); +SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table +** METHOD: sqlite3 ** ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns ** information about column C of table T in database D @@ -5232,7 +5322,7 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** parsed, if that has not already been done, and returns an error if ** any errors are encountered while loading the schema. */ -SQLITE_API int sqlite3_table_column_metadata( +SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -5246,6 +5336,7 @@ SQLITE_API int sqlite3_table_column_metadata( /* ** CAPI3REF: Load An Extension +** METHOD: sqlite3 ** ** ^This interface loads an SQLite extension library from the named file. ** @@ -5278,7 +5369,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ** See also the [load_extension() SQL function]. */ -SQLITE_API int sqlite3_load_extension( +SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ @@ -5287,6 +5378,7 @@ SQLITE_API int sqlite3_load_extension( /* ** CAPI3REF: Enable Or Disable Extension Loading +** METHOD: sqlite3 ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with [extension loading], and as a means of disabling @@ -5298,7 +5390,7 @@ SQLITE_API int sqlite3_load_extension( ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ -SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); +SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions @@ -5336,7 +5428,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ -SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Cancel Automatic Extension Loading @@ -5348,7 +5440,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); ** unregistered and it returns 0 if X was not on the list of initialization ** routines. */ -SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading @@ -5356,7 +5448,7 @@ SQLITE_API int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ -SQLITE_API void sqlite3_reset_auto_extension(void); +SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered @@ -5536,6 +5628,7 @@ struct sqlite3_index_info { /* ** CAPI3REF: Register A Virtual Table Implementation +** METHOD: sqlite3 ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before @@ -5559,13 +5652,13 @@ struct sqlite3_index_info { ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ -SQLITE_API int sqlite3_create_module( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); -SQLITE_API int sqlite3_create_module_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -5593,7 +5686,7 @@ SQLITE_API int sqlite3_create_module_v2( */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* NO LONGER USED */ + int nRef; /* Number of open cursors */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; @@ -5628,10 +5721,11 @@ struct sqlite3_vtab_cursor { ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table +** METHOD: sqlite3 ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. @@ -5646,7 +5740,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -5674,6 +5768,8 @@ typedef struct sqlite3_blob sqlite3_blob; /* ** CAPI3REF: Open A BLOB For Incremental I/O +** METHOD: sqlite3 +** CONSTRUCTOR: sqlite3_blob ** ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; @@ -5743,7 +5839,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ -SQLITE_API int sqlite3_blob_open( +SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, @@ -5755,6 +5851,7 @@ SQLITE_API int sqlite3_blob_open( /* ** CAPI3REF: Move a BLOB Handle to a New Row +** METHOD: sqlite3_blob ** ** ^This function is used to move an existing blob handle so that it points ** to a different row of the same database table. ^The new row is identified @@ -5775,10 +5872,11 @@ SQLITE_API int sqlite3_blob_open( ** ** ^This function sets the database handle error code and message. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle +** DESTRUCTOR: sqlite3_blob ** ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed ** unconditionally. Even if this routine returns an error code, the @@ -5797,10 +5895,11 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_i ** is passed a valid open blob handle, the values returned by the ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. */ -SQLITE_API int sqlite3_blob_close(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB +** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The @@ -5812,10 +5911,11 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ -SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z @@ -5840,10 +5940,11 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); ** ** See also: [sqlite3_blob_write()]. */ -SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally +** METHOD: sqlite3_blob ** ** ^(This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. N bytes of data are copied from the buffer Z @@ -5881,7 +5982,7 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); ** ** See also: [sqlite3_blob_read()]. */ -SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); +SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects @@ -5912,9 +6013,9 @@ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOff ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ -SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); +SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes @@ -6027,11 +6128,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ -SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); -SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); -SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object @@ -6141,8 +6242,8 @@ struct sqlite3_mutex_methods { ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG -SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); -SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* @@ -6171,6 +6272,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); /* ** CAPI3REF: Retrieve the mutex for a database connection +** METHOD: sqlite3 ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument @@ -6178,10 +6280,11 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ -SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files +** METHOD: sqlite3 ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated @@ -6212,7 +6315,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ -SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); +SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface @@ -6231,7 +6334,7 @@ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void* ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ -SQLITE_API int sqlite3_test_control(int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes @@ -6265,12 +6368,13 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 -#define SQLITE_TESTCTRL_LAST 24 +#define SQLITE_TESTCTRL_IMPOSTER 25 +#define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** -** ^This interface is used to retrieve runtime status information +** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes @@ -6284,19 +6388,22 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** ^The sqlite3_status() routine returns SQLITE_OK on success and a -** non-zero [error code] on failure. +** ^The sqlite3_status() and sqlite3_status64() routines return +** SQLITE_OK on success and a non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can be -** called while other threads are running the same or different SQLite -** interfaces. However the values returned in *pCurrent and -** *pHighwater reflect the status of SQLite at different points in time -** and it is possible that another thread might change the parameter -** in between the times when *pCurrent and *pHighwater are written. +** If either the current value or the highwater mark is too large to +** be represented by a 32-bit integer, then the values returned by +** sqlite3_status() are undefined. ** ** See also: [sqlite3_db_status()] */ -SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int SQLITE_STDCALL sqlite3_status64( + int op, + sqlite3_int64 *pCurrent, + sqlite3_int64 *pHighwater, + int resetFlag +); /* @@ -6394,6 +6501,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Database Connection Status +** METHOD: sqlite3 ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the @@ -6414,7 +6522,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections @@ -6522,6 +6630,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Prepared Statement Status +** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS counters] that measure the number @@ -6543,7 +6652,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements @@ -6966,20 +7075,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] ** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b> ** -** ^Each call to sqlite3_backup_step() sets two values inside -** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source database file. -** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces -** retrieve these two values, respectively. -** -** ^The values returned by these functions are only updated by -** sqlite3_backup_step(). ^If the source database is modified during a backup -** operation, then the values are not updated to account for any extra -** pages that need to be updated or the size of the source database file -** changing. +** ^The sqlite3_backup_remaining() routine returns the number of pages still +** to be backed up at the conclusion of the most recent sqlite3_backup_step(). +** ^The sqlite3_backup_pagecount() routine returns the total number of pages +** in the source database at the conclusion of the most recent +** sqlite3_backup_step(). +** ^(The values returned by these functions are only updated by +** sqlite3_backup_step(). If the source database is modified in a way that +** changes the size of the source database or the number of pages remaining, +** those changes are not reflected in the output of sqlite3_backup_pagecount() +** and sqlite3_backup_remaining() until after the next +** sqlite3_backup_step().)^ ** ** <b>Concurrent Usage of Database Handles</b> ** @@ -7012,19 +7121,20 @@ typedef struct sqlite3_backup sqlite3_backup; ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ -SQLITE_API sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); -SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); -SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); -SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification +** METHOD: sqlite3 ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or @@ -7137,7 +7247,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ -SQLITE_API int sqlite3_unlock_notify( +SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ @@ -7152,8 +7262,8 @@ SQLITE_API int sqlite3_unlock_notify( ** strings in a case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ -SQLITE_API int sqlite3_stricmp(const char *, const char *); -SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); +SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *, const char *); +SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing @@ -7168,7 +7278,7 @@ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. */ -SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); +SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr); /* ** CAPI3REF: Error Logging Interface @@ -7191,10 +7301,11 @@ SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr); ** a few hundred characters, it will be truncated to the length of the ** buffer. */ -SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); +SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** is invoked each time data is committed to a database in wal mode. @@ -7226,7 +7337,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ -SQLITE_API void *sqlite3_wal_hook( +SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* @@ -7234,6 +7345,7 @@ SQLITE_API void *sqlite3_wal_hook( /* ** CAPI3REF: Configure an auto-checkpoint +** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D @@ -7260,10 +7372,11 @@ SQLITE_API void *sqlite3_wal_hook( ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ -SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ @@ -7281,10 +7394,11 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** start a callback but which do not need the full power (and corresponding ** complication) of [sqlite3_wal_checkpoint_v2()]. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database +** METHOD: sqlite3 ** ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint ** operation on database X of [database connection] D in mode M. Status @@ -7374,7 +7488,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface ** from SQL. */ -SQLITE_API int sqlite3_wal_checkpoint_v2( +SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ @@ -7410,7 +7524,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( ** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options ** may be added in the future. */ -SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options @@ -7463,7 +7577,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ -SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); +SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Conflict resolution modes @@ -7539,6 +7653,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Prepared Statement Scan Status +** METHOD: sqlite3_stmt ** ** This interface returns information about the predicted and measured ** performance for pStmt. Advanced applications can use this @@ -7567,7 +7682,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( +SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ @@ -7576,13 +7691,14 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( /* ** CAPI3REF: Zero Scan-Status Counters +** METHOD: sqlite3_stmt ** ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. ** ** This API is only available if the library is built with pre-processor ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. */ -SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); +SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* @@ -7637,7 +7753,7 @@ typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ -SQLITE_API int sqlite3_rtree_geometry_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), @@ -7663,7 +7779,7 @@ struct sqlite3_rtree_geometry { ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...) */ -SQLITE_API int sqlite3_rtree_query_callback( +SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), diff --git a/ext/standard/array.c b/ext/standard/array.c index 3397de923b..b05900656d 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -821,7 +821,8 @@ PHP_FUNCTION(end) entry = Z_INDIRECT_P(entry); } - RETURN_ZVAL_FAST(entry); + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -854,7 +855,8 @@ PHP_FUNCTION(prev) entry = Z_INDIRECT_P(entry); } - RETURN_ZVAL_FAST(entry); + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -887,7 +889,8 @@ PHP_FUNCTION(next) entry = Z_INDIRECT_P(entry); } - RETURN_ZVAL_FAST(entry); + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -920,7 +923,8 @@ PHP_FUNCTION(reset) entry = Z_INDIRECT_P(entry); } - RETURN_ZVAL_FAST(entry); + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -950,7 +954,8 @@ PHP_FUNCTION(current) entry = Z_INDIRECT_P(entry); } - RETURN_ZVAL_FAST(entry); + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } /* }}} */ @@ -996,7 +1001,8 @@ PHP_FUNCTION(min) RETVAL_NULL(); } else { if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) { - RETVAL_ZVAL_FAST(result); + ZVAL_DEREF(result); + ZVAL_COPY(return_value, result); } else { php_error_docref(NULL, E_WARNING, "Array must contain at least one element"); RETVAL_FALSE; @@ -1016,7 +1022,8 @@ PHP_FUNCTION(min) } } - RETVAL_ZVAL_FAST(min); + ZVAL_DEREF(min); + ZVAL_COPY(return_value, min); } } /* }}} */ @@ -1043,7 +1050,8 @@ PHP_FUNCTION(max) RETVAL_NULL(); } else { if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) { - RETVAL_ZVAL_FAST(result); + ZVAL_DEREF(result); + ZVAL_COPY(return_value, result); } else { php_error_docref(NULL, E_WARNING, "Array must contain at least one element"); RETVAL_FALSE; @@ -1063,7 +1071,8 @@ PHP_FUNCTION(max) } } - RETVAL_ZVAL_FAST(max); + ZVAL_DEREF(max); + ZVAL_COPY(return_value, max); } } /* }}} */ @@ -2588,7 +2597,7 @@ PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */ } ZVAL_UNDEF(&tmp); if (Z_TYPE_P(src_zval) == IS_OBJECT) { - ZVAL_DUP(&tmp, src_zval); + ZVAL_COPY(&tmp, src_zval); convert_to_array(&tmp); src_zval = &tmp; } @@ -3061,6 +3070,7 @@ PHP_FUNCTION(array_column) array_init(return_value); ZEND_HASH_FOREACH_VAL(arr_hash, data) { + ZVAL_DEREF(data); if (Z_TYPE_P(data) != IS_ARRAY) { /* Skip elemens which are not sub-arrays */ continue; @@ -4623,7 +4633,7 @@ PHP_FUNCTION(array_sum) if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { continue; } - ZVAL_DUP(&entry_n, entry); + ZVAL_COPY(&entry_n, entry); convert_scalar_to_number(&entry_n); fast_add_function(return_value, return_value, &entry_n); } ZEND_HASH_FOREACH_END(); @@ -4652,7 +4662,7 @@ PHP_FUNCTION(array_product) if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { continue; } - ZVAL_DUP(&entry_n, entry); + ZVAL_COPY(&entry_n, entry); convert_scalar_to_number(&entry_n); if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) { @@ -4700,7 +4710,8 @@ PHP_FUNCTION(array_reduce) htbl = Z_ARRVAL_P(input); if (zend_hash_num_elements(htbl) == 0) { - RETURN_ZVAL(&result, 1, 1); + ZVAL_COPY_VALUE(return_value, &result); + return; } fci.retval = &retval; @@ -4855,7 +4866,7 @@ PHP_FUNCTION(array_map) /* Short-circuit: if no callback and only one array, just return it. */ if (!ZEND_FCI_INITIALIZED(fci)) { - RETVAL_ZVAL(&arrays[0], 1, 0); + ZVAL_COPY(return_value, &arrays[0]); return; } diff --git a/ext/standard/assert.c b/ext/standard/assert.c index 59db4068bd..236c2c18e9 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -343,7 +343,7 @@ PHP_FUNCTION(assert_options) case ASSERT_CALLBACK: if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) { - RETVAL_ZVAL(&ASSERTG(callback), 1, 0); + ZVAL_COPY(return_value, &ASSERTG(callback)); } else if (ASSERTG(cb)) { RETVAL_STRING(ASSERTG(cb)); } else { diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 462a234205..c4e0cee9f1 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -4144,7 +4144,7 @@ PHP_FUNCTION(putenv) Obviously the CRT version will be useful more often. But generally, doing both brings us on the safe track at least in NTS build. */ - && _putenv(pe.putenv_string) == 0 + && _putenv_s(pe.key, value ? value : "") == 0 # endif ) { /* success */ # endif diff --git a/ext/standard/credits.c b/ext/standard/credits.c index 7edec5c51d..dc40c637b7 100644 --- a/ext/standard/credits.c +++ b/ext/standard/credits.c @@ -96,8 +96,8 @@ PHPAPI void php_print_credits(int flag) /* {{{ */ if (flag & PHP_CREDITS_DOCS) { php_info_print_table_start(); php_info_print_table_colspan_header(2, "PHP Documentation"); - CREDIT_LINE("Authors", "Mehdi Achour, Friedhelm Betz, Antony Dovgal, Nuno Lopes, Hannes Magnusson, Georg Richter, Damien Seguy, Jakub Vrana, Adam Harvey, Peter Cowburn"); - CREDIT_LINE("Editor", "Philip Olson"); + CREDIT_LINE("Authors", "Mehdi Achour, Friedhelm Betz, Antony Dovgal, Nuno Lopes, Hannes Magnusson, Philip Olson, Georg Richter, Damien Seguy, Jakub Vrana, Adam Harvey"); + CREDIT_LINE("Editor", "Peter Cowburn"); CREDIT_LINE("User Note Maintainers", "Daniel P. Brown, Thiago Henrique Pojda"); CREDIT_LINE("Other Contributors", "Previously active authors, editors and other contributors are listed in the manual."); php_info_print_table_end(); diff --git a/ext/standard/credits_sapi.h b/ext/standard/credits_sapi.h index a98add49de..f677344a54 100644 --- a/ext/standard/credits_sapi.h +++ b/ext/standard/credits_sapi.h @@ -16,5 +16,4 @@ CREDIT_LINE("CLI", "Edin Kadribasic, Marcus Boerger, Johannes Schlueter, Moriyos CREDIT_LINE("Embed", "Edin Kadribasic"); CREDIT_LINE("FastCGI Process Manager", "Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet"); CREDIT_LINE("litespeed", "George Wang"); -CREDIT_LINE("NSAPI", "Jayakumar Muthukumarasamy, Uwe Schindler"); CREDIT_LINE("phpdbg", "Felipe Pena, Joe Watkins, Bob Weinand"); diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index da51ee9885..74ab291f62 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -151,7 +151,7 @@ static void php_to64(char *s, zend_long v, int n) /* {{{ */ } /* }}} */ -PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len) +PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet) { char *crypt_res; zend_string *result; @@ -225,7 +225,10 @@ PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const ch if (salt[0] != '_') { /* DES style hashes */ if (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1])) { - php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + if (!quiet) { + /* error consistently about invalid DES fallbacks */ + php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + } } } @@ -254,8 +257,10 @@ PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const ch # error Data struct used by crypt_r() is unknown. Please report. # endif if (salt[0] != '$' && salt[0] != '_' && (!IS_VALID_SALT_CHARACTER(salt[0]) || !IS_VALID_SALT_CHARACTER(salt[1]))) { - /* error consistently about invalid DES fallbacks */ - php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + if (!quiet) { + /* error consistently about invalid DES fallbacks */ + php_error_docref(NULL, E_DEPRECATED, DES_INVALID_SALT_ERROR); + } } crypt_res = crypt_r(password, salt, &buffer); if (!crypt_res || (salt[0] == '*' && salt[1] == '0')) { @@ -313,7 +318,7 @@ PHP_FUNCTION(crypt) } salt[salt_in_len] = '\0'; - if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len)) == NULL) { + if ((result = php_crypt(str, (int)str_len, salt, (int)salt_in_len, 0)) == NULL) { if (salt[0] == '*' && salt[1] == '0') { RETURN_STRING("*1"); } else { diff --git a/ext/standard/exec.c b/ext/standard/exec.c index 71dfc7c361..60fd7ba1aa 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -383,6 +383,14 @@ PHPAPI zend_string *php_escape_shell_arg(char *str) } } #ifdef PHP_WIN32 + if (y > 0 && '\\' == cmd->val[y - 1]) { + int k = 0, n = y - 1; + for (; n >= 0 && '\\' == cmd->val[n]; n--, k++); + if (k % 2) { + cmd->val[y++] = '\\'; + } + } + cmd->val[y++] = '"'; #else cmd->val[y++] = '\''; diff --git a/ext/standard/file.c b/ext/standard/file.c index 7aa1ce7a85..1755dab81b 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -122,9 +122,6 @@ php_file_globals file_globals; # include <wchar.h> #endif -#ifndef S_ISDIR -# define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif /* }}} */ #define PHP_STREAM_TO_ZVAL(stream, arg) \ @@ -522,7 +519,7 @@ PHP_FUNCTION(file_get_contents) zend_bool use_include_path = 0; php_stream *stream; zend_long offset = -1; - zend_long maxlen = PHP_STREAM_COPY_ALL; + zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL; zval *zcontext = NULL; php_stream_context *context = NULL; zend_string *contents; diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c index ffcba3598d..764b490b9a 100644 --- a/ext/standard/filestat.c +++ b/ext/standard/filestat.c @@ -85,18 +85,6 @@ #include "basic_functions.h" #include "php_filestat.h" -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif -#ifndef S_ISLNK -#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) -#endif - -#define S_IXROOT ( S_IXUSR | S_IXGRP | S_IXOTH ) - PHP_RINIT_FUNCTION(filestat) /* {{{ */ { BG(CurrentStatFile)=NULL; diff --git a/ext/standard/http.c b/ext/standard/http.c index 25b77280de..7fe094009e 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -36,7 +36,7 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, const char *prop_name; size_t arg_sep_len, newprefix_len, prop_len; zend_ulong idx; - zval *zdata = NULL, copyzval; + zval *zdata = NULL; if (!ht) { return FAILURE; @@ -204,16 +204,14 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, default: { zend_string *ekey; - /* fall back on convert to string */ - ZVAL_DUP(©zval, zdata); - convert_to_string_ex(©zval); + zend_string *tmp = zval_get_string(zdata); if (enc_type == PHP_QUERY_RFC3986) { - ekey = php_raw_url_encode(Z_STRVAL(copyzval), Z_STRLEN(copyzval)); + ekey = php_raw_url_encode(tmp->val, tmp->len); } else { - ekey = php_url_encode(Z_STRVAL(copyzval), Z_STRLEN(copyzval)); + ekey = php_url_encode(tmp->val, tmp->len); } smart_str_append(formstr, ekey); - zval_ptr_dtor(©zval); + zend_string_release(tmp); zend_string_free(ekey); } } diff --git a/ext/standard/info.c b/ext/standard/info.c index 9490aae7d6..d8794c4ad6 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -307,7 +307,17 @@ char* php_get_windows_name() GetSystemInfo(&si); } - if (VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion >= 6 ) { + if (VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion >= 10) { + if (osvi.dwMajorVersion == 10) { + if( osvi.dwMinorVersion == 0 ) { + if( osvi.wProductType == VER_NT_WORKSTATION ) { + major = "Windows 10"; + } else { + major = "Windows Server 2016"; + } + } + } + } else if (VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion >= 6) { if (osvi.dwMajorVersion == 6) { if( osvi.dwMinorVersion == 0 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) { @@ -323,6 +333,11 @@ char* php_get_windows_name() } } else if ( osvi.dwMinorVersion == 2 ) { /* could be Windows 8/Windows Server 2012, could be Windows 8.1/Windows Server 2012 R2 */ + /* XXX and one more X - the above comment is true if no manifest is used for two cases: + - if the PHP build doesn't use the correct manifest + - if PHP DLL loaded under some binary that doesn't use the correct manifest + + So keep the handling here as is for now, even if we know 6.2 is win8 and nothing else, and think about an improvement. */ OSVERSIONINFOEX osvi81; DWORDLONG dwlConditionMask = 0; int op = VER_GREATER_EQUAL; @@ -353,6 +368,12 @@ char* php_get_windows_name() major = "Windows Server 2012"; } } + } else if (osvi.dwMinorVersion == 3) { + if( osvi.wProductType == VER_NT_WORKSTATION ) { + major = "Windows 8.1"; + } else { + major = "Windows Server 2012 R2"; + } } else { major = "Unknown Windows version"; } @@ -374,10 +395,25 @@ char* php_get_windows_name() sub = "Enterprise Edition"; break; case PRODUCT_BUSINESS: - sub = "Business Edition"; + if ((osvi.dwMajorVersion > 6) || (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 0)) { + sub = "Professional Edition"; + } else { + sub = "Business Edition"; + } + break; + case PRODUCT_BUSINESS_N: + if ((osvi.dwMajorVersion > 6) || (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 0)) { + sub = "Professional N Edition"; + } else { + sub = "Business N Edition"; + } break; case PRODUCT_STARTER: - sub = "Starter Edition"; + if ((osvi.dwMajorVersion > 6) || (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 0)) { + sub = "Starter N Edition"; + } else { + sub = "Starter Edition"; + } break; case PRODUCT_CLUSTER_SERVER: sub = "Cluster Server Edition"; diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 5633372022..a9046cea69 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -224,6 +224,44 @@ void php_mail_log_to_file(char *filename, char *message, size_t message_size) { } +static int php_mail_detect_multiple_crlf(char *hdr) { + /* This function detects multiple/malformed multiple newlines. */ + size_t len; + + if (!hdr) { + return 0; + } + + /* Should not have any newlines at the beginning. */ + /* RFC 2822 2.2. Header Fields */ + if (*hdr < 33 || *hdr > 126 || *hdr == ':') { + return 1; + } + + while(*hdr) { + if (*hdr == '\r') { + if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || (*(hdr+1) == '\n' && (*(hdr+2) == '\0' || *(hdr+2) == '\n' || *(hdr+2) == '\r'))) { + /* Malformed or multiple newlines. */ + return 1; + } else { + hdr += 2; + } + } else if (*hdr == '\n') { + if (*(hdr+1) == '\0' || *(hdr+1) == '\r' || *(hdr+1) == '\n') { + /* Malformed or multiple newlines. */ + return 1; + } else { + hdr += 2; + } + } else { + hdr++; + } + } + + return 0; +} + + /* {{{ php_mail */ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd) @@ -278,6 +316,7 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char efree(tmp); } + if (PG(mail_x_header)) { const char *tmp = zend_get_executed_filename(); zend_string *f; @@ -292,6 +331,11 @@ PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char zend_string_release(f); } + if (hdr && php_mail_detect_multiple_crlf(hdr)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Multiple or malformed newlines found in additional_header"); + MAIL_RET(0); + } + if (!sendmail_path) { #if (defined PHP_WIN32 || defined NETWARE) /* handle old style win smtp sending */ diff --git a/ext/standard/math.c b/ext/standard/math.c index a3685c00b9..1ae74f8049 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -327,9 +327,16 @@ PHP_FUNCTION(ceil) { zval *value; +#ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); +#endif + convert_scalar_to_number_ex(value); if (Z_TYPE_P(value) == IS_DOUBLE) { @@ -347,9 +354,16 @@ PHP_FUNCTION(floor) { zval *value; +#ifndef FAST_ZPP if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); +#endif + convert_scalar_to_number_ex(value); if (Z_TYPE_P(value) == IS_DOUBLE) { diff --git a/ext/standard/microtime.c b/ext/standard/microtime.c index 6d5b8cc695..4b391bbb7d 100644 --- a/ext/standard/microtime.c +++ b/ext/standard/microtime.c @@ -25,6 +25,7 @@ #endif #ifdef PHP_WIN32 #include "win32/time.h" +#include "win32/getrusage.h" #elif defined(NETWARE) #include <sys/timeval.h> #include <sys/time.h> @@ -129,9 +130,14 @@ PHP_FUNCTION(getrusage) } array_init(return_value); + #define PHP_RUSAGE_PARA(a) \ add_assoc_long(return_value, #a, usg.a) -#if !defined( _OSD_POSIX) && !defined(__BEOS__) /* BS2000 has only a few fields in the rusage struct */ + +#ifdef PHP_WIN32 /* Windows only implements a limited amount of fields from the rusage struct */ + PHP_RUSAGE_PARA(ru_majflt); + PHP_RUSAGE_PARA(ru_maxrss); +#elif !defined( _OSD_POSIX) && !defined(__BEOS__) /* BS2000 has only a few fields in the rusage struct*/ PHP_RUSAGE_PARA(ru_oublock); PHP_RUSAGE_PARA(ru_inblock); PHP_RUSAGE_PARA(ru_msgsnd); @@ -150,6 +156,7 @@ PHP_FUNCTION(getrusage) PHP_RUSAGE_PARA(ru_utime.tv_sec); PHP_RUSAGE_PARA(ru_stime.tv_usec); PHP_RUSAGE_PARA(ru_stime.tv_sec); + #undef PHP_RUSAGE_PARA } #endif /* HAVE_GETRUSAGE */ diff --git a/ext/standard/password.c b/ext/standard/password.c index 209e2533f8..592f41838d 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -260,7 +260,7 @@ PHP_FUNCTION(password_verify) if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &password, &password_len, &hash, &hash_len) == FAILURE) { RETURN_FALSE; } - if ((ret = php_crypt(password, (int)password_len, hash, (int)hash_len)) == NULL) { + if ((ret = php_crypt(password, (int)password_len, hash, (int)hash_len, 1)) == NULL) { RETURN_FALSE; } @@ -340,18 +340,13 @@ PHP_FUNCTION(password_hash) break; case IS_LONG: case IS_DOUBLE: - case IS_OBJECT: { - zval cast_option_buffer; - - ZVAL_DUP(&cast_option_buffer, option_buffer); - convert_to_string(&cast_option_buffer); - if (Z_TYPE(cast_option_buffer) == IS_STRING) { - buffer = estrndup(Z_STRVAL(cast_option_buffer), Z_STRLEN(cast_option_buffer)); - buffer_len = Z_STRLEN(cast_option_buffer); - zval_dtor(&cast_option_buffer); - break; - } - zval_dtor(&cast_option_buffer); + case IS_OBJECT: + { + zend_string *tmp = zval_get_string(option_buffer); + buffer = estrndup(tmp->val, tmp->len); + buffer_len = tmp->len; + zend_string_release(tmp); + break; } case IS_FALSE: case IS_TRUE: @@ -415,7 +410,7 @@ PHP_FUNCTION(password_hash) /* This cast is safe, since both values are defined here in code and cannot overflow */ hash_len = (int) (hash_format_len + salt_len); - if ((result = php_crypt(password, (int)password_len, hash, hash_len)) == NULL) { + if ((result = php_crypt(password, (int)password_len, hash, hash_len, 1)) == NULL) { efree(hash); RETURN_FALSE; } diff --git a/ext/standard/php_crypt.h b/ext/standard/php_crypt.h index d77180bd0b..88368c966f 100644 --- a/ext/standard/php_crypt.h +++ b/ext/standard/php_crypt.h @@ -23,7 +23,7 @@ #ifndef PHP_CRYPT_H #define PHP_CRYPT_H -PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len); +PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const char *salt, int salt_len, zend_bool quiet); PHP_FUNCTION(crypt); #if HAVE_CRYPT PHP_MINIT_FUNCTION(crypt); diff --git a/ext/standard/php_string.h b/ext/standard/php_string.h index a02658dc63..b5cf8f3051 100644 --- a/ext/standard/php_string.h +++ b/ext/standard/php_string.h @@ -154,6 +154,9 @@ PHPAPI char *php_strerror(int errnum); # define php_mblen(ptr, len) 1 # define php_mb_reset() #elif defined(_REENTRANT) && defined(HAVE_MBRLEN) && defined(HAVE_MBSTATE_T) +# ifdef PHP_WIN32 +# include <wchar.h> +# endif # define php_mblen(ptr, len) ((int) mbrlen(ptr, len, &BG(mblen_state))) # define php_mb_reset() memset(&BG(mblen_state), 0, sizeof(BG(mblen_state))) #else diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index d774808a7b..bbcdd1073a 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -240,6 +240,7 @@ static void proc_open_rsrc_dtor(zend_resource *rsrc) FG(pclose_ret) = -1; #endif _php_free_envp(proc->env, proc->is_persistent); + pefree(proc->pipes, proc->is_persistent); pefree(proc->command, proc->is_persistent); pefree(proc, proc->is_persistent); @@ -434,7 +435,8 @@ PHP_FUNCTION(proc_open) zval *descitem = NULL; zend_string *str_index; zend_ulong nindex; - struct php_proc_open_descriptor_item descriptors[PHP_PROC_OPEN_MAX_DESCRIPTORS]; + struct php_proc_open_descriptor_item *descriptors = NULL; + int ndescriptors_array; #ifdef PHP_WIN32 PROCESS_INFORMATION pi; HANDLE childHandle; @@ -499,7 +501,11 @@ PHP_FUNCTION(proc_open) memset(&env, 0, sizeof(env)); } - memset(descriptors, 0, sizeof(descriptors)); + ndescriptors_array = zend_hash_num_elements(Z_ARRVAL_P(descriptorspec)); + + descriptors = safe_emalloc(sizeof(struct php_proc_open_descriptor_item), ndescriptors_array, 0); + + memset(descriptors, 0, sizeof(struct php_proc_open_descriptor_item) * ndescriptors_array); #ifdef PHP_WIN32 /* we use this to allow the child to inherit handles */ @@ -669,9 +675,7 @@ PHP_FUNCTION(proc_open) goto exit_fail; } } - - if (++ndesc == PHP_PROC_OPEN_MAX_DESCRIPTORS) - break; + ndesc++; } ZEND_HASH_FOREACH_END(); #ifdef PHP_WIN32 @@ -875,6 +879,7 @@ PHP_FUNCTION(proc_open) proc = (struct php_process_handle*)pemalloc(sizeof(struct php_process_handle), is_persistent); proc->is_persistent = is_persistent; proc->command = command; + proc->pipes = pemalloc(sizeof(zend_resource *) * ndesc, is_persistent); proc->npipes = ndesc; proc->child = child; #ifdef PHP_WIN32 @@ -952,10 +957,12 @@ PHP_FUNCTION(proc_open) } } + efree(descriptors); ZVAL_RES(return_value, zend_register_resource(proc, le_proc_open)); return; exit_fail: + efree(descriptors); _php_free_envp(env, is_persistent); pefree(command, is_persistent); #if PHP_CAN_DO_PTS diff --git a/ext/standard/proc_open.h b/ext/standard/proc_open.h index 13177ce2b0..52e5c1ed87 100644 --- a/ext/standard/proc_open.h +++ b/ext/standard/proc_open.h @@ -25,8 +25,6 @@ typedef int php_file_descriptor_t; typedef pid_t php_process_id_t; #endif -#define PHP_PROC_OPEN_MAX_DESCRIPTORS 16 - /* Environment block under win32 is a NUL terminated sequence of NUL terminated * name=value strings. * Under unix, it is an argv style array. @@ -44,7 +42,7 @@ struct php_process_handle { HANDLE childHandle; #endif int npipes; - zend_resource *pipes[PHP_PROC_OPEN_MAX_DESCRIPTORS]; + zend_resource **pipes; char *command; int is_persistent; php_process_env_t env; diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index fa9758b06c..be0cbefac1 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -397,7 +397,7 @@ PHP_FUNCTION(stream_get_contents) { php_stream *stream; zval *zsrc; - zend_long maxlen = PHP_STREAM_COPY_ALL, + zend_long maxlen = (ssize_t) PHP_STREAM_COPY_ALL, desiredpos = -1L; zend_string *contents; @@ -955,7 +955,7 @@ PHP_FUNCTION(stream_context_get_options) RETURN_FALSE; } - RETURN_ZVAL(&context->options, 1, 0); + ZVAL_COPY(return_value, &context->options); } /* }}} */ @@ -1038,8 +1038,8 @@ PHP_FUNCTION(stream_context_get_params) add_assoc_zval_ex(return_value, "notification", sizeof("notification")-1, &context->notifier->ptr); if (Z_REFCOUNTED(context->notifier->ptr)) Z_ADDREF(context->notifier->ptr); } - ZVAL_ZVAL(&options, &context->options, 1, 0); - add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &options); + if (Z_REFCOUNTED(context->options)) Z_ADDREF(context->options); + add_assoc_zval_ex(return_value, "options", sizeof("options")-1, &context->options); } /* }}} */ diff --git a/ext/standard/string.c b/ext/standard/string.c index 3162533186..9bf3b4c61f 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1200,69 +1200,79 @@ PHP_FUNCTION(explode) */ PHPAPI void php_implode(const zend_string *delim, zval *arr, zval *return_value) { - zval *tmp; - smart_str implstr = {0}; - int numelems, i = 0; - zend_string *str; + zval *tmp; + int numelems; + zend_string *str; + char *cptr; + size_t len = 0; + zend_string **strings, **strptr; numelems = zend_hash_num_elements(Z_ARRVAL_P(arr)); if (numelems == 0) { RETURN_EMPTY_STRING(); + } else if (numelems == 1) { + /* loop to search the first not undefined element... */ + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { + RETURN_STR(zval_get_string(tmp)); + } ZEND_HASH_FOREACH_END(); } - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { -again: - switch (Z_TYPE_P(tmp)) { - case IS_STRING: - smart_str_append(&implstr, Z_STR_P(tmp)); - break; - - case IS_LONG: - smart_str_append_long(&implstr, Z_LVAL_P(tmp)); - break; + strings = emalloc((sizeof(zend_long) + sizeof(zend_string *)) * numelems); + strptr = strings - 1; - case IS_TRUE: - smart_str_appendl(&implstr, "1", sizeof("1")-1); - break; - - case IS_NULL: - case IS_FALSE: - break; - - case IS_DOUBLE: { - char *stmp; - size_t str_len = spprintf(&stmp, 0, "%.*G", (int) EG(precision), Z_DVAL_P(tmp)); - smart_str_appendl(&implstr, stmp, str_len); - efree(stmp); - break; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), tmp) { + if (Z_TYPE_P(tmp) == IS_LONG) { + double val = Z_LVAL_P(tmp); + *++strptr = NULL; + ((zend_long *) (strings + numelems))[strptr - strings] = Z_LVAL_P(tmp); + if (val < 0) { + val = -10 * val; } - - case IS_REFERENCE: - tmp = Z_REFVAL_P(tmp); - goto again; - - default: - str = zval_get_string(tmp); - smart_str_append(&implstr, str); - zend_string_release(str); - break; - + if (val < 10) { + len++; + } else { + len += (int) log10(10 * (double) val); + } + } else { + *++strptr = zval_get_string(tmp); + len += (*strptr)->len; } + } ZEND_HASH_FOREACH_END(); - if (++i != numelems) { - smart_str_append(&implstr, delim); + str = zend_string_alloc(len + (numelems - 1) * delim->len, 0); + cptr = str->val + str->len; + *cptr = 0; + + do { + if (*strptr) { + cptr -= (*strptr)->len; + memcpy(cptr, (*strptr)->val, (*strptr)->len); + zend_string_release(*strptr); + } else { + char *oldPtr = cptr; + char oldVal = *cptr; + zend_long val = ((zend_long *) (strings + numelems))[strptr - strings]; + cptr = zend_print_long_to_buf(cptr, val); + *oldPtr = oldVal; } - } ZEND_HASH_FOREACH_END(); - smart_str_0(&implstr); + cptr -= delim->len; + memcpy(cptr, delim->val, delim->len); + } while (--strptr > strings); - if (implstr.s) { - RETURN_NEW_STR(implstr.s); + if (*strptr) { + memcpy(str->val, (*strptr)->val, (*strptr)->len); + zend_string_release(*strptr); } else { - smart_str_free(&implstr); - RETURN_EMPTY_STRING(); + char *oldPtr = cptr; + char oldVal = *cptr; + zend_print_long_to_buf(cptr, ((zend_long *) (strings + numelems))[strptr - strings]); + *oldPtr = oldVal; } + + efree(strings); + RETURN_NEW_STR(str); } /* }}} */ @@ -1725,17 +1735,17 @@ PHP_FUNCTION(pathinfo) } if (opt == PHP_PATHINFO_ALL) { - RETURN_ZVAL(&tmp, 0, 1); + ZVAL_COPY_VALUE(return_value, &tmp); } else { zval *element; if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) { - RETVAL_ZVAL(element, 1, 0); + ZVAL_DEREF(element); + ZVAL_COPY(return_value, element); } else { ZVAL_EMPTY_STRING(return_value); } + zval_ptr_dtor(&tmp); } - - zval_ptr_dtor(&tmp); } /* }}} */ diff --git a/ext/standard/tests/array/bug69723.phpt b/ext/standard/tests/array/bug69723.phpt new file mode 100644 index 0000000000..23b3576a51 --- /dev/null +++ b/ext/standard/tests/array/bug69723.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #69723 (Passing parameters by reference and array_column) +--FILE-- +<?php +function byReference( & $array){ + foreach($array as &$item){ + $item['nanana'] = 'batman'; + $item['superhero'] = 'robin'; + } +} + +$array = [ + [ + 'superhero'=> 'superman', + 'nanana' => 'no nana' + ], + [ + 'superhero'=> 'acuaman', + 'nanana' => 'no nana' + ], + + ]; + +var_dump(array_column($array, 'superhero')); +byReference($array); +var_dump(array_column($array, 'superhero')); +?> +--EXPECT-- +array(2) { + [0]=> + string(8) "superman" + [1]=> + string(7) "acuaman" +} +array(2) { + [0]=> + string(5) "robin" + [1]=> + string(5) "robin" +} diff --git a/ext/standard/tests/file/bug69628.phpt b/ext/standard/tests/file/bug69628.phpt new file mode 100644 index 0000000000..7e18619a3e --- /dev/null +++ b/ext/standard/tests/file/bug69628.phpt @@ -0,0 +1,49 @@ +--TEST-- +Bug #69628: GLOB_BRACE with multiple brackets within the braces fails +--SKIPIF-- +<?php +if (!defined('GLOB_BRACE')) { + die('skip this test requires GLOB_BRACE support'); +} +?> +--FILE-- +<?php + +$file_path = dirname(__FILE__); + +// temp dirname used here +$dirname = "$file_path/bug69628"; + +// temp dir created +mkdir($dirname); + +// temp files created +file_put_contents("$dirname/image.jPg", ''); +file_put_contents("$dirname/image.gIf", ''); +file_put_contents("$dirname/image.png", ''); + +sort_var_dump(glob("$dirname/*.{[jJ][pP][gG],[gG][iI][fF]}", GLOB_BRACE)); + +function sort_var_dump($results) { + sort($results); + var_dump($results); +} + +?> +--CLEAN-- +<?php + +$file_path = dirname(__FILE__); +unlink("$file_path/bug69628/image.jPg"); +unlink("$file_path/bug69628/image.gIf"); +unlink("$file_path/bug69628/image.png"); +rmdir("$file_path/bug69628/"); + +?> +--EXPECTF-- +array(2) { + [0]=> + string(%d) "%s/bug69628/image.gIf" + [1]=> + string(%d) "%s/bug69628/image.jPg" +} diff --git a/ext/standard/tests/file/glob_variation.phpt b/ext/standard/tests/file/glob_variation.phpt index 00fd6ff3ed..9d3b879379 100644 --- a/ext/standard/tests/file/glob_variation.phpt +++ b/ext/standard/tests/file/glob_variation.phpt @@ -5,6 +5,9 @@ Test glob() function: usage variations if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip.. Not valid for Windows'); } +if (!defined('GLOB_BRACE')) { + die('skip this test requires GLOB_BRACE support'); +} ?> --FILE-- <?php diff --git a/ext/standard/tests/file/tempnam_variation3-win32.phpt b/ext/standard/tests/file/tempnam_variation3-win32.phpt index cc8194afa3..df0d88feda 100644 --- a/ext/standard/tests/file/tempnam_variation3-win32.phpt +++ b/ext/standard/tests/file/tempnam_variation3-win32.phpt @@ -102,7 +102,10 @@ OK Failed, not created in the correct directory %s vs %s 0 -- Iteration 6 -- -OK + +Warning: tempnam() expects parameter 2 to be a valid path, string given in %stempnam_variation3-win32.php on line 54 +Failed, not created in the correct directory %s vs %sext\standard\tests\file\tempnamVar3 +0 -- Iteration 7 -- Warning: tempnam() expects parameter 2 to be a valid path, array given in %s\ext\standard\tests\file\tempnam_variation3-win32.php on line %d diff --git a/ext/standard/tests/general_functions/bug69646.phpt b/ext/standard/tests/general_functions/bug69646.phpt new file mode 100644 index 0000000000..b077c67319 --- /dev/null +++ b/ext/standard/tests/general_functions/bug69646.phpt @@ -0,0 +1,47 @@ +--TEST-- +Bug #69646 OS command injection vulnerability in escapeshellarg() +--SKIPIF-- +<?php +if( substr(PHP_OS, 0, 3) != "WIN" ) + die("skip.. Windows only"); +?> +--FILE-- +<?php + +$a = 'a\\'; +$b = 'b -c d\\'; +var_dump( $a, escapeshellarg($a) ); +var_dump( $b, escapeshellarg($b) ); + +$helper_script = <<<SCRIPT +<?php + +print( "--- ARG INFO ---\n" ); +var_dump( \$argv ); + +SCRIPT; + +$script = dirname(__FILE__) . DIRECTORY_SEPARATOR . "arginfo.php"; +file_put_contents($script, $helper_script); + +$cmd = PHP_BINARY . " " . $script . " " . escapeshellarg($a) . " " . escapeshellarg($b); + +system($cmd); + +unlink($script); +?> +--EXPECTF-- +string(2) "a\" +string(5) ""a\\"" +string(7) "b -c d\" +string(10) ""b -c d\\"" +--- ARG INFO --- +array(3) { + [0]=> + string(%d) "%sarginfo.php" + [1]=> + string(2) "a\" + [2]=> + string(7) "b -c d\" +} + diff --git a/ext/standard/tests/general_functions/getrusage_basic.phpt b/ext/standard/tests/general_functions/getrusage_basic.phpt index 0e5b42c420..b2379a7663 100644 --- a/ext/standard/tests/general_functions/getrusage_basic.phpt +++ b/ext/standard/tests/general_functions/getrusage_basic.phpt @@ -1,10 +1,7 @@ --TEST-- Test getrusage() function: basic test --SKIPIF-- -<?php -if( substr(PHP_OS, 0, 3) == "WIN" ) - die("skip.. Do not run on Windows"); -?> +<?php if (!function_exists("getrusage")) print "skip"; ?> --FILE-- <?php /* Prototype : array getrusage ([ int $who ] ) diff --git a/ext/standard/tests/general_functions/getrusage_error.phpt b/ext/standard/tests/general_functions/getrusage_error.phpt index f00627a766..7ac99655b8 100644 --- a/ext/standard/tests/general_functions/getrusage_error.phpt +++ b/ext/standard/tests/general_functions/getrusage_error.phpt @@ -1,10 +1,7 @@ --TEST-- Test getrusage() function : error conditions - incorrect number of args --SKIPIF-- -<?php -if( substr(PHP_OS, 0, 3) == "WIN" ) - die("skip.. Do not run on Windows"); -?> +<?php if (!function_exists("getrusage")) print "skip"; ?> --FILE-- <?php /* Prototype : array getrusage ([ int $who ] ) diff --git a/ext/standard/tests/general_functions/getrusage_variation1.phpt b/ext/standard/tests/general_functions/getrusage_variation1.phpt index ae2b150548..568c9c7383 100644 --- a/ext/standard/tests/general_functions/getrusage_variation1.phpt +++ b/ext/standard/tests/general_functions/getrusage_variation1.phpt @@ -3,8 +3,7 @@ Test getrusage() function : usage variation - diff data types as $who arg --SKIPIF-- <?php if (PHP_INT_SIZE != 8) die("skip this test is for 64-bit only"); -if( substr(PHP_OS, 0, 3) == "WIN" ) - die("skip.. Do not run on Windows"); +if (!function_exists("getrusage")) die("skip"); ?> --FILE-- <?php diff --git a/ext/standard/tests/general_functions/proc_open_pipes.inc b/ext/standard/tests/general_functions/proc_open_pipes.inc new file mode 100644 index 0000000000..0d9894683a --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_pipes.inc @@ -0,0 +1,22 @@ +<?php + +function create_sleep_script() +{ + $fname = dirname(__FILE__) . DIRECTORY_SEPARATOR . "proc_open_pipes_sleep.php"; + + if (!file_exists($fname)) { + file_put_contents($fname, "<?php\nsleep(1);\n"); + } + + return $fname; +} + +function unlink_sleep_script() +{ + $fname = dirname(__FILE__) . DIRECTORY_SEPARATOR . "proc_open_pipes_sleep.php"; + + if (file_exists($fname)) { + unlink($fname); + } +} + diff --git a/ext/standard/tests/general_functions/proc_open_pipes1.phpt b/ext/standard/tests/general_functions/proc_open_pipes1.phpt new file mode 100644 index 0000000000..f6472e2f99 --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_pipes1.phpt @@ -0,0 +1,86 @@ +--TEST-- +proc_open() with > 16 pipes +--FILE-- +<?php + +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +for ($i = 3; $i<= 30; $i++) { + $spec[$i] = array('pipe', 'w'); +} + +$php = getenv("TEST_PHP_EXECUTABLE"); +$callee = create_sleep_script(); +proc_open("$php $callee", $spec, $pipes); + +var_dump(count($spec)); +var_dump($pipes); + +?> +--CLEAN-- +<?php +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +unlink_sleep_script(); + +?> +--EXPECTF-- +int(28) +array(28) { + [3]=> + resource(%d) of type (Unknown) + [4]=> + resource(%d) of type (Unknown) + [5]=> + resource(%d) of type (Unknown) + [6]=> + resource(%d) of type (Unknown) + [7]=> + resource(%d) of type (Unknown) + [8]=> + resource(%d) of type (Unknown) + [9]=> + resource(%d) of type (Unknown) + [10]=> + resource(%d) of type (Unknown) + [11]=> + resource(%d) of type (Unknown) + [12]=> + resource(%d) of type (Unknown) + [13]=> + resource(%d) of type (Unknown) + [14]=> + resource(%d) of type (Unknown) + [15]=> + resource(%d) of type (Unknown) + [16]=> + resource(%d) of type (Unknown) + [17]=> + resource(%d) of type (Unknown) + [18]=> + resource(%d) of type (Unknown) + [19]=> + resource(%d) of type (Unknown) + [20]=> + resource(%d) of type (Unknown) + [21]=> + resource(%d) of type (Unknown) + [22]=> + resource(%d) of type (Unknown) + [23]=> + resource(%d) of type (Unknown) + [24]=> + resource(%d) of type (Unknown) + [25]=> + resource(%d) of type (Unknown) + [26]=> + resource(%d) of type (Unknown) + [27]=> + resource(%d) of type (Unknown) + [28]=> + resource(%d) of type (Unknown) + [29]=> + resource(%d) of type (Unknown) + [30]=> + resource(%d) of type (Unknown) +} diff --git a/ext/standard/tests/general_functions/proc_open_pipes2.phpt b/ext/standard/tests/general_functions/proc_open_pipes2.phpt new file mode 100644 index 0000000000..b07a19e143 --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_pipes2.phpt @@ -0,0 +1,28 @@ +--TEST-- +proc_open() with no pipes +--FILE-- +<?php + +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +$spec = array(); + +$php = getenv("TEST_PHP_EXECUTABLE"); +$callee = create_sleep_script(); +proc_open("$php $callee", $spec, $pipes); + +var_dump(count($spec)); +var_dump($pipes); + +?> +--CLEAN-- +<?php +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +unlink_sleep_script(); + +?> +--EXPECTF-- +int(0) +array(0) { +} diff --git a/ext/standard/tests/general_functions/proc_open_pipes3.phpt b/ext/standard/tests/general_functions/proc_open_pipes3.phpt new file mode 100644 index 0000000000..2ddcd276d7 --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_pipes3.phpt @@ -0,0 +1,64 @@ +--TEST-- +proc_open() with invalid pipes +--FILE-- +<?php + +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +for ($i = 3; $i<= 5; $i++) { + $spec[$i] = array('pipe', 'w'); +} + +$php = getenv("TEST_PHP_EXECUTABLE"); +$callee = create_sleep_script(); + +$spec[$i] = array('pi'); +proc_open("$php $callee", $spec, $pipes); + +$spec[$i] = 1; +proc_open("$php $callee", $spec, $pipes); + +$spec[$i] = array('pipe', "test"); +proc_open("$php $callee", $spec, $pipes); +var_dump($pipes); + +$spec[$i] = array('file', "test", "z"); +proc_open("$php $callee", $spec, $pipes); +var_dump($pipes); + +echo "END\n"; +?> +--CLEAN-- +<?php +include dirname(__FILE__) . "/proc_open_pipes.inc"; + +unlink_sleep_script(); + +?> +--EXPECTF-- +Warning: proc_open(): pi is not a valid descriptor spec/mode in %s on line %d + +Warning: proc_open(): Descriptor item must be either an array or a File-Handle in %s on line %d +array(4) { + [3]=> + resource(%d) of type (Unknown) + [4]=> + resource(%d) of type (Unknown) + [5]=> + resource(%d) of type (Unknown) + [6]=> + resource(%d) of type (Unknown) +} + +Warning: proc_open(test): failed to open stream: %s in %s on line %d +array(4) { + [3]=> + resource(%d) of type (Unknown) + [4]=> + resource(%d) of type (Unknown) + [5]=> + resource(%d) of type (Unknown) + [6]=> + resource(%d) of type (Unknown) +} +END diff --git a/ext/standard/tests/mail/mail_basic6.phpt b/ext/standard/tests/mail/mail_basic6.phpt new file mode 100644 index 0000000000..d0d45b78f3 --- /dev/null +++ b/ext/standard/tests/mail/mail_basic6.phpt @@ -0,0 +1,329 @@ +--TEST-- +Test mail() function : basic functionality +--INI-- +sendmail_path=tee mailBasic.out >/dev/null +mail.add_x_header = Off +--SKIPIF-- +<?php +if(substr(PHP_OS, 0, 3) == "WIN") + die("skip Won't run on Windows"); +?> +--FILE-- +<?php +/* Prototype : int mail(string to, string subject, string message [, string additional_headers [, string additional_parameters]]) + * Description: Send an email message with invalid addtional_headers + * Source code: ext/standard/mail.c + * Alias to functions: + */ + +echo "*** Testing mail() : basic functionality ***\n"; + + +// Valid header +$to = 'user@example.com'; +$subject = 'Test Subject'; +$message = 'A Message'; +$additional_headers = "HEAD1: a\r\nHEAD2: b\r\n"; +$outFile = "mailBasic.out"; +@unlink($outFile); + +echo "-- Valid Header --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo file_get_contents($outFile); +unlink($outFile); + +// Valid header +$additional_headers = "HEAD1: a\nHEAD2: b\n"; +@unlink($outFile); + +echo "-- Valid Header --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Valid header +// \r is accepted as valid. This may be changed to invalid. +$additional_headers = "HEAD1: a\rHEAD2: b\r"; +@unlink($outFile); + +echo "-- Valid Header --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +//=============================================================================== +// Invalid header +$additional_headers = "\nHEAD1: a\nHEAD2: b\n"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "\rHEAD1: a\nHEAD2: b\r"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "\r\nHEAD1: a\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "\r\n\r\nHEAD1: a\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "\n\nHEAD1: a\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "\r\rHEAD1: a\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - preceeding newline--\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\r\n\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\r\n\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\n\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\r\rHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\n\rHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +$additional_headers = "HEAD1: a\n\r\nHEAD2: b\r\n"; +@unlink($outFile); + +echo "-- Invalid Header - multiple newlines in the middle --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +// Invalid, but PHP_FUNCTION(mail) trims newlines +$additional_headers = "HEAD1: a\r\nHEAD2: b\r\n\n"; +@unlink($outFile); + +echo "-- Invalid Header - trailing newlines --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +// Invalid, but PHP_FUNCTION(mail) trims newlines +$additional_headers = "HEAD1: a\r\nHEAD2: b\n\n"; +@unlink($outFile); + +echo "-- Invalid Header - trailing newlines --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +// Invalid, but PHP_FUNCTION(mail) trims newlines +$additional_headers = "HEAD1: a\r\nHEAD2: b\n"; +@unlink($outFile); + +echo "-- Invalid Header - trailing newlines --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +// Invalid header +// Invalid, but PHP_FUNCTION(mail) trims newlines +$additional_headers = "HEAD1: a\r\nHEAD2: b\r"; +@unlink($outFile); + +echo "-- Invalid Header - trailing newlines --\n"; +// Calling mail() with all additional headers +var_dump( mail($to, $subject, $message, $additional_headers) ); +echo @file_get_contents($outFile); +@unlink($outFile); + +?> +===DONE=== +--EXPECTF-- +*** Testing mail() : basic functionality *** +-- Valid Header -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +-- Valid Header -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +-- Valid Header -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a
HEAD2: b + +A Message +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - preceeding newline-- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - multiple newlines in the middle -- + +Warning: mail(): Multiple or malformed newlines found in additional_header in %s/mail_basic6.php on line %d +bool(false) +-- Invalid Header - trailing newlines -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +-- Invalid Header - trailing newlines -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +-- Invalid Header - trailing newlines -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +-- Invalid Header - trailing newlines -- +bool(true) +To: user@example.com +Subject: Test Subject +HEAD1: a +HEAD2: b + +A Message +===DONE=== diff --git a/ext/standard/tests/password/password_verify.phpt b/ext/standard/tests/password/password_verify.phpt index e7ecc7edd3..a196763c13 100644 --- a/ext/standard/tests/password/password_verify.phpt +++ b/ext/standard/tests/password/password_verify.phpt @@ -11,6 +11,13 @@ var_dump(password_verify("foo", '$2a$07$usesomesillystringforsalt$')); var_dump(password_verify('rasmusler', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); var_dump(password_verify('rasmuslerdorf', '$2a$07$usesomesillystringfore2uDLvp1Ii2e./U9C8sBjqp8I90dH6hi')); + +var_dump(password_verify("foo", null)); + +var_dump(password_verify("rasmuslerdorf", "rl.3StKT.4T8M")); + +var_dump(password_verify("foo", "$1")); + echo "OK!"; ?> --EXPECT-- @@ -18,4 +25,7 @@ bool(false) bool(false) bool(false) bool(true) +bool(false) +bool(true) +bool(false) OK! diff --git a/ext/standard/tests/php_version_win_const.phpt b/ext/standard/tests/php_version_win_const.phpt new file mode 100644 index 0000000000..12de79b1ae --- /dev/null +++ b/ext/standard/tests/php_version_win_const.phpt @@ -0,0 +1,18 @@ +--TEST-- +Check that windows version constants are initialized +--SKIPIF-- +<?php +if (substr(PHP_OS, 0, 3) != 'WIN') { + die('skip.. Windows only'); +} +?> +--FILE-- +<?php +var_dump(PHP_WINDOWS_VERSION_MAJOR > 0, PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR); +?> +==DONE== +--EXPECTF-- +bool(true) +int(%d) +int(%d) +==DONE== diff --git a/ext/standard/tests/strings/004.phpt b/ext/standard/tests/strings/004.phpt index b9904c614a..e1414db46c 100644 --- a/ext/standard/tests/strings/004.phpt +++ b/ext/standard/tests/strings/004.phpt @@ -33,52 +33,52 @@ stats('str_shuffle', $a); ?> --EXPECTREGEX-- shuffle -1234: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1243: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1324: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1342: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1423: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1432: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2134: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2143: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2314: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2341: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2413: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2431: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3124: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3142: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3214: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3241: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3412: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3421: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4123: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4132: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4213: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4231: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4312: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4321: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] +1234: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1243: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1324: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1342: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1423: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1432: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2134: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2143: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2314: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2341: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2413: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2431: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3124: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3142: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3214: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3241: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3412: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3421: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4123: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4132: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4213: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4231: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4312: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4321: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] str_shuffle -1234: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1243: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1324: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1342: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1423: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -1432: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2134: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2143: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2314: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2341: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2413: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -2431: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3124: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3142: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3214: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3241: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3412: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -3421: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4123: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4132: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4213: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4231: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4312: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] -4321: 3[0-9][0-9][0-9]: 0.0[3-4][0-9] +1234: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1243: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1324: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1342: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1423: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +1432: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2134: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2143: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2314: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2341: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2413: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +2431: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3124: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3142: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3214: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3241: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3412: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +3421: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4123: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4132: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4213: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4231: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4312: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] +4321: [34][0-9][0-9][0-9]: 0.0[3-4][0-9] diff --git a/ext/standard/type.c b/ext/standard/type.c index f884017f6f..8251a3f155 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -101,7 +101,6 @@ PHP_FUNCTION(settype) } ZVAL_DEREF(var); - SEPARATE_ZVAL_NOREF(var); if (!strcasecmp(type, "integer")) { convert_to_long(var); } else if (!strcasecmp(type, "int")) { @@ -155,7 +154,7 @@ PHP_FUNCTION(intval) ZEND_PARSE_PARAMETERS_END(); #endif - RETVAL_ZVAL(num, 1, 0); + ZVAL_DUP(return_value, num); convert_to_long_base(return_value, (int)base); } /* }}} */ @@ -170,8 +169,7 @@ PHP_FUNCTION(floatval) return; } - RETVAL_ZVAL(num, 1, 0); - convert_to_double(return_value); + RETURN_DOUBLE(zval_get_double(num)); } /* }}} */ diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index 80fd7e940f..d0a079c280 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -273,7 +273,7 @@ PHP_FUNCTION(msg_get_queue) RETURN_FALSE; } } - RETVAL_ZVAL(zend_list_insert(mq, le_sysvmsg), 0, 0); + ZVAL_COPY_VALUE(return_value, zend_list_insert(mq, le_sysvmsg)); } /* }}} */ diff --git a/ext/tokenizer/tests/bug67395.phpt b/ext/tokenizer/tests/bug67395.phpt index c9b7f3012f..8101c81edb 100644 --- a/ext/tokenizer/tests/bug67395.phpt +++ b/ext/tokenizer/tests/bug67395.phpt @@ -1,5 +1,7 @@ --TEST-- Bug 67395: token_name() does not return name for T_POW and T_POW_EQUAL token +--SKIPIF-- +<?php if (!extension_loaded("tokenizer")) print "skip"; ?> --FILE-- <?php diff --git a/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_000.phpt b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_000.phpt new file mode 100644 index 0000000000..b9da91502a --- /dev/null +++ b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_000.phpt @@ -0,0 +1,19 @@ +--TEST-- +Parse errors during token_get_all() with TOKEN_PARSE flag +--SKIPIF-- +<?php if (!extension_loaded("tokenizer")) print "skip"; ?> +--FILE-- +<?php + +try { + token_get_all('<?php invalid code;', TOKEN_PARSE); +} catch (ParseError $e) { + echo $e->getMessage(), PHP_EOL; +} + +echo "Done"; + +?> +--EXPECT-- +syntax error, unexpected 'code' (T_STRING) +Done diff --git a/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_001.phpt b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_001.phpt new file mode 100644 index 0000000000..eb0e300964 --- /dev/null +++ b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_001.phpt @@ -0,0 +1,81 @@ +--TEST-- +Semi reserved words support: member access +--SKIPIF-- +<?php if (!extension_loaded("tokenizer")) print "skip"; ?> +--FILE-- +<?php +$tokens = token_get_all('<?php +X::continue; +X::$continue; +$x->$continue; +X::continue(); +$x->continue(); +X::class; + +class X { + const CONTINUE = 1; + public $x = self::CONTINUE + 1; +} +', TOKEN_PARSE); + +array_walk($tokens, function($tk) { + if(is_array($tk)) { + if(($t = token_name($tk[0])) == 'T_WHITESPACE') return; + echo "L{$tk[2]}: ".$t." {$tk[1]}", PHP_EOL; + } + else echo $tk, PHP_EOL; +}); + +echo "Done"; + +?> +--EXPECTF-- +L1: T_OPEN_TAG <?php + +L2: T_STRING X +L2: T_DOUBLE_COLON :: +L2: T_STRING continue +; +L3: T_STRING X +L3: T_DOUBLE_COLON :: +L3: T_VARIABLE $continue +; +L4: T_VARIABLE $x +L4: T_OBJECT_OPERATOR -> +L4: T_VARIABLE $continue +; +L5: T_STRING X +L5: T_DOUBLE_COLON :: +L5: T_STRING continue +( +) +; +L6: T_VARIABLE $x +L6: T_OBJECT_OPERATOR -> +L6: T_STRING continue +( +) +; +L7: T_STRING X +L7: T_DOUBLE_COLON :: +L7: T_STRING class +; +L9: T_CLASS class +L9: T_STRING X +{ +L10: T_CONST const +L10: T_STRING CONTINUE += +L10: T_LNUMBER 1 +; +L11: T_PUBLIC public +L11: T_VARIABLE $x += +L11: T_STRING self +L11: T_DOUBLE_COLON :: +L11: T_STRING CONTINUE ++ +L11: T_LNUMBER 1 +; +} +Done diff --git a/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_002.phpt b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_002.phpt new file mode 100644 index 0000000000..3dd8e14d84 --- /dev/null +++ b/ext/tokenizer/tests/token_get_all_TOKEN_PARSE_002.phpt @@ -0,0 +1,68 @@ +--TEST-- +Semi reserved words support: class const +--SKIPIF-- +<?php if (!extension_loaded("tokenizer")) print "skip"; ?> +--FILE-- +<?php +$tokens = token_get_all('<?php + class SomeClass { + const CONST = 1; + const CONTINUE = (self::CONST + 1); + const ARRAY = [1, self::CONTINUE => [3, 4], 5]; + } +', TOKEN_PARSE); + +array_walk($tokens, function($tk) { + if(is_array($tk)) { + if(($t = token_name($tk[0])) == 'T_WHITESPACE') return; + echo "L{$tk[2]}: ".$t." {$tk[1]}", PHP_EOL; + } + else echo $tk, PHP_EOL; +}); + +echo "Done"; + +?> +--EXPECTF-- +L1: T_OPEN_TAG <?php + +L2: T_CLASS class +L2: T_STRING SomeClass +{ +L3: T_CONST const +L3: T_STRING CONST += +L3: T_LNUMBER 1 +; +L4: T_CONST const +L4: T_STRING CONTINUE += +( +L4: T_STRING self +L4: T_DOUBLE_COLON :: +L4: T_STRING CONST ++ +L4: T_LNUMBER 1 +) +; +L5: T_CONST const +L5: T_STRING ARRAY += +[ +L5: T_LNUMBER 1 +, +L5: T_STRING self +L5: T_DOUBLE_COLON :: +L5: T_STRING CONTINUE +L5: T_DOUBLE_ARROW => +[ +L5: T_LNUMBER 3 +, +L5: T_LNUMBER 4 +] +, +L5: T_LNUMBER 5 +] +; +} +Done diff --git a/ext/tokenizer/tests/token_get_all_error.phpt b/ext/tokenizer/tests/token_get_all_error.phpt index 29e97c38c4..9ded0a1774 100644 --- a/ext/tokenizer/tests/token_get_all_error.phpt +++ b/ext/tokenizer/tests/token_get_all_error.phpt @@ -19,7 +19,7 @@ var_dump( token_get_all()); echo "-- Testing token_get_all() function with more than expected no. of arguments --\n"; $source = '<?php ?>'; $extra_arg = 10; -var_dump( token_get_all($source, $extra_arg)); +var_dump( token_get_all($source, true, $extra_arg)); echo "Done" ?> @@ -28,10 +28,10 @@ echo "Done" -- Testing token_get_all() function with zero arguments -- -Warning: token_get_all() expects exactly 1 parameter, 0 given in %s on line %d +Warning: token_get_all() expects at least 1 parameter, 0 given in %s on line 11 NULL -- Testing token_get_all() function with more than expected no. of arguments -- -Warning: token_get_all() expects exactly 1 parameter, 2 given in %s on line %d +Warning: token_get_all() expects at most 2 parameters, 3 given in %s on line 17 NULL -Done +Done
\ No newline at end of file diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index c011894441..63405ea6cd 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -37,6 +37,12 @@ #define zendcursor LANG_SCNG(yy_cursor) #define zendlimit LANG_SCNG(yy_limit) +#define TOKEN_PARSE 1 + +void tokenizer_token_get_all_register_constants(INIT_FUNC_ARGS) { + REGISTER_LONG_CONSTANT("TOKEN_PARSE", TOKEN_PARSE, CONST_CS|CONST_PERSISTENT); +} + /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_token_get_all, 0, 0, 1) ZEND_ARG_INFO(0, source) @@ -83,6 +89,7 @@ ZEND_GET_MODULE(tokenizer) PHP_MINIT_FUNCTION(tokenizer) { tokenizer_register_constants(INIT_FUNC_ARGS_PASSTHRU); + tokenizer_token_get_all_register_constants(INIT_FUNC_ARGS_PASSTHRU); return SUCCESS; } /* }}} */ @@ -97,19 +104,33 @@ PHP_MINFO_FUNCTION(tokenizer) } /* }}} */ -static void tokenize(zval *return_value) +static zend_bool tokenize(zval *return_value, zend_string *source) { + zval source_zval; + zend_lex_state original_lex_state; zval token; zval keyword; int token_type; zend_bool destroy; int token_line = 1; - int need_tokens = -1; // for __halt_compiler lexing. -1 = disabled + int need_tokens = -1; /* for __halt_compiler lexing. -1 = disabled */ + + ZVAL_STR_COPY(&source_zval, source); + zend_save_lexical_state(&original_lex_state); + if (zend_prepare_string_for_scanning(&source_zval, "") == FAILURE) { + zend_restore_lexical_state(&original_lex_state); + return 0; + } + + LANG_SCNG(yy_state) = yycINITIAL; array_init(return_value); ZVAL_NULL(&token); while ((token_type = lex_scan(&token))) { + + if(token_type == T_ERROR) break; + destroy = 1; switch (token_type) { case T_CLOSE_TAG: @@ -123,8 +144,6 @@ static void tokenize(zval *return_value) case T_DOC_COMMENT: destroy = 0; break; - case T_ERROR: - return; } if (token_type >= 256) { @@ -147,13 +166,13 @@ static void tokenize(zval *return_value) } ZVAL_NULL(&token); - // after T_HALT_COMPILER collect the next three non-dropped tokens + /* after T_HALT_COMPILER collect the next three non-dropped tokens */ if (need_tokens != -1) { if (token_type != T_WHITESPACE && token_type != T_OPEN_TAG - && token_type != T_COMMENT && token_type != T_DOC_COMMENT - && --need_tokens == 0 + && token_type != T_COMMENT && token_type != T_DOC_COMMENT + && --need_tokens == 0 ) { - // fetch the rest into a T_INLINE_HTML + /* fetch the rest into a T_INLINE_HTML */ if (zendcursor != zendlimit) { array_init(&keyword); add_next_index_long(&keyword, T_INLINE_HTML); @@ -169,34 +188,114 @@ static void tokenize(zval *return_value) token_line = CG(zend_lineno); } + + zval_dtor(&source_zval); + zend_restore_lexical_state(&original_lex_state); + + return 1; } -/* {{{ proto array token_get_all(string source) - */ -PHP_FUNCTION(token_get_all) +zval token_stream; + +void on_event(zend_php_scanner_event event, int token, int line) { - zend_string *source; - zval source_zval; - zend_lex_state original_lex_state; + zval keyword; + HashTable *tokens_ht; + zval *token_zv; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &source) == FAILURE) { - return; + switch(event) { + case ON_TOKEN: + if (token == T_ERROR || token == END) break; + if (token >= 256) { + array_init(&keyword); + add_next_index_long(&keyword, token); + add_next_index_stringl(&keyword, (char *)LANG_SCNG(yy_text), LANG_SCNG(yy_leng)); + add_next_index_long(&keyword, line); + add_next_index_zval(&token_stream, &keyword); + } else { + add_next_index_stringl(&token_stream, (char *)LANG_SCNG(yy_text), LANG_SCNG(yy_leng)); + } + break; + case ON_FEEDBACK: + tokens_ht = Z_ARRVAL(token_stream); + token_zv = zend_hash_index_find(tokens_ht, zend_hash_num_elements(tokens_ht) - 1); + if (token_zv && Z_TYPE_P(token_zv) == IS_ARRAY) { + ZVAL_LONG(zend_hash_index_find(Z_ARRVAL_P(token_zv), 0), token); + } + break; + case ON_STOP: + if (LANG_SCNG(yy_cursor) != LANG_SCNG(yy_limit)) { + array_init(&keyword); + add_next_index_long(&keyword, T_INLINE_HTML); + add_next_index_stringl(&keyword, + (char *)LANG_SCNG(yy_cursor), LANG_SCNG(yy_limit) - LANG_SCNG(yy_cursor)); + add_next_index_long(&keyword, CG(zend_lineno)); + add_next_index_zval(&token_stream, &keyword); + } + break; } +} + +static zend_bool tokenize_parse(zval *return_value, zend_string *source) +{ + zval source_zval; + zend_lex_state original_lex_state; + zend_bool original_in_compilation; + zend_bool success; ZVAL_STR_COPY(&source_zval, source); + + original_in_compilation = CG(in_compilation); + CG(in_compilation) = 1; zend_save_lexical_state(&original_lex_state); - if (zend_prepare_string_for_scanning(&source_zval, "") == FAILURE) { - zend_restore_lexical_state(&original_lex_state); - RETURN_FALSE; - } + if ((success = (zend_prepare_string_for_scanning(&source_zval, "") == SUCCESS))) { + CG(ast) = NULL; + CG(ast_arena) = zend_arena_create(1024 * 32); + LANG_SCNG(yy_state) = yycINITIAL; + LANG_SCNG(on_event) = on_event; - LANG_SCNG(yy_state) = yycINITIAL; + array_init(&token_stream); + if((success = (zendparse() == SUCCESS))) { + ZVAL_COPY_VALUE(return_value, &token_stream); + } else { + zval_ptr_dtor(&token_stream); + } - tokenize(return_value); + zend_ast_destroy(CG(ast)); + zend_arena_destroy(CG(ast_arena)); + } + /* restore compiler and scanner global states */ zend_restore_lexical_state(&original_lex_state); + CG(in_compilation) = original_in_compilation; + zval_dtor(&source_zval); + + return success; +} + +/* }}} */ + +/* {{{ proto array token_get_all(string source) + */ +PHP_FUNCTION(token_get_all) +{ + zend_string *source; + zend_long flags = 0; + zend_bool success; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &source, &flags) == FAILURE) { + return; + } + + if (flags & TOKEN_PARSE) { + success = tokenize_parse(return_value, source); + } else { + success = tokenize(return_value, source); + } + + if (!success) RETURN_FALSE; } /* }}} */ diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 1dd76fcba8..7c17fb983a 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -847,7 +847,7 @@ static zval *php_zip_get_property_ptr_ptr(zval *object, zval *member, int type, zend_object_handlers *std_hnd; if (Z_TYPE_P(member) != IS_STRING) { - ZVAL_DUP(&tmp_member, member); + ZVAL_COPY(&tmp_member, member); convert_to_string(&tmp_member); member = &tmp_member; cache_slot = NULL; @@ -881,7 +881,7 @@ static zval *php_zip_read_property(zval *object, zval *member, int type, void ** zend_object_handlers *std_hnd; if (Z_TYPE_P(member) != IS_STRING) { - ZVAL_DUP(&tmp_member, member); + ZVAL_COPY(&tmp_member, member); convert_to_string(&tmp_member); member = &tmp_member; cache_slot = NULL; @@ -920,7 +920,7 @@ static int php_zip_has_property(zval *object, zval *member, int type, void **cac int retval = 0; if (Z_TYPE_P(member) != IS_STRING) { - ZVAL_DUP(&tmp_member, member); + ZVAL_COPY(&tmp_member, member); convert_to_string(&tmp_member); member = &tmp_member; cache_slot = NULL; diff --git a/ext/zlib/tests/dictionary_usage.phpt b/ext/zlib/tests/dictionary_usage.phpt index 8ffa9e8bcd..f7ef3b3a9c 100644 --- a/ext/zlib/tests/dictionary_usage.phpt +++ b/ext/zlib/tests/dictionary_usage.phpt @@ -9,6 +9,10 @@ $r = deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => $dict]); $a = deflate_add($r, "abdcde", ZLIB_FINISH); var_dump($a); +$r = deflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => implode("\0", $dict)."\0"]); +$dictStr_a = deflate_add($r, "abdcde", ZLIB_FINISH); +var_dump($dictStr_a === $a); + $r = inflate_init(ZLIB_ENCODING_DEFLATE, ["dictionary" => $dict]); var_dump(inflate_add($r, $a, ZLIB_FINISH)); @@ -19,6 +23,7 @@ var_dump(inflate_add($r, $a, ZLIB_FINISH)); ?> --EXPECTF-- string(%d) "%s" +bool(true) string(6) "abdcde" Warning: inflate_add(): dictionary does not match expected dictionary (incorrect adler32 hash) in %s on line %d diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 8169424362..b4b95e85a5 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -759,60 +759,87 @@ static zend_bool zlib_create_dictionary_string(HashTable *options, char **dict, zval *option_buffer; if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("dictionary"))) != NULL) { - HashTable *dictionary; - ZVAL_DEREF(option_buffer); - if (Z_TYPE_P(option_buffer) != IS_ARRAY) { - php_error_docref(NULL, E_WARNING, "dictionary must be of type array, got %s", zend_get_type_by_const(Z_TYPE_P(option_buffer))); - return 0; - } - dictionary = Z_ARR_P(option_buffer); - - if (zend_hash_num_elements(dictionary) > 0) { - char *dictptr; - zval *cur; - zend_string **strings = emalloc(sizeof(zend_string *) * zend_hash_num_elements(dictionary)); - zend_string **end, **ptr = strings - 1; - - ZEND_HASH_FOREACH_VAL(dictionary, cur) { + switch (Z_TYPE_P(option_buffer)) { + case IS_STRING: { + zend_string *str = Z_STR_P(option_buffer); int i; - - *++ptr = zval_get_string(cur); - if (!*ptr || (*ptr)->len == 0) { - if (*ptr) { - efree(*ptr); - } - while (--ptr >= strings) { - efree(ptr); + zend_bool last_null = 1; + + for (i = 0; i < str->len; i++) { + if (str->val[i]) { + last_null = 0; + } else { + if (last_null) { + php_error_docref(NULL, E_WARNING, "dictionary string must not contain empty entries (two consecutive NULL-bytes or one at the very beginning)"); + return 0; + } + last_null = 1; } - efree(strings); - php_error_docref(NULL, E_WARNING, "dictionary entries must be non-empty strings"); - return 0; } - for (i = 0; i < (*ptr)->len; i++) { - if ((*ptr)->val[i] == 0) { - do { - efree(ptr); - } while (--ptr >= strings); - efree(strings); - php_error_docref(NULL, E_WARNING, "dictionary entries must not contain a NULL-byte"); - return 0; - } + if (!last_null) { + php_error_docref(NULL, E_WARNING, "dictionary string must be NULL-byte terminated (each dictionary entry has to be NULL-terminated)"); + } + + *dict = emalloc(str->len); + memcpy(*dict, str->val, str->len); + *dictlen = str->len; + } break; + + case IS_ARRAY: { + HashTable *dictionary = Z_ARR_P(option_buffer); + + if (zend_hash_num_elements(dictionary) > 0) { + char *dictptr; + zval *cur; + zend_string **strings = emalloc(sizeof(zend_string *) * zend_hash_num_elements(dictionary)); + zend_string **end, **ptr = strings - 1; + + ZEND_HASH_FOREACH_VAL(dictionary, cur) { + int i; + + *++ptr = zval_get_string(cur); + if (!*ptr || (*ptr)->len == 0) { + if (*ptr) { + efree(*ptr); + } + while (--ptr >= strings) { + efree(ptr); + } + efree(strings); + php_error_docref(NULL, E_WARNING, "dictionary entries must be non-empty strings"); + return 0; + } + for (i = 0; i < (*ptr)->len; i++) { + if ((*ptr)->val[i] == 0) { + do { + efree(ptr); + } while (--ptr >= strings); + efree(strings); + php_error_docref(NULL, E_WARNING, "dictionary entries must not contain a NULL-byte"); + return 0; + } + } + + *dictlen += (*ptr)->len + 1; + } ZEND_HASH_FOREACH_END(); + + dictptr = *dict = emalloc(*dictlen); + ptr = strings; + end = strings + zend_hash_num_elements(dictionary); + do { + memcpy(dictptr, (*ptr)->val, (*ptr)->len); + dictptr += (*ptr)->len; + *dictptr++ = 0; + zend_string_release(*ptr); + } while (++ptr != end); + efree(strings); } + } break; - *dictlen += (*ptr)->len + 1; - } ZEND_HASH_FOREACH_END(); - - dictptr = *dict = emalloc(*dictlen); - ptr = strings; - end = strings + zend_hash_num_elements(dictionary); - do { - memcpy(dictptr, *ptr, (*ptr)->len); - dictptr += (*ptr)->len; - *dictptr++ = 0; - zend_string_release(*ptr); - } while (++ptr != end); - efree(strings); + default: + php_error_docref(NULL, E_WARNING, "dictionary must be of type zero-terminated string or array, got %s", zend_get_type_by_const(Z_TYPE_P(option_buffer))); + return 0; } } @@ -1,6 +1,15 @@ #! /bin/sh -STD='make -f Makefile.frag RE2C="re2c" RE2C_FLAGS="-i" YACC="bison -y -l" srcdir=Zend builddir=Zend top_srcdir=.' +if [ -z $YACC ]; then + YACC="bison" +fi +YACC="$YACC -y -l" + +if [ -z $RE2C ]; then + RE2C="re2c" +fi + +STD="make -f Makefile.frag RE2C='$RE2C' RE2C_FLAGS='-i' YACC='$YACC' srcdir=Zend builddir=Zend top_srcdir=." (eval "$STD Zend/zend_language_parser.c Zend/zend_language_scanner.c Zend/zend_ini_parser.c Zend/zend_ini_scanner.c") diff --git a/sapi/cgi/fastcgi.c b/main/fastcgi.c index 7d6fd649ea..03dcc57b3c 100644 --- a/sapi/cgi/fastcgi.c +++ b/main/fastcgi.c @@ -20,7 +20,6 @@ #include "php.h" #include "php_network.h" -#include "fastcgi.h" #include <string.h> #include <stdlib.h> @@ -36,15 +35,15 @@ #include <windows.h> - typedef unsigned int in_addr_t; +typedef unsigned int in_addr_t; - struct sockaddr_un { - short sun_family; - char sun_path[MAXPATHLEN]; - }; +struct sockaddr_un { + short sun_family; + char sun_path[MAXPATHLEN]; +}; - static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; - static int is_impersonate = 0; +static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; +static int is_impersonate = 0; #define FCGI_LOCK(fd) \ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ @@ -131,10 +130,18 @@ #endif +#include "fastcgi.h" + +/* maybe it's better to use weak name instead */ +#ifndef HAVE_ATTRIBUTE_WEAK +static fcgi_logger fcgi_log; +#endif + typedef union _sa_t { struct sockaddr sa; struct sockaddr_un sa_unix; struct sockaddr_in sa_inet; + struct sockaddr_in6 sa_inet6; } sa_t; static HashTable fcgi_mgmt_vars; @@ -142,44 +149,10 @@ static HashTable fcgi_mgmt_vars; static int is_initialized = 0; static int is_fastcgi = 0; static int in_shutdown = 0; -static in_addr_t *allowed_clients = NULL; +static sa_t *allowed_clients = NULL; +static sa_t client_sa; /* hash table */ - -#define FCGI_HASH_TABLE_SIZE 128 -#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1) -#define FCGI_HASH_SEG_SIZE 4096 - -typedef struct _fcgi_hash_bucket { - unsigned int hash_value; - unsigned int var_len; - char *var; - unsigned int val_len; - char *val; - struct _fcgi_hash_bucket *next; - struct _fcgi_hash_bucket *list_next; -} fcgi_hash_bucket; - -typedef struct _fcgi_hash_buckets { - unsigned int idx; - struct _fcgi_hash_buckets *next; - struct _fcgi_hash_bucket data[FCGI_HASH_TABLE_SIZE]; -} fcgi_hash_buckets; - -typedef struct _fcgi_data_seg { - char *pos; - char *end; - struct _fcgi_data_seg *next; - char data[1]; -} fcgi_data_seg; - -typedef struct _fcgi_hash { - fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE]; - fcgi_hash_bucket *list; - fcgi_hash_buckets *buckets; - fcgi_data_seg *data; -} fcgi_hash; - static void fcgi_hash_init(fcgi_hash *h) { memset(h->hash_table, 0, sizeof(h->hash_table)); @@ -339,29 +312,6 @@ static void fcgi_hash_apply(fcgi_hash *h, fcgi_apply_func func, void *arg) } } -struct _fcgi_request { - int listen_socket; - int tcp; - int fd; - int id; - int keep; -#ifdef TCP_NODELAY - int nodelay; -#endif - int closed; - - int in_len; - int in_pad; - - fcgi_header *out_hdr; - unsigned char *out_pos; - unsigned char out_buf[1024*8]; - unsigned char reserved[sizeof(fcgi_end_request_rec)]; - - int has_env; - fcgi_hash env; -}; - #ifdef _WIN32 static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) @@ -397,6 +347,11 @@ static void fcgi_setup_signals(void) } #endif +void fcgi_set_in_shutdown(int new_value) +{ + in_shutdown = new_value; +} + int fcgi_in_shutdown(void) { return in_shutdown; @@ -407,6 +362,20 @@ void fcgi_terminate(void) in_shutdown = 1; } +#ifndef HAVE_ATTRIBUTE_WEAK +void fcgi_set_logger(fcgi_logger lg) { + fcgi_log = lg; +} +#else +void __attribute__((weak)) fcgi_log(int type, const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} +#endif + int fcgi_init(void) { if (!is_initialized) { @@ -626,10 +595,10 @@ int fcgi_listen(const char *path, int backlog) hep = gethostbyname(host); } if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) { - fprintf(stderr, "Cannot resolve host name '%s'!\n", host); + fcgi_log(FCGI_ERROR, "Cannot resolve host name '%s'!\n", host); return -1; } else if (hep->h_addr_list[1]) { - fprintf(stderr, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host); + fcgi_log(FCGI_ERROR, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host); return -1; } sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr; @@ -666,7 +635,7 @@ int fcgi_listen(const char *path, int backlog) int path_len = strlen(path); if (path_len >= sizeof(sa.sa_unix.sun_path)) { - fprintf(stderr, "Listening socket's path name is too long.\n"); + fcgi_log(FCGI_ERROR, "Listening socket's path name is too long.\n"); return -1; } @@ -689,7 +658,7 @@ int fcgi_listen(const char *path, int backlog) bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 || listen(listen_socket, backlog) < 0) { - fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno)); + fcgi_log(FCGI_ERROR, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno)); return -1; } @@ -708,7 +677,7 @@ int fcgi_listen(const char *path, int backlog) if (*cur == ',') n++; cur++; } - allowed_clients = malloc(sizeof(in_addr_t) * (n+2)); + allowed_clients = malloc(sizeof(sa_t) * (n+2)); n = 0; cur = ip; while (cur) { @@ -717,15 +686,25 @@ int fcgi_listen(const char *path, int backlog) *end = 0; end++; } - allowed_clients[n] = inet_addr(cur); - if (allowed_clients[n] == INADDR_NONE) { - fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur); + if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET; + n++; +#ifdef HAVE_IPV6 + } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET6; + n++; +#endif + } else { + fcgi_log(FCGI_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); } - n++; cur = end; } - allowed_clients[n] = INADDR_NONE; + allowed_clients[n].sa.sa_family = 0; free(ip); + if (!n) { + fcgi_log(FCGI_ERROR, "There are no allowed addresses"); + /* don't clear allowed_clients as it will create an "open for all" security issue */ + } } } @@ -744,36 +723,92 @@ int fcgi_listen(const char *path, int backlog) return listen_socket; } -fcgi_request *fcgi_init_request(int listen_socket) +void fcgi_set_allowed_clients(char *ip) +{ + char *cur, *end; + int n; + + if (ip) { + ip = strdup(ip); + cur = ip; + n = 0; + while (*cur) { + if (*cur == ',') n++; + cur++; + } + if (allowed_clients) free(allowed_clients); + allowed_clients = malloc(sizeof(sa_t) * (n+2)); + n = 0; + cur = ip; + while (cur) { + end = strchr(cur, ','); + if (end) { + *end = 0; + end++; + } + if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET; + n++; +#ifdef HAVE_IPV6 + } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET6; + n++; +#endif + } else { + fcgi_log(FCGI_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); + } + cur = end; + } + allowed_clients[n].sa.sa_family = 0; + free(ip); + if (!n) { + fcgi_log(FCGI_ERROR, "There are no allowed addresses"); + /* don't clear allowed_clients as it will create an "open for all" security issue */ + } + } +} + +static void fcgi_hook_dummy() { + return; +} + +fcgi_request *fcgi_init_request(fcgi_request *req, int listen_socket) { - fcgi_request *req = (fcgi_request*)calloc(1, sizeof(fcgi_request)); + memset(req, 0, sizeof(fcgi_request)); req->listen_socket = listen_socket; req->fd = -1; req->id = -1; + /* req->in_len = 0; req->in_pad = 0; req->out_hdr = NULL; + +#ifdef TCP_NODELAY + req->nodelay = 0; +#endif + + req->env = NULL; + req->has_env = 0; + + */ req->out_pos = req->out_buf; + req->hook.on_accept = fcgi_hook_dummy; + req->hook.on_read = fcgi_hook_dummy; + req->hook.on_close = fcgi_hook_dummy; #ifdef _WIN32 req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); #endif -#ifdef TCP_NODELAY - req->nodelay = 0; -#endif - fcgi_hash_init(&req->env); return req; } -void fcgi_destroy_request(fcgi_request *req) -{ +void fcgi_destroy_request(fcgi_request *req) { fcgi_hash_destroy(&req->env); - free(req); } static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) @@ -1052,7 +1087,7 @@ static int fcgi_read_request(fcgi_request *req) } len = (int)(p - buf - sizeof(fcgi_header)); len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len); - if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) { + if (safe_write(req, buf, sizeof(fcgi_header) + len) != (ssize_t)sizeof(fcgi_header)+len) { req->keep = 0; return 0; } @@ -1117,7 +1152,7 @@ int fcgi_read(fcgi_request *req, char *str, int len) return n; } -static inline void fcgi_close(fcgi_request *req, int force, int destroy) +void fcgi_close(fcgi_request *req, int force, int destroy) { if (destroy && req->has_env) { fcgi_hash_clean(&req->env); @@ -1163,7 +1198,52 @@ static inline void fcgi_close(fcgi_request *req, int force, int destroy) req->nodelay = 0; #endif req->fd = -1; + + req->hook.on_close(); + } +} + +int fcgi_is_closed(fcgi_request *req) +{ + return (req->fd < 0); +} + +static int fcgi_is_allowed() { + int i; + + if (client_sa.sa.sa_family == AF_UNIX) { + return 1; + } + if (!allowed_clients) { + return 1; + } + if (client_sa.sa.sa_family == AF_INET) { + for (i = 0; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET + && !memcmp(&client_sa.sa_inet.sin_addr, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } + } } +#ifdef HAVE_IPV6 + if (client_sa.sa.sa_family == AF_INET6) { + for (i = 0; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET6 + && !memcmp(&client_sa.sa_inet6.sin6_addr, &allowed_clients[i].sa_inet6.sin6_addr, 12)) { + return 1; + } +#ifdef IN6_IS_ADDR_V4MAPPED + if (allowed_clients[i].sa.sa_family == AF_INET + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr) + && !memcmp(((char *)&client_sa.sa_inet6.sin6_addr)+12, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } +#endif + } + } +#endif + + return 0; } int fcgi_accept_request(fcgi_request *req) @@ -1209,37 +1289,18 @@ int fcgi_accept_request(fcgi_request *req) sa_t sa; socklen_t len = sizeof(sa); + req->hook.on_accept(); + FCGI_LOCK(req->listen_socket); req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); FCGI_UNLOCK(req->listen_socket); - if (req->fd >= 0) { - if (((struct sockaddr *)&sa)->sa_family == AF_INET) { -#ifndef _WIN32 - req->tcp = 1; -#endif - if (allowed_clients) { - int n = 0; - int allowed = 0; - - while (allowed_clients[n] != INADDR_NONE) { - if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) { - allowed = 1; - break; - } - n++; - } - if (!allowed) { - fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr)); - closesocket(req->fd); - req->fd = -1; - continue; - } - } -#ifndef _WIN32 - } else { - req->tcp = 0; -#endif - } + + client_sa = sa; + if (req->fd >= 0 && !fcgi_is_allowed()) { + fcgi_log(FCGI_ERROR, "Connection disallowed: IP address '%s' has been dropped.", fcgi_get_last_client_ip()); + closesocket(req->fd); + req->fd = -1; + continue; } } @@ -1259,6 +1320,8 @@ int fcgi_accept_request(fcgi_request *req) struct pollfd fds; int ret; + req->hook.on_read(); + fds.fd = req->fd; fds.events = POLLIN; fds.revents = 0; @@ -1271,6 +1334,8 @@ int fcgi_accept_request(fcgi_request *req) } fcgi_close(req, 1, 0); #else + req->hook.on_read(); + if (req->fd < FD_SETSIZE) { struct timeval tv = {5,0}; fd_set set; @@ -1287,7 +1352,7 @@ int fcgi_accept_request(fcgi_request *req) } fcgi_close(req, 1, 0); } else { - fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded."); + fcgi_log(FCGI_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded."); fcgi_close(req, 1, 0); } #endif @@ -1554,6 +1619,31 @@ void fcgi_free_mgmt_var_cb(zval *zv) pefree(Z_STR_P(zv), 1); } +const char *fcgi_get_last_client_ip() +{ + static char str[INET6_ADDRSTRLEN]; + + /* Ipv4 */ + if (client_sa.sa.sa_family == AF_INET) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet.sin_addr, str, INET6_ADDRSTRLEN); + } +#ifdef HAVE_IPV6 +#ifdef IN6_IS_ADDR_V4MAPPED + /* Ipv4-Mapped-Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr)) { + return inet_ntop(AF_INET, ((char *)&client_sa.sa_inet6.sin6_addr)+12, str, INET6_ADDRSTRLEN); + } +#endif + /* Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet6.sin6_addr, str, INET6_ADDRSTRLEN); + } +#endif + /* Unix socket */ + return NULL; +} + /* * Local variables: * tab-width: 4 diff --git a/sapi/cgi/fastcgi.h b/main/fastcgi.h index fa98730220..7e56f0ef89 100644 --- a/sapi/cgi/fastcgi.h +++ b/main/fastcgi.h @@ -49,6 +49,14 @@ typedef enum _fcgi_role { FCGI_FILTER = 3 } fcgi_role; +enum { + FCGI_DEBUG = 1, + FCGI_NOTICE = 2, + FCGI_WARNING = 3, + FCGI_ERROR = 4, + FCGI_ALERT = 5, +}; + typedef enum _fcgi_request_type { FCGI_BEGIN_REQUEST = 1, /* [in] */ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */ @@ -110,18 +118,94 @@ typedef struct _fcgi_end_request_rec { typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg); -typedef struct _fcgi_request fcgi_request; +#define FCGI_HASH_TABLE_SIZE 128 +#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1) +#define FCGI_HASH_SEG_SIZE 4096 + +typedef struct _fcgi_hash_bucket { + unsigned int hash_value; + unsigned int var_len; + char *var; + unsigned int val_len; + char *val; + struct _fcgi_hash_bucket *next; + struct _fcgi_hash_bucket *list_next; +} fcgi_hash_bucket; + +typedef struct _fcgi_hash_buckets { + unsigned int idx; + struct _fcgi_hash_buckets *next; + struct _fcgi_hash_bucket data[FCGI_HASH_TABLE_SIZE]; +} fcgi_hash_buckets; + +typedef struct _fcgi_data_seg { + char *pos; + char *end; + struct _fcgi_data_seg *next; + char data[1]; +} fcgi_data_seg; + +typedef struct _fcgi_hash { + fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE]; + fcgi_hash_bucket *list; + fcgi_hash_buckets *buckets; + fcgi_data_seg *data; +} fcgi_hash; + +typedef struct _fcgi_request fcgi_request; +typedef struct _fcgi_req_hook fcgi_req_hook; + +struct _fcgi_req_hook { + void(*on_accept)(); + void(*on_read)(); + void(*on_close)(); +}; + +struct _fcgi_request { + int listen_socket; + int tcp; + int fd; + int id; + int keep; +#ifdef TCP_NODELAY + int nodelay; +#endif + int closed; + int in_len; + int in_pad; + + fcgi_header *out_hdr; + + unsigned char *out_pos; + unsigned char out_buf[1024*8]; + unsigned char reserved[sizeof(fcgi_end_request_rec)]; + + fcgi_req_hook hook; + + int has_env; + fcgi_hash env; +}; int fcgi_init(void); void fcgi_shutdown(void); int fcgi_is_fastcgi(void); +int fcgi_is_closed(fcgi_request *req); +void fcgi_close(fcgi_request *req, int force, int destroy); int fcgi_in_shutdown(void); void fcgi_terminate(void); int fcgi_listen(const char *path, int backlog); -fcgi_request* fcgi_init_request(int listen_socket); +fcgi_request* fcgi_init_request(fcgi_request *request, int listen_socket); void fcgi_destroy_request(fcgi_request *req); +void fcgi_set_allowed_clients(char *ip); int fcgi_accept_request(fcgi_request *req); int fcgi_finish_request(fcgi_request *req, int force_close); +const char *fcgi_get_last_client_ip(); +void fcgi_set_in_shutdown(int new_value); + +#ifndef HAVE_ATTRIBUTE_WEAK +typedef void (*fcgi_logger)(int type, const char *fmt, ...); +void fcgi_set_logger(fcgi_logger lg); +#endif char* fcgi_getenv(fcgi_request *req, const char* var, int var_len); char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val); diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index 85127c3379..a4c6e4a6db 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -53,10 +53,6 @@ #include <sys/socket.h> #endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - #ifdef PHP_WIN32 #include <winsock2.h> #elif defined(NETWARE) && defined(USE_WINSOCK) diff --git a/main/main.c b/main/main.c index ebaaa2a8e4..2d58b9c99c 100644 --- a/main/main.c +++ b/main/main.c @@ -114,10 +114,6 @@ #endif /* }}} */ -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - PHPAPI int (*php_register_internal_extensions_func)(void) = php_register_internal_extensions; #ifndef ZTS diff --git a/main/php_ini.c b/main/php_ini.c index 80d34848f8..99919ca56e 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -37,10 +37,6 @@ #include <dirent.h> #endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - #ifdef PHP_WIN32 #define TRANSLATE_SLASHES_LOWER(path) \ { \ diff --git a/main/php_variables.c b/main/php_variables.c index 6b8a1726d5..d800a2d3c4 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -245,7 +245,6 @@ static zend_bool add_post_var(zval *arr, post_var_data_t *var, zend_bool eof) { char *ksep, *vsep, *val; size_t klen, vlen; - /* FIXME: string-size_t */ size_t new_vlen; if (var->ptr >= var->end) { @@ -312,6 +311,11 @@ static inline int add_post_vars(zval *arr, post_var_data_t *vars, zend_bool eof) return SUCCESS; } +#ifdef PHP_WIN32 +#define SAPI_POST_HANDLER_BUFSIZ 16384 +#else +# define SAPI_POST_HANDLER_BUFSIZ BUFSIZ +#endif SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) { zval *arr = (zval *) arg; @@ -322,8 +326,8 @@ SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) memset(&post_data, 0, sizeof(post_data)); while (!php_stream_eof(s)) { - char buf[BUFSIZ] = {0}; - size_t len = php_stream_read(s, buf, BUFSIZ); + char buf[SAPI_POST_HANDLER_BUFSIZ] = {0}; + size_t len = php_stream_read(s, buf, SAPI_POST_HANDLER_BUFSIZ); if (len && len != (size_t) -1) { smart_str_appendl(&post_data.str, buf, len); @@ -334,7 +338,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) } } - if (len != BUFSIZ){ + if (len != SAPI_POST_HANDLER_BUFSIZ){ break; } } @@ -345,6 +349,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) } } } +#undef SAPI_POST_HANDLER_BUFSIZ SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) { diff --git a/main/streams/php_streams_int.h b/main/streams/php_streams_int.h index c47c666178..0188202c91 100644 --- a/main/streams/php_streams_int.h +++ b/main/streams/php_streams_int.h @@ -55,10 +55,6 @@ # define EWOULDBLOCK WSAEWOULDBLOCK #endif -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif - /* This functions transforms the first char to 'w' if it's not 'r', 'a' or 'w' * and strips any subsequent chars except '+' and 'b'. * Use this to sanitize stream->mode if you call e.g. fdopen, fopencookie or diff --git a/main/win95nt.h b/main/win95nt.h index adf9f61e34..12356eeccd 100644 --- a/main/win95nt.h +++ b/main/win95nt.h @@ -40,9 +40,6 @@ typedef char * caddr_t; #define S_IFIFO _IFIFO #define S_IFBLK _IFBLK #define S_IFLNK _IFLNK -#ifndef S_ISREG -#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif #define chdir(path) _chdir(path) #define mkdir(a, b) _mkdir(a) #define rmdir(a) _rmdir(a) diff --git a/php.ini-development b/php.ini-development index 4aaf2f8164..902d4b6b88 100644 --- a/php.ini-development +++ b/php.ini-development @@ -876,7 +876,6 @@ default_socket_timeout = 60 ;extension=php_ldap.dll ;extension=php_mbstring.dll ;extension=php_exif.dll ; Must be after mbstring as it depends on it -;extension=php_mysql.dll ;extension=php_mysqli.dll ;extension=php_oci8_12c.dll ; Use with Oracle Database 12c Instant Client ;extension=php_openssl.dll @@ -1110,64 +1109,6 @@ ibase.dateformat = "%Y-%m-%d" ; Default time format. ibase.timeformat = "%H:%M:%S" -[MySQL] -; Allow accessing, from PHP's perspective, local files with LOAD DATA statements -; http://php.net/mysql.allow_local_infile -mysql.allow_local_infile = On - -; Allow or prevent persistent links. -; http://php.net/mysql.allow-persistent -mysql.allow_persistent = On - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/mysql.cache_size -mysql.cache_size = 2000 - -; Maximum number of persistent links. -1 means no limit. -; http://php.net/mysql.max-persistent -mysql.max_persistent = -1 - -; Maximum number of links (persistent + non-persistent). -1 means no limit. -; http://php.net/mysql.max-links -mysql.max_links = -1 - -; Default port number for mysql_connect(). If unset, mysql_connect() will use -; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the -; compile-time value defined MYSQL_PORT (in that order). Win32 will only look -; at MYSQL_PORT. -; http://php.net/mysql.default-port -mysql.default_port = - -; Default socket name for local MySQL connects. If empty, uses the built-in -; MySQL defaults. -; http://php.net/mysql.default-socket -mysql.default_socket = - -; Default host for mysql_connect() (doesn't apply in safe mode). -; http://php.net/mysql.default-host -mysql.default_host = - -; Default user for mysql_connect() (doesn't apply in safe mode). -; http://php.net/mysql.default-user -mysql.default_user = - -; Default password for mysql_connect() (doesn't apply in safe mode). -; Note that this is generally a *bad* idea to store passwords in this file. -; *Any* user with PHP access can run 'echo get_cfg_var("mysql.default_password") -; and reveal this password! And of course, any users with read access to this -; file will be able to reveal the password as well. -; http://php.net/mysql.default-password -mysql.default_password = - -; Maximum time (in seconds) for connect timeout. -1 means no limit -; http://php.net/mysql.connect-timeout -mysql.connect_timeout = 60 - -; Trace mode. When trace_mode is active (=On), warnings for table/index scans and -; SQL-Errors will be displayed. -; http://php.net/mysql.trace-mode -mysql.trace_mode = Off - [MySQLi] ; Maximum number of persistent links. -1 means no limit. diff --git a/php.ini-production b/php.ini-production index 9042d08a9d..539a89aa20 100644 --- a/php.ini-production +++ b/php.ini-production @@ -876,7 +876,6 @@ default_socket_timeout = 60 ;extension=php_ldap.dll ;extension=php_mbstring.dll ;extension=php_exif.dll ; Must be after mbstring as it depends on it -;extension=php_mysql.dll ;extension=php_mysqli.dll ;extension=php_oci8_12c.dll ; Use with Oracle Database 12c Instant Client ;extension=php_openssl.dll @@ -1110,64 +1109,6 @@ ibase.dateformat = "%Y-%m-%d" ; Default time format. ibase.timeformat = "%H:%M:%S" -[MySQL] -; Allow accessing, from PHP's perspective, local files with LOAD DATA statements -; http://php.net/mysql.allow_local_infile -mysql.allow_local_infile = On - -; Allow or prevent persistent links. -; http://php.net/mysql.allow-persistent -mysql.allow_persistent = On - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/mysql.cache_size -mysql.cache_size = 2000 - -; Maximum number of persistent links. -1 means no limit. -; http://php.net/mysql.max-persistent -mysql.max_persistent = -1 - -; Maximum number of links (persistent + non-persistent). -1 means no limit. -; http://php.net/mysql.max-links -mysql.max_links = -1 - -; Default port number for mysql_connect(). If unset, mysql_connect() will use -; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the -; compile-time value defined MYSQL_PORT (in that order). Win32 will only look -; at MYSQL_PORT. -; http://php.net/mysql.default-port -mysql.default_port = - -; Default socket name for local MySQL connects. If empty, uses the built-in -; MySQL defaults. -; http://php.net/mysql.default-socket -mysql.default_socket = - -; Default host for mysql_connect() (doesn't apply in safe mode). -; http://php.net/mysql.default-host -mysql.default_host = - -; Default user for mysql_connect() (doesn't apply in safe mode). -; http://php.net/mysql.default-user -mysql.default_user = - -; Default password for mysql_connect() (doesn't apply in safe mode). -; Note that this is generally a *bad* idea to store passwords in this file. -; *Any* user with PHP access can run 'echo get_cfg_var("mysql.default_password") -; and reveal this password! And of course, any users with read access to this -; file will be able to reveal the password as well. -; http://php.net/mysql.default-password -mysql.default_password = - -; Maximum time (in seconds) for connect timeout. -1 means no limit -; http://php.net/mysql.connect-timeout -mysql.connect_timeout = 60 - -; Trace mode. When trace_mode is active (=On), warnings for table/index scans and -; SQL-Errors will be displayed. -; http://php.net/mysql.trace-mode -mysql.trace_mode = Off - [MySQLi] ; Maximum number of persistent links. -1 means no limit. diff --git a/run-tests.php b/run-tests.php index 897500a58b..70d9cc3f5d 100755 --- a/run-tests.php +++ b/run-tests.php @@ -1662,9 +1662,15 @@ TEST $file } $env['REDIRECT_STATUS'] = '1'; - $env['QUERY_STRING'] = $query_string; - $env['PATH_TRANSLATED'] = $test_file; - $env['SCRIPT_FILENAME'] = $test_file; + if (empty($env['QUERY_STRING'])) { + $env['QUERY_STRING'] = $query_string; + } + if (empty($env['PATH_TRANSLATED'])) { + $env['PATH_TRANSLATED'] = $test_file; + } + if (empty($env['SCRIPT_FILENAME'])) { + $env['SCRIPT_FILENAME'] = $test_file; + } if (array_key_exists('COOKIE', $section_text)) { $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); @@ -1745,12 +1751,17 @@ TEST $file } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { $post = trim($section_text['POST']); - save_text($tmp_post, $post); $content_length = strlen($post); + save_text($tmp_post, $post); $env['REQUEST_METHOD'] = 'POST'; - $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $env['CONTENT_LENGTH'] = $content_length; + if (empty($env['CONTENT_TYPE'])) { + $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + } + + if (empty($env['CONTENT_LENGTH'])) { + $env['CONTENT_LENGTH'] = $content_length; + } $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; diff --git a/sapi/cgi/Makefile.frag b/sapi/cgi/Makefile.frag index d3d7cb3553..75c72bf8b7 100644 --- a/sapi/cgi/Makefile.frag +++ b/sapi/cgi/Makefile.frag @@ -1,6 +1,6 @@ cgi: $(SAPI_CGI_PATH) -$(SAPI_CGI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_CGI_OBJS) +$(SAPI_CGI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FASTCGI_OBJS) $(PHP_CGI_OBJS) $(BUILD_CGI) install-cgi: $(SAPI_CGI_PATH) diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index b479d4df4e..c14aeb405a 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -219,6 +219,16 @@ static php_cgi_globals_struct php_cgi_globals; #define TRANSLATE_SLASHES(path) #endif +#ifndef HAVE_ATTRIBUTE_WEAK +static void fcgi_log(int type, const char *format, ...) { + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} +#endif + static int print_module_info(zval *element) { zend_module_entry *module = Z_PTR_P(element); @@ -1026,12 +1036,12 @@ static int is_valid_path(const char *path) /* }}} */ #define CGI_GETENV(name) \ - ((request) ? \ + ((request->has_env) ? \ FCGI_GETENV(request, name) : \ getenv(name)) #define CGI_PUTENV(name, value) \ - ((request) ? \ + ((request->has_env) ? \ FCGI_PUTENV(request, name, value) : \ _sapi_cgi_putenv(name, sizeof(name)-1, value)) @@ -1724,7 +1734,7 @@ int main(int argc, char *argv[]) int fastcgi; char *bindpath = NULL; int fcgi_fd = 0; - fcgi_request *request = NULL; + fcgi_request request = {0}; int warmup_repeats = 0; int repeats = 1; int benchmark = 0; @@ -1928,6 +1938,10 @@ consult the installation file that came with this distribution, or visit \n\ } } +#ifndef HAVE_ATTRIBUTE_WEAK + fcgi_set_logger(fcgi_log); +#endif + if (bindpath) { int backlog = 128; if (getenv("PHP_FCGI_BACKLOG")) { @@ -1958,109 +1972,109 @@ consult the installation file that came with this distribution, or visit \n\ php_import_environment_variables = cgi_php_import_environment_variables; /* library is already initialized, now init our request */ - request = fcgi_init_request(fcgi_fd); + fcgi_init_request(&request, fcgi_fd); #ifndef PHP_WIN32 - /* Pre-fork, if required */ - if (getenv("PHP_FCGI_CHILDREN")) { - char * children_str = getenv("PHP_FCGI_CHILDREN"); - children = atoi(children_str); - if (children < 0) { - fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n"); - return FAILURE; + /* Pre-fork, if required */ + if (getenv("PHP_FCGI_CHILDREN")) { + char * children_str = getenv("PHP_FCGI_CHILDREN"); + children = atoi(children_str); + if (children < 0) { + fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n"); + return FAILURE; + } + fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str)); + /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */ + fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str)); + } else { + fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1); + fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1); } - fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str)); - /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */ - fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str)); - } else { - fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1); - fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1); - } - if (children) { - int running = 0; - pid_t pid; + if (children) { + int running = 0; + pid_t pid; - /* Create a process group for ourself & children */ - setsid(); - pgroup = getpgrp(); + /* Create a process group for ourself & children */ + setsid(); + pgroup = getpgrp(); #ifdef DEBUG_FASTCGI - fprintf(stderr, "Process group %d\n", pgroup); + fprintf(stderr, "Process group %d\n", pgroup); #endif - /* Set up handler to kill children upon exit */ - act.sa_flags = 0; - act.sa_handler = fastcgi_cleanup; - if (sigaction(SIGTERM, &act, &old_term) || - sigaction(SIGINT, &act, &old_int) || - sigaction(SIGQUIT, &act, &old_quit) - ) { - perror("Can't set signals"); - exit(1); - } + /* Set up handler to kill children upon exit */ + act.sa_flags = 0; + act.sa_handler = fastcgi_cleanup; + if (sigaction(SIGTERM, &act, &old_term) || + sigaction(SIGINT, &act, &old_int) || + sigaction(SIGQUIT, &act, &old_quit) + ) { + perror("Can't set signals"); + exit(1); + } - if (fcgi_in_shutdown()) { - goto parent_out; - } + if (fcgi_in_shutdown()) { + goto parent_out; + } - while (parent) { - do { + while (parent) { + do { #ifdef DEBUG_FASTCGI - fprintf(stderr, "Forking, %d running\n", running); -#endif - pid = fork(); - switch (pid) { - case 0: - /* One of the children. - * Make sure we don't go round the - * fork loop any more - */ - parent = 0; + fprintf(stderr, "Forking, %d running\n", running); +#endif + pid = fork(); + switch (pid) { + case 0: + /* One of the children. + * Make sure we don't go round the + * fork loop any more + */ + parent = 0; - /* don't catch our signals */ - sigaction(SIGTERM, &old_term, 0); - sigaction(SIGQUIT, &old_quit, 0); - sigaction(SIGINT, &old_int, 0); - break; - case -1: - perror("php (pre-forking)"); - exit(1); - break; - default: - /* Fine */ - running++; - break; - } - } while (parent && (running < children)); + /* don't catch our signals */ + sigaction(SIGTERM, &old_term, 0); + sigaction(SIGQUIT, &old_quit, 0); + sigaction(SIGINT, &old_int, 0); + break; + case -1: + perror("php (pre-forking)"); + exit(1); + break; + default: + /* Fine */ + running++; + break; + } + } while (parent && (running < children)); - if (parent) { + if (parent) { #ifdef DEBUG_FASTCGI - fprintf(stderr, "Wait for kids, pid %d\n", getpid()); + fprintf(stderr, "Wait for kids, pid %d\n", getpid()); #endif - parent_waiting = 1; - while (1) { - if (wait(&status) >= 0) { - running--; - break; - } else if (exit_signal) { - break; + parent_waiting = 1; + while (1) { + if (wait(&status) >= 0) { + running--; + break; + } else if (exit_signal) { + break; + } } - } - if (exit_signal) { + if (exit_signal) { #if 0 - while (running > 0) { - while (wait(&status) < 0) { + while (running > 0) { + while (wait(&status) < 0) { + } + running--; } - running--; - } #endif - goto parent_out; + goto parent_out; + } } } + } else { + parent = 0; } - } else { - parent = 0; - } #endif /* WIN32 */ } @@ -2087,8 +2101,8 @@ consult the installation file that came with this distribution, or visit \n\ break; case 'h': case '?': - if (request) { - fcgi_destroy_request(request); + if (request.listen_socket) { + fcgi_destroy_request(&request); } fcgi_shutdown(); no_headers = 1; @@ -2111,9 +2125,9 @@ consult the installation file that came with this distribution, or visit \n\ fcgi_impersonate(); } #endif - while (!fastcgi || fcgi_accept_request(request) >= 0) { - SG(server_context) = fastcgi ? (void *) request : (void *) 1; - init_request_info(request); + while (!fastcgi || fcgi_accept_request(&request) >= 0) { + SG(server_context) = fastcgi ? (void *)&request : (void *) 1; + init_request_info(&request); if (!cgi && !fastcgi) { while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { @@ -2298,7 +2312,7 @@ consult the installation file that came with this distribution, or visit \n\ * get path_translated */ if (php_request_startup() == FAILURE) { if (fastcgi) { - fcgi_finish_request(request, 1); + fcgi_finish_request(&request, 1); } SG(server_context) = NULL; php_module_shutdown(); @@ -2509,7 +2523,7 @@ fastcgi_request_done: /* only fastcgi will get here */ requests++; if (max_requests && (requests == max_requests)) { - fcgi_finish_request(request, 1); + fcgi_finish_request(&request, 1); if (bindpath) { free(bindpath); } @@ -2521,8 +2535,9 @@ fastcgi_request_done: } /* end of fastcgi loop */ } - if (request) { - fcgi_destroy_request(request); + + if (request.listen_socket) { + fcgi_destroy_request(&request); } fcgi_shutdown(); diff --git a/sapi/cgi/config.w32 b/sapi/cgi/config.w32 index daf4f05687..4e803fb1c4 100644 --- a/sapi/cgi/config.w32 +++ b/sapi/cgi/config.w32 @@ -5,6 +5,7 @@ ARG_ENABLE('cgi', 'Build CGI version of PHP', 'yes'); if (PHP_CGI == "yes") { ADD_FLAG("LDFLAGS_CGI", "/stack:67108864"); - SAPI('cgi', 'cgi_main.c fastcgi.c', 'php-cgi.exe', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + SAPI('cgi', 'cgi_main.c', 'php-cgi.exe', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); + ADD_SOURCES('main', 'fastcgi.c', 'cgi'); ADD_FLAG('LIBS_CGI', 'ws2_32.lib kernel32.lib advapi32.lib'); } diff --git a/sapi/cgi/config9.m4 b/sapi/cgi/config9.m4 index a005f8e0ad..e2195f4ee6 100644 --- a/sapi/cgi/config9.m4 +++ b/sapi/cgi/config9.m4 @@ -50,21 +50,21 @@ if test "$PHP_CGI" != "no"; then esac dnl Select SAPI - PHP_SELECT_SAPI(cgi, program, cgi_main.c fastcgi.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1, '$(SAPI_CGI_PATH)') + PHP_SELECT_SAPI(cgi, program, cgi_main.c, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1, '$(SAPI_CGI_PATH)') case $host_alias in *aix*) if test "$php_sapi_module" = "shared"; then - BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" else - BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" fi ;; *darwin*) - BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; *) - BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; esac diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 568fc9f2d1..b07d6858da 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -91,10 +91,6 @@ # define SOCK_EADDRINUSE WSAEADDRINUSE #endif -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - #include "ext/standard/file.h" /* for php_set_sock_blocking() :-( */ #include "zend_smart_str.h" #include "ext/standard/html.h" diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag index 8e3b5e9316..e39fbcb1e8 100644 --- a/sapi/fpm/Makefile.frag +++ b/sapi/fpm/Makefile.frag @@ -1,6 +1,6 @@ fpm: $(SAPI_FPM_PATH) -$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FPM_OBJS) +$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FASTCGI_OBJS) $(PHP_FPM_OBJS) $(BUILD_FPM) install-fpm: $(SAPI_FPM_PATH) diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index b4a4f4fb33..1c496e5a9d 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -678,8 +678,7 @@ if test "$PHP_FPM" != "no"; then PHP_FPM_CFLAGS="-I$abs_srcdir/sapi/fpm" - PHP_FPM_FILES="fpm/fastcgi.c \ - fpm/fpm.c \ + PHP_FPM_FILES="fpm/fpm.c \ fpm/fpm_children.c \ fpm/fpm_cleanup.c \ fpm/fpm_clock.c \ @@ -713,13 +712,13 @@ if test "$PHP_FPM" != "no"; then case $host_alias in *aix*) - BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *darwin*) - BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *) - BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; esac diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c deleted file mode 100644 index 71eaa78cd4..0000000000 --- a/sapi/fpm/fpm/fastcgi.c +++ /dev/null @@ -1,1340 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2015 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@zend.com> | - +----------------------------------------------------------------------+ -*/ - -/* $Id: fastcgi.c 287777 2009-08-26 19:17:32Z pajoye $ */ - -#include "php.h" -#include "fastcgi.h" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdarg.h> -#include <errno.h> -#include <limits.h> - -#include <php_config.h> -#include "fpm.h" -#include "fpm_request.h" -#include "zlog.h" - -#ifdef _WIN32 - -#include <windows.h> - - struct sockaddr_un { - short sun_family; - char sun_path[MAXPATHLEN]; - }; - - static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE; - static int is_impersonate = 0; - -#define FCGI_LOCK(fd) \ - if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ - DWORD ret; \ - while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \ - if (in_shutdown) return -1; \ - } \ - if (ret == WAIT_FAILED) { \ - fprintf(stderr, "WaitForSingleObject() failed\n"); \ - return -1; \ - } \ - } - -#define FCGI_UNLOCK(fd) \ - if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \ - ReleaseMutex(fcgi_accept_mutex); \ - } - -#else - -# include <sys/types.h> -# include <sys/stat.h> -# include <unistd.h> -# include <fcntl.h> -# include <sys/socket.h> -# include <sys/un.h> -# include <netinet/in.h> -# include <arpa/inet.h> -# include <netdb.h> -# include <signal.h> - -# define closesocket(s) close(s) - -# if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) -# include <sys/poll.h> -# endif -# if defined(HAVE_SYS_SELECT_H) -# include <sys/select.h> -# endif - -#ifndef INADDR_NONE -#define INADDR_NONE ((unsigned long) -1) -#endif - -# ifndef HAVE_SOCKLEN_T - typedef unsigned int socklen_t; -# endif - -# ifdef USE_LOCKING -# define FCGI_LOCK(fd) \ - do { \ - struct flock lock; \ - lock.l_type = F_WRLCK; \ - lock.l_start = 0; \ - lock.l_whence = SEEK_SET; \ - lock.l_len = 0; \ - if (fcntl(fd, F_SETLKW, &lock) != -1) { \ - break; \ - } else if (errno != EINTR || in_shutdown) { \ - return -1; \ - } \ - } while (1) - -# define FCGI_UNLOCK(fd) \ - do { \ - int orig_errno = errno; \ - while (1) { \ - struct flock lock; \ - lock.l_type = F_UNLCK; \ - lock.l_start = 0; \ - lock.l_whence = SEEK_SET; \ - lock.l_len = 0; \ - if (fcntl(fd, F_SETLK, &lock) != -1) { \ - break; \ - } else if (errno != EINTR) { \ - return -1; \ - } \ - } \ - errno = orig_errno; \ - } while (0) -# else -# define FCGI_LOCK(fd) -# define FCGI_UNLOCK(fd) -# endif - -#endif - -typedef union _sa_t { - struct sockaddr sa; - struct sockaddr_un sa_unix; - struct sockaddr_in sa_inet; - struct sockaddr_in6 sa_inet6; -} sa_t; - -static HashTable fcgi_mgmt_vars; - -static int is_initialized = 0; -static int in_shutdown = 0; -static sa_t *allowed_clients = NULL; - -static sa_t client_sa; - -/* hash table */ - -#define FCGI_HASH_TABLE_SIZE 128 -#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1) -#define FCGI_HASH_SEG_SIZE 4096 - -typedef struct _fcgi_hash_bucket { - unsigned int hash_value; - unsigned int var_len; - char *var; - unsigned int val_len; - char *val; - struct _fcgi_hash_bucket *next; - struct _fcgi_hash_bucket *list_next; -} fcgi_hash_bucket; - -typedef struct _fcgi_hash_buckets { - unsigned int idx; - struct _fcgi_hash_buckets *next; - struct _fcgi_hash_bucket data[FCGI_HASH_TABLE_SIZE]; -} fcgi_hash_buckets; - -typedef struct _fcgi_data_seg { - char *pos; - char *end; - struct _fcgi_data_seg *next; - char data[1]; -} fcgi_data_seg; - -typedef struct _fcgi_hash { - fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE]; - fcgi_hash_bucket *list; - fcgi_hash_buckets *buckets; - fcgi_data_seg *data; -} fcgi_hash; - -static void fcgi_hash_init(fcgi_hash *h) -{ - memset(h->hash_table, 0, sizeof(h->hash_table)); - h->list = NULL; - h->buckets = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets)); - h->buckets->idx = 0; - h->buckets->next = NULL; - h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + FCGI_HASH_SEG_SIZE); - h->data->pos = h->data->data; - h->data->end = h->data->pos + FCGI_HASH_SEG_SIZE; - h->data->next = NULL; -} - -static void fcgi_hash_destroy(fcgi_hash *h) -{ - fcgi_hash_buckets *b; - fcgi_data_seg *p; - - b = h->buckets; - while (b) { - fcgi_hash_buckets *q = b; - b = b->next; - free(q); - } - p = h->data; - while (p) { - fcgi_data_seg *q = p; - p = p->next; - free(q); - } -} - -static void fcgi_hash_clean(fcgi_hash *h) -{ - memset(h->hash_table, 0, sizeof(h->hash_table)); - h->list = NULL; - /* delete all bucket blocks except the first one */ - while (h->buckets->next) { - fcgi_hash_buckets *q = h->buckets; - - h->buckets = h->buckets->next; - free(q); - } - h->buckets->idx = 0; - /* delete all data segments except the first one */ - while (h->data->next) { - fcgi_data_seg *q = h->data; - - h->data = h->data->next; - free(q); - } - h->data->pos = h->data->data; -} - -static inline char* fcgi_hash_strndup(fcgi_hash *h, char *str, unsigned int str_len) -{ - char *ret; - - if (UNEXPECTED(h->data->pos + str_len + 1 >= h->data->end)) { - unsigned int seg_size = (str_len + 1 > FCGI_HASH_SEG_SIZE) ? str_len + 1 : FCGI_HASH_SEG_SIZE; - fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size); - - p->pos = p->data; - p->end = p->pos + seg_size; - p->next = h->data; - h->data = p; - } - ret = h->data->pos; - memcpy(ret, str, str_len); - ret[str_len] = 0; - h->data->pos += str_len + 1; - return ret; -} - -static char* fcgi_hash_set(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, char *val, unsigned int val_len) -{ - unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; - fcgi_hash_bucket *p = h->hash_table[idx]; - - while (UNEXPECTED(p != NULL)) { - if (UNEXPECTED(p->hash_value == hash_value) && - p->var_len == var_len && - memcmp(p->var, var, var_len) == 0) { - - p->val_len = val_len; - p->val = fcgi_hash_strndup(h, val, val_len); - return p->val; - } - p = p->next; - } - - if (UNEXPECTED(h->buckets->idx >= FCGI_HASH_TABLE_SIZE)) { - fcgi_hash_buckets *b = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets)); - b->idx = 0; - b->next = h->buckets; - h->buckets = b; - } - p = h->buckets->data + h->buckets->idx; - h->buckets->idx++; - p->next = h->hash_table[idx]; - h->hash_table[idx] = p; - p->list_next = h->list; - h->list = p; - p->hash_value = hash_value; - p->var_len = var_len; - p->var = fcgi_hash_strndup(h, var, var_len); - p->val_len = val_len; - p->val = fcgi_hash_strndup(h, val, val_len); - return p->val; -} - -static void fcgi_hash_del(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len) -{ - unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; - fcgi_hash_bucket **p = &h->hash_table[idx]; - - while (*p != NULL) { - if ((*p)->hash_value == hash_value && - (*p)->var_len == var_len && - memcmp((*p)->var, var, var_len) == 0) { - - (*p)->val = NULL; /* NULL value means deleted */ - (*p)->val_len = 0; - *p = (*p)->next; - return; - } - p = &(*p)->next; - } -} - -static char *fcgi_hash_get(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, unsigned int *val_len) -{ - unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; - fcgi_hash_bucket *p = h->hash_table[idx]; - - while (p != NULL) { - if (p->hash_value == hash_value && - p->var_len == var_len && - memcmp(p->var, var, var_len) == 0) { - *val_len = p->val_len; - return p->val; - } - p = p->next; - } - return NULL; -} - -static void fcgi_hash_apply(fcgi_hash *h, fcgi_apply_func func, void *arg) -{ - fcgi_hash_bucket *p = h->list; - - while (p) { - if (EXPECTED(p->val != NULL)) { - func(p->var, p->var_len, p->val, p->val_len, arg); - } - p = p->list_next; - } -} - -struct _fcgi_request { - int listen_socket; -#ifdef _WIN32 - int tcp; -#endif - int fd; - int id; - int keep; -#ifdef TCP_NODELAY - int nodelay; -#endif - int closed; - - int in_len; - int in_pad; - - fcgi_header *out_hdr; - unsigned char *out_pos; - unsigned char out_buf[1024*8]; - unsigned char reserved[sizeof(fcgi_end_request_rec)]; - - int has_env; - fcgi_hash env; -}; - -#ifdef _WIN32 - -static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg) -{ - HANDLE shutdown_event = (HANDLE) arg; - WaitForSingleObject(shutdown_event, INFINITE); - in_shutdown = 1; - return 0; -} - -#else - -static void fcgi_signal_handler(int signo) -{ - if (signo == SIGUSR1 || signo == SIGTERM) { - in_shutdown = 1; - } -} - -static void fcgi_setup_signals(void) -{ - struct sigaction new_sa, old_sa; - - sigemptyset(&new_sa.sa_mask); - new_sa.sa_flags = 0; - new_sa.sa_handler = fcgi_signal_handler; - sigaction(SIGUSR1, &new_sa, NULL); - sigaction(SIGTERM, &new_sa, NULL); - sigaction(SIGPIPE, NULL, &old_sa); - if (old_sa.sa_handler == SIG_DFL) { - sigaction(SIGPIPE, &new_sa, NULL); - } -} -#endif - -int fcgi_in_shutdown(void) -{ - return in_shutdown; -} - -void fcgi_terminate(void) -{ - in_shutdown = 1; -} - -int fcgi_init(void) -{ - if (!is_initialized) { - zend_hash_init(&fcgi_mgmt_vars, 8, NULL, fcgi_free_mgmt_var_cb, 1); - fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1); - - is_initialized = 1; -#ifdef _WIN32 -# if 0 - /* TODO: Support for TCP sockets */ - WSADATA wsaData; - - if (WSAStartup(MAKEWORD(2,0), &wsaData)) { - fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); - return 0; - } -# endif - { - char *str; - DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT; - HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); - - SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); - - str = getenv("_FCGI_SHUTDOWN_EVENT_"); - if (str != NULL) { - HANDLE shutdown_event = (HANDLE) atoi(str); - if (!CreateThread(NULL, 0, fcgi_shutdown_thread, - shutdown_event, 0, NULL)) { - return -1; - } - } - str = getenv("_FCGI_MUTEX_"); - if (str != NULL) { - fcgi_accept_mutex = (HANDLE) atoi(str); - } - return 1; - } -#else - fcgi_setup_signals(); - return 1; -#endif - } - return 1; -} - -void fcgi_set_in_shutdown(int new_value) -{ - in_shutdown = new_value; -} - -void fcgi_shutdown(void) -{ - if (is_initialized) { - zend_hash_destroy(&fcgi_mgmt_vars); - } - if (allowed_clients) { - free(allowed_clients); - } -} - -void fcgi_set_allowed_clients(char *ip) -{ - char *cur, *end; - int n; - - if (ip) { - ip = strdup(ip); - cur = ip; - n = 0; - while (*cur) { - if (*cur == ',') n++; - cur++; - } - if (allowed_clients) free(allowed_clients); - allowed_clients = malloc(sizeof(sa_t) * (n+2)); - n = 0; - cur = ip; - while (cur) { - end = strchr(cur, ','); - if (end) { - *end = 0; - end++; - } - if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { - allowed_clients[n].sa.sa_family = AF_INET; - n++; - } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { - allowed_clients[n].sa.sa_family = AF_INET6; - n++; - } else { - zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); - } - cur = end; - } - allowed_clients[n].sa.sa_family = 0; - free(ip); - if (!n) { - zlog(ZLOG_ERROR, "There are no allowed addresses for this pool"); - /* don't clear allowed_clients as it will create an "open for all" security issue */ - } - } -} - -fcgi_request *fcgi_init_request(int listen_socket) -{ - fcgi_request *req = (fcgi_request*)calloc(1, sizeof(fcgi_request)); - req->listen_socket = listen_socket; - req->fd = -1; - req->id = -1; - - req->in_len = 0; - req->in_pad = 0; - - req->out_hdr = NULL; - req->out_pos = req->out_buf; - -#ifdef _WIN32 - req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); -#endif - -#ifdef TCP_NODELAY - req->nodelay = 0; -#endif - - fcgi_hash_init(&req->env); - - return req; -} - -void fcgi_destroy_request(fcgi_request *req) -{ - fcgi_hash_destroy(&req->env); - free(req); -} - -static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count) -{ - int ret; - size_t n = 0; - - do { - errno = 0; -#ifdef _WIN32 - if (!req->tcp) { - ret = write(req->fd, ((char*)buf)+n, count-n); - } else { - ret = send(req->fd, ((char*)buf)+n, count-n, 0); - if (ret <= 0) { - errno = WSAGetLastError(); - } - } -#else - ret = write(req->fd, ((char*)buf)+n, count-n); -#endif - if (ret > 0) { - n += ret; - } else if (ret <= 0 && errno != 0 && errno != EINTR) { - return ret; - } - } while (n != count); - return n; -} - -static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count) -{ - int ret; - size_t n = 0; - - do { - errno = 0; -#ifdef _WIN32 - if (!req->tcp) { - ret = read(req->fd, ((char*)buf)+n, count-n); - } else { - ret = recv(req->fd, ((char*)buf)+n, count-n, 0); - if (ret <= 0) { - errno = WSAGetLastError(); - } - } -#else - ret = read(req->fd, ((char*)buf)+n, count-n); -#endif - if (ret > 0) { - n += ret; - } else if (ret == 0 && errno == 0) { - return n; - } else if (ret <= 0 && errno != 0 && errno != EINTR) { - return ret; - } - } while (n != count); - return n; -} - -static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len) -{ - int pad = ((len + 7) & ~7) - len; - - hdr->contentLengthB0 = (unsigned char)(len & 0xff); - hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff); - hdr->paddingLength = (unsigned char)pad; - hdr->requestIdB0 = (unsigned char)(req_id & 0xff); - hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff); - hdr->reserved = 0; - hdr->type = type; - hdr->version = FCGI_VERSION_1; - if (pad) { - memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad); - } - return pad; -} - -static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end) -{ - int name_len = 0; - int val_len = 0; - - while (p < end) { - name_len = *p++; - if (UNEXPECTED(name_len >= 128)) { - if (UNEXPECTED(p + 3 >= end)) return 0; - name_len = ((name_len & 0x7f) << 24); - name_len |= (*p++ << 16); - name_len |= (*p++ << 8); - name_len |= *p++; - } - if (UNEXPECTED(p >= end)) return 0; - val_len = *p++; - if (UNEXPECTED(val_len >= 128)) { - if (UNEXPECTED(p + 3 >= end)) return 0; - val_len = ((val_len & 0x7f) << 24); - val_len |= (*p++ << 16); - val_len |= (*p++ << 8); - val_len |= *p++; - } - if (UNEXPECTED(name_len > (INT_MAX - val_len)) || /* would the addition overflow? */ - UNEXPECTED(name_len + val_len > end - p)) { /* would we exceed the buffer? */ - /* Malformated request */ - return 0; - } - - /* - * get the effective length of the name in case it's not a valid string - * don't do this on the value because it can be binary data - */ - if (UNEXPECTED(memchr(p, 0, name_len) != NULL)) { - /* Malicious request */ - return 0; - } - fcgi_hash_set(&req->env, FCGI_HASH_FUNC(p, name_len), (char*)p, name_len, (char*)p + name_len, val_len); - p += name_len + val_len; - } - return 1; -} - -static void fcgi_free_var(zval *zv) -{ - efree(Z_PTR_P(zv)); -} - -static int fcgi_read_request(fcgi_request *req) -{ - fcgi_header hdr; - int len, padding; - unsigned char buf[FCGI_MAX_LENGTH+8]; - - req->keep = 0; - req->closed = 0; - req->in_len = 0; - req->out_hdr = NULL; - req->out_pos = req->out_buf; - req->has_env = 1; - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - - while (hdr.type == FCGI_STDIN && len == 0) { - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - } - - if (len + padding > FCGI_MAX_LENGTH) { - return 0; - } - - req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0; - - if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) { - if (safe_read(req, buf, len+padding) != len+padding) { - return 0; - } - - req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); -#ifdef TCP_NODELAY - if (req->keep && req->tcp && !req->nodelay) { -# ifdef _WIN32 - BOOL on = 1; -# else - int on = 1; -# endif - - setsockopt(req->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on)); - req->nodelay = 1; - } -#endif - switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { - case FCGI_RESPONDER: - fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1); - break; - case FCGI_AUTHORIZER: - fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1); - break; - case FCGI_FILTER: - fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1); - break; - default: - return 0; - } - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - return 0; - } - - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - - while (hdr.type == FCGI_PARAMS && len > 0) { - if (len + padding > FCGI_MAX_LENGTH) { - return 0; - } - - if (safe_read(req, buf, len+padding) != len+padding) { - req->keep = 0; - return 0; - } - - if (!fcgi_get_params(req, buf, buf+len)) { - req->keep = 0; - return 0; - } - - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1) { - req->keep = 0; - return 0; - } - len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - padding = hdr.paddingLength; - } - } else if (hdr.type == FCGI_GET_VALUES) { - unsigned char *p = buf + sizeof(fcgi_header); - zval *value; - unsigned int zlen; - fcgi_hash_bucket *q; - - if (safe_read(req, buf, len+padding) != len+padding) { - req->keep = 0; - return 0; - } - - if (!fcgi_get_params(req, buf, buf+len)) { - req->keep = 0; - return 0; - } - - q = req->env.list; - while (q != NULL) { - if ((value = zend_hash_str_find(&fcgi_mgmt_vars, q->var, q->var_len)) == NULL) { - q = q->list_next; - continue; - } - zlen = (unsigned int)Z_STRLEN_P(value); - if ((p + 4 + 4 + q->var_len + zlen) >= (buf + sizeof(buf))) { - break; - } - if (q->var_len < 0x80) { - *p++ = q->var_len; - } else { - *p++ = ((q->var_len >> 24) & 0xff) | 0x80; - *p++ = (q->var_len >> 16) & 0xff; - *p++ = (q->var_len >> 8) & 0xff; - *p++ = q->var_len & 0xff; - } - if (zlen < 0x80) { - *p++ = zlen; - } else { - *p++ = ((zlen >> 24) & 0xff) | 0x80; - *p++ = (zlen >> 16) & 0xff; - *p++ = (zlen >> 8) & 0xff; - *p++ = zlen & 0xff; - } - memcpy(p, q->var, q->var_len); - p += q->var_len; - memcpy(p, Z_STRVAL_P(value), zlen); - p += zlen; - q = q->list_next; - } - len = p - buf - sizeof(fcgi_header); - len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len); - if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) { - req->keep = 0; - return 0; - } - return 0; - } else { - return 0; - } - - return 1; -} - -int fcgi_read(fcgi_request *req, char *str, int len) -{ - int ret, n, rest; - fcgi_header hdr; - unsigned char buf[255]; - - n = 0; - rest = len; - while (rest > 0) { - if (req->in_len == 0) { - if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) || - hdr.version < FCGI_VERSION_1 || - hdr.type != FCGI_STDIN) { - req->keep = 0; - return 0; - } - req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0; - req->in_pad = hdr.paddingLength; - if (req->in_len == 0) { - return n; - } - } - - if (req->in_len >= rest) { - ret = safe_read(req, str, rest); - } else { - ret = safe_read(req, str, req->in_len); - } - if (ret < 0) { - req->keep = 0; - return ret; - } else if (ret > 0) { - req->in_len -= ret; - rest -= ret; - n += ret; - str += ret; - if (req->in_len == 0) { - if (req->in_pad) { - if (safe_read(req, buf, req->in_pad) != req->in_pad) { - req->keep = 0; - return ret; - } - } - } else { - return n; - } - } else { - return n; - } - } - return n; -} - -void fcgi_close(fcgi_request *req, int force, int destroy) -{ - if (destroy && req->has_env) { - fcgi_hash_clean(&req->env); - req->has_env = 0; - } - -#ifdef _WIN32 - if (is_impersonate && !req->tcp) { - RevertToSelf(); - } -#endif - - if ((force || !req->keep) && req->fd >= 0) { -#ifdef _WIN32 - if (!req->tcp) { - HANDLE pipe = (HANDLE)_get_osfhandle(req->fd); - - if (!force) { - FlushFileBuffers(pipe); - } - DisconnectNamedPipe(pipe); - } else { - if (!force) { - char buf[8]; - - shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} - } - closesocket(req->fd); - } -#else - if (!force) { - char buf[8]; - - shutdown(req->fd, 1); - while (recv(req->fd, buf, sizeof(buf), 0) > 0) {} - } - close(req->fd); -#endif -#ifdef TCP_NODELAY - req->nodelay = 0; -#endif - req->fd = -1; - fpm_request_finished(); - } -} - -int fcgi_is_closed(fcgi_request *req) -{ - return (req->fd < 0); -} - -static int fcgi_is_allowed() { - int i; - - if (client_sa.sa.sa_family == AF_UNIX) { - return 1; - } - if (!allowed_clients) { - return 1; - } - if (client_sa.sa.sa_family == AF_INET) { - for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { - if (allowed_clients[i].sa.sa_family == AF_INET - && !memcmp(&client_sa.sa_inet.sin_addr, &allowed_clients[i].sa_inet.sin_addr, 4)) { - return 1; - } - } - } - if (client_sa.sa.sa_family == AF_INET6) { - for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { - if (allowed_clients[i].sa.sa_family == AF_INET6 - && !memcmp(&client_sa.sa_inet6.sin6_addr, &allowed_clients[i].sa_inet6.sin6_addr, 12)) { - return 1; - } -#ifdef IN6_IS_ADDR_V4MAPPED - if (allowed_clients[i].sa.sa_family == AF_INET - && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr) - && !memcmp(((char *)&client_sa.sa_inet6.sin6_addr)+12, &allowed_clients[i].sa_inet.sin_addr, 4)) { - return 1; - } -#endif - } - } - - zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", fcgi_get_last_client_ip()); - return 0; -} - -int fcgi_accept_request(fcgi_request *req) -{ -#ifdef _WIN32 - HANDLE pipe; - OVERLAPPED ov; -#endif - - while (1) { - if (req->fd < 0) { - while (1) { - if (in_shutdown) { - return -1; - } -#ifdef _WIN32 - if (!req->tcp) { - pipe = (HANDLE)_get_osfhandle(req->listen_socket); - FCGI_LOCK(req->listen_socket); - ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (!ConnectNamedPipe(pipe, &ov)) { - errno = GetLastError(); - if (errno == ERROR_IO_PENDING) { - while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) { - if (in_shutdown) { - CloseHandle(ov.hEvent); - FCGI_UNLOCK(req->listen_socket); - return -1; - } - } - } else if (errno != ERROR_PIPE_CONNECTED) { - } - } - CloseHandle(ov.hEvent); - req->fd = req->listen_socket; - FCGI_UNLOCK(req->listen_socket); - } else { - SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket); -#else - { - int listen_socket = req->listen_socket; -#endif - sa_t sa; - socklen_t len = sizeof(sa); - - fpm_request_accepting(); - - FCGI_LOCK(req->listen_socket); - req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len); - FCGI_UNLOCK(req->listen_socket); - - client_sa = sa; - if (req->fd >= 0 && !fcgi_is_allowed()) { - closesocket(req->fd); - req->fd = -1; - continue; - } - } - -#ifdef _WIN32 - if (req->fd < 0 && (in_shutdown || errno != EINTR)) { -#else - if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) { -#endif - return -1; - } - -#ifdef _WIN32 - break; -#else - if (req->fd >= 0) { -#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL) - struct pollfd fds; - int ret; - - fpm_request_reading_headers(); - - fds.fd = req->fd; - fds.events = POLLIN; - fds.revents = 0; - do { - errno = 0; - ret = poll(&fds, 1, 5000); - } while (ret < 0 && errno == EINTR); - if (ret > 0 && (fds.revents & POLLIN)) { - break; - } - fcgi_close(req, 1, 0); -#else - fpm_request_reading_headers(); - - if (req->fd < FD_SETSIZE) { - struct timeval tv = {5,0}; - fd_set set; - int ret; - - FD_ZERO(&set); - FD_SET(req->fd, &set); - do { - errno = 0; - ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0; - } while (ret < 0 && errno == EINTR); - if (ret > 0 && FD_ISSET(req->fd, &set)) { - break; - } - fcgi_close(req, 1, 0); - } else { - zlog(ZLOG_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded."); - fcgi_close(req, 1, 0); - } -#endif - } -#endif - } - } else if (in_shutdown) { - return -1; - } - if (fcgi_read_request(req)) { -#ifdef _WIN32 - if (is_impersonate && !req->tcp) { - pipe = (HANDLE)_get_osfhandle(req->fd); - if (!ImpersonateNamedPipeClient(pipe)) { - fcgi_close(req, 1, 1); - continue; - } - } -#endif - return req->fd; - } else { - fcgi_close(req, 1, 1); - } - } -} - -static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type) -{ - req->out_hdr = (fcgi_header*) req->out_pos; - req->out_hdr->type = type; - req->out_pos += sizeof(fcgi_header); - return req->out_hdr; -} - -static inline void close_packet(fcgi_request *req) -{ - if (req->out_hdr) { - int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header)); - - req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len); - req->out_hdr = NULL; - } -} - -int fcgi_flush(fcgi_request *req, int close) -{ - int len; - - close_packet(req); - - len = req->out_pos - req->out_buf; - - if (close) { - fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos); - - fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request)); - rec->body.appStatusB3 = 0; - rec->body.appStatusB2 = 0; - rec->body.appStatusB1 = 0; - rec->body.appStatusB0 = 0; - rec->body.protocolStatus = FCGI_REQUEST_COMPLETE; - len += sizeof(fcgi_end_request_rec); - } - - if (safe_write(req, req->out_buf, len) != len) { - req->keep = 0; - req->out_pos = req->out_buf; - return 0; - } - - req->out_pos = req->out_buf; - return 1; -} - -ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len) -{ - int limit, rest; - - if (len <= 0) { - return 0; - } - - if (req->out_hdr && req->out_hdr->type != type) { - close_packet(req); - } - - /* Optimized version */ - limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf); - if (!req->out_hdr) { - limit -= sizeof(fcgi_header); - if (limit < 0) limit = 0; - } - - if (len < limit) { - if (!req->out_hdr) { - open_packet(req, type); - } - memcpy(req->out_pos, str, len); - req->out_pos += len; - } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) { - if (!req->out_hdr) { - open_packet(req, type); - } - if (limit > 0) { - memcpy(req->out_pos, str, limit); - req->out_pos += limit; - } - if (!fcgi_flush(req, 0)) { - return -1; - } - if (len > limit) { - open_packet(req, type); - memcpy(req->out_pos, str + limit, len - limit); - req->out_pos += len - limit; - } - } else { - int pos = 0; - int pad; - - close_packet(req); - while ((len - pos) > 0xffff) { - open_packet(req, type); - fcgi_make_header(req->out_hdr, type, req->id, 0xfff8); - req->out_hdr = NULL; - if (!fcgi_flush(req, 0)) { - return -1; - } - if (safe_write(req, str + pos, 0xfff8) != 0xfff8) { - req->keep = 0; - return -1; - } - pos += 0xfff8; - } - - pad = (((len - pos) + 7) & ~7) - (len - pos); - rest = pad ? 8 - pad : 0; - - open_packet(req, type); - fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest); - req->out_hdr = NULL; - if (!fcgi_flush(req, 0)) { - return -1; - } - if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) { - req->keep = 0; - return -1; - } - if (pad) { - open_packet(req, type); - memcpy(req->out_pos, str + len - rest, rest); - req->out_pos += rest; - } - } - - return len; -} - -int fcgi_finish_request(fcgi_request *req, int force_close) -{ - int ret = 1; - - if (req->fd >= 0) { - if (!req->closed) { - ret = fcgi_flush(req, 1); - req->closed = 1; - } - fcgi_close(req, force_close, 1); - } - return ret; -} - -char* fcgi_getenv(fcgi_request *req, const char* var, int var_len) -{ - unsigned int val_len; - - if (!req) return NULL; - - return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len); -} - -char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value) -{ - unsigned int val_len; - - return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len); -} - -char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val) -{ - if (!req) return NULL; - if (val == NULL) { - fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len); - return NULL; - } else { - return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, (unsigned int)strlen(val)); - } -} - -char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val) -{ - if (val == NULL) { - fcgi_hash_del(&req->env, hash_value, var, var_len); - return NULL; - } else { - return fcgi_hash_set(&req->env, hash_value, var, var_len, val, (unsigned int)strlen(val)); - } -} - -void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array) -{ - fcgi_hash_apply(&req->env, func, array); -} - -void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len) -{ - zval zvalue; - ZVAL_NEW_STR(&zvalue, zend_string_init(value, value_len, 1)); - zend_hash_str_add(&fcgi_mgmt_vars, name, name_len, &zvalue); -} - -void fcgi_free_mgmt_var_cb(zval *zv) -{ - zend_string_free(Z_STR_P(zv)); -} - -const char *fcgi_get_last_client_ip() /* {{{ */ -{ - static char str[INET6_ADDRSTRLEN]; - - /* Ipv4 */ - if (client_sa.sa.sa_family == AF_INET) { - return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet.sin_addr, str, INET6_ADDRSTRLEN); - } -#ifdef IN6_IS_ADDR_V4MAPPED - /* Ipv4-Mapped-Ipv6 */ - if (client_sa.sa.sa_family == AF_INET6 - && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr)) { - return inet_ntop(AF_INET, ((char *)&client_sa.sa_inet6.sin6_addr)+12, str, INET6_ADDRSTRLEN); - } -#endif - /* Ipv6 */ - if (client_sa.sa.sa_family == AF_INET6) { - return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet6.sin6_addr, str, INET6_ADDRSTRLEN); - } - /* Unix socket */ - return NULL; -} -/* }}} */ -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ diff --git a/sapi/fpm/fpm/fastcgi.h b/sapi/fpm/fpm/fastcgi.h deleted file mode 100644 index 31419dfdd0..0000000000 --- a/sapi/fpm/fpm/fastcgi.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2015 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Dmitry Stogov <dmitry@zend.com> | - +----------------------------------------------------------------------+ -*/ - -/* $Id: fastcgi.h 272370 2008-12-31 11:15:49Z sebastian $ */ - -/* FastCGI protocol */ - -#define FCGI_VERSION_1 1 - -#define FCGI_MAX_LENGTH 0xffff - -#define FCGI_KEEP_CONN 1 - -/* this is near the perfect hash function for most useful FastCGI variables - * which combines efficiency and minimal hash collisions - */ - -#define FCGI_HASH_FUNC(var, var_len) \ - (UNEXPECTED(var_len < 3) ? var_len : \ - (((unsigned int)var[3]) << 2) + \ - (((unsigned int)var[var_len-2]) << 4) + \ - (((unsigned int)var[var_len-1]) << 2) + \ - var_len) - -#define FCGI_GETENV(request, name) \ - fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1)) - -#define FCGI_PUTENV(request, name, value) \ - fcgi_quick_putenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1), value) - -typedef enum _fcgi_role { - FCGI_RESPONDER = 1, - FCGI_AUTHORIZER = 2, - FCGI_FILTER = 3 -} fcgi_role; - -typedef enum _fcgi_request_type { - FCGI_BEGIN_REQUEST = 1, /* [in] */ - FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */ - FCGI_END_REQUEST = 3, /* [out] */ - FCGI_PARAMS = 4, /* [in] environment variables */ - FCGI_STDIN = 5, /* [in] post data */ - FCGI_STDOUT = 6, /* [out] response */ - FCGI_STDERR = 7, /* [out] errors */ - FCGI_DATA = 8, /* [in] filter data (not supported) */ - FCGI_GET_VALUES = 9, /* [in] */ - FCGI_GET_VALUES_RESULT = 10 /* [out] */ -} fcgi_request_type; - -typedef enum _fcgi_protocol_status { - FCGI_REQUEST_COMPLETE = 0, - FCGI_CANT_MPX_CONN = 1, - FCGI_OVERLOADED = 2, - FCGI_UNKNOWN_ROLE = 3 -} dcgi_protocol_status; - -typedef struct _fcgi_header { - unsigned char version; - unsigned char type; - unsigned char requestIdB1; - unsigned char requestIdB0; - unsigned char contentLengthB1; - unsigned char contentLengthB0; - unsigned char paddingLength; - unsigned char reserved; -} fcgi_header; - -typedef struct _fcgi_begin_request { - unsigned char roleB1; - unsigned char roleB0; - unsigned char flags; - unsigned char reserved[5]; -} fcgi_begin_request; - -typedef struct _fcgi_begin_request_rec { - fcgi_header hdr; - fcgi_begin_request body; -} fcgi_begin_request_rec; - -typedef struct _fcgi_end_request { - unsigned char appStatusB3; - unsigned char appStatusB2; - unsigned char appStatusB1; - unsigned char appStatusB0; - unsigned char protocolStatus; - unsigned char reserved[3]; -} fcgi_end_request; - -typedef struct _fcgi_end_request_rec { - fcgi_header hdr; - fcgi_end_request body; -} fcgi_end_request_rec; - -/* FastCGI client API */ - -typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg); - -typedef struct _fcgi_request fcgi_request; - -int fcgi_init(void); -void fcgi_shutdown(void); -int fcgi_in_shutdown(void); -void fcgi_terminate(void); -fcgi_request* fcgi_init_request(int listen_socket); -void fcgi_destroy_request(fcgi_request *req); -int fcgi_accept_request(fcgi_request *req); -int fcgi_finish_request(fcgi_request *req, int force_close); - -void fcgi_set_allowed_clients(char *); -void fcgi_close(fcgi_request *req, int force, int destroy); -int fcgi_is_closed(fcgi_request *req); - -char* fcgi_getenv(fcgi_request *req, const char* var, int var_len); -char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val); -char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value); -char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val); -void fcgi_loadenv(fcgi_request *req, fcgi_apply_func load_func, zval *array); - -int fcgi_read(fcgi_request *req, char *str, int len); - -ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len); -int fcgi_flush(fcgi_request *req, int close); - -void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len); -void fcgi_free_mgmt_var_cb(zval *ptr); - -const char *fcgi_get_last_client_ip(); - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index efd1f7155a..001355ec1e 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -219,14 +219,15 @@ static php_cgi_globals_struct php_cgi_globals; #define TRANSLATE_SLASHES(path) #endif -static int print_module_info(zval *zv) +static int print_module_info(zval *zv) /* {{{ */ { zend_module_entry *module = Z_PTR_P(zv); php_printf("%s\n", module->name); return 0; } +/* }}} */ -static int module_name_cmp(const void *a, const void *b) +static int module_name_cmp(const void *a, const void *b) /* {{{ */ { Bucket *f = (Bucket *) a; Bucket *s = (Bucket *) b; @@ -234,8 +235,9 @@ static int module_name_cmp(const void *a, const void *b) return strcasecmp( ((zend_module_entry *) Z_PTR(f->val))->name, ((zend_module_entry *) Z_PTR(s->val))->name); } +/* }}} */ -static void print_modules(void) +static void print_modules(void) /* {{{ */ { HashTable sorted_registry; @@ -245,20 +247,23 @@ static void print_modules(void) zend_hash_apply(&sorted_registry, print_module_info); zend_hash_destroy(&sorted_registry); } +/* }}} */ -static int print_extension_info(zend_extension *ext, void *arg) +static int print_extension_info(zend_extension *ext, void *arg) /* {{{ */ { php_printf("%s\n", ext->name); return 0; } +/* }}} */ -static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) +static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) /* {{{ */ { return strcmp( ((zend_extension *)(*f)->data)->name, ((zend_extension *)(*s)->data)->name); } +/* }}} */ -static void print_extensions(void) +static void print_extensions(void) /* {{{ */ { zend_llist sorted_exts; @@ -268,12 +273,13 @@ static void print_extensions(void) zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL); zend_llist_destroy(&sorted_exts); } +/* }}} */ #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif -static inline size_t sapi_cgibin_single_write(const char *str, uint str_length) +static inline size_t sapi_cgibin_single_write(const char *str, uint str_length) /* {{{ */ { ssize_t ret; @@ -298,8 +304,9 @@ static inline size_t sapi_cgibin_single_write(const char *str, uint str_length) return fwrite(str, 1, MIN(str_length, 16384), stdout); #endif } +/* }}} */ -static size_t sapi_cgibin_ub_write(const char *str, size_t str_length) +static size_t sapi_cgibin_ub_write(const char *str, size_t str_length) /* {{{ */ { const char *ptr = str; uint remaining = str_length; @@ -317,9 +324,9 @@ static size_t sapi_cgibin_ub_write(const char *str, size_t str_length) return str_length; } +/* }}} */ - -static void sapi_cgibin_flush(void *server_context) +static void sapi_cgibin_flush(void *server_context) /* {{{ */ { /* fpm has started, let use fcgi instead of stdout */ if (fpm_is_running) { @@ -339,10 +346,11 @@ static void sapi_cgibin_flush(void *server_context) php_handle_aborted_connection(); } } +/* }}} */ #define SAPI_CGI_MAX_HEADER_LENGTH 1024 -static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) +static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) /* {{{ */ { char buf[SAPI_CGI_MAX_HEADER_LENGTH]; sapi_header_struct *h; @@ -443,12 +451,26 @@ static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers) return SAPI_HEADER_SENT_SUCCESSFULLY; } +/* }}} */ #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif -static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) +#ifndef HAVE_ATTRIBUTE_WEAK +static void fpm_fcgi_log(int type, const char *fmt, ...) /* {{{ */ +#else +void fcgi_log(int type, const char *fmt, ...) +#endif +{ + va_list args; + va_start(args, fmt); + vzlog("", 0, type, fmt, args); + va_end(args); +} +/* }}} */ + +static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) /* {{{ */ { uint read_bytes = 0; int tmp_read_bytes; @@ -486,8 +508,9 @@ static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes) } return read_bytes; } +/* }}} */ -static char *sapi_cgibin_getenv(char *name, size_t name_len) +static char *sapi_cgibin_getenv(char *name, size_t name_len) /* {{{ */ { /* if fpm has started, use fcgi env */ if (fpm_is_running) { @@ -498,8 +521,10 @@ static char *sapi_cgibin_getenv(char *name, size_t name_len) /* if fpm has not started yet, use std env */ return getenv(name); } +/* }}} */ -static char *_sapi_cgibin_putenv(char *name, char *value) +#if 0 +static char *_sapi_cgibin_putenv(char *name, char *value) /* {{{ */ { int name_len; @@ -511,15 +536,18 @@ static char *_sapi_cgibin_putenv(char *name, char *value) fcgi_request *request = (fcgi_request*) SG(server_context); return fcgi_putenv(request, name, name_len, value); } +/* }}} */ +#endif -static char *sapi_cgi_read_cookies(void) +static char *sapi_cgi_read_cookies(void) /* {{{ */ { fcgi_request *request = (fcgi_request*) SG(server_context); return FCGI_GETENV(request, "HTTP_COOKIE"); } +/* }}} */ -static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg) +static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg) /* {{{ */ { zval *array_ptr = (zval*)arg; int filter_arg = (Z_ARR_P(array_ptr) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV]))?PARSE_ENV:PARSE_SERVER; @@ -529,13 +557,11 @@ static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, uns php_register_variable_safe(var, val, new_val_len, array_ptr); } } +/* }}} */ -void cgi_php_import_environment_variables(zval *array_ptr) +void cgi_php_import_environment_variables(zval *array_ptr) /* {{{ */ { fcgi_request *request; - zend_string *var; - char *val; - int filter_arg; if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY && Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) && @@ -559,8 +585,9 @@ void cgi_php_import_environment_variables(zval *array_ptr) request = (fcgi_request*) SG(server_context); fcgi_loadenv(request, cgi_php_load_env_var, array_ptr); } +/* }}} */ -static void sapi_cgi_register_variables(zval *track_vars_array) +static void sapi_cgi_register_variables(zval *track_vars_array) /* {{{ */ { fcgi_request *request = (fcgi_request*) SG(server_context); size_t php_self_len; @@ -601,6 +628,7 @@ static void sapi_cgi_register_variables(zval *track_vars_array) } } } +/* }}} */ /* {{{ sapi_cgi_log_fastcgi * @@ -713,7 +741,7 @@ static void php_cgi_ini_activate_user_config(char *path, int path_len, const cha } /* }}} */ -static int sapi_cgi_activate(void) +static int sapi_cgi_activate(void) /* {{{ */ { fcgi_request *request = (fcgi_request*) SG(server_context); char *path, *doc_root, *server_name; @@ -784,8 +812,9 @@ static int sapi_cgi_activate(void) return SUCCESS; } +/* }}} */ -static int sapi_cgi_deactivate(void) +static int sapi_cgi_deactivate(void) /* {{{ */ { /* flush only when SAPI was started. The reasons are: 1. SAPI Deactivate is called from two places: module init and request shutdown @@ -802,14 +831,16 @@ static int sapi_cgi_deactivate(void) } return SUCCESS; } +/* }}} */ -static int php_cgi_startup(sapi_module_struct *sapi_module) +static int php_cgi_startup(sapi_module_struct *sapi_module) /* {{{ */ { if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) { return FAILURE; } return SUCCESS; } +/* }}} */ /* {{{ sapi_module_struct cgi_sapi_module */ @@ -1360,6 +1391,14 @@ static void init_request_info(void) } /* }}} */ +static void fpm_init_request(fcgi_request *req, int listen_fd) /* {{{ */ { + fcgi_init_request(req, listen_fd); + req->hook.on_accept = fpm_request_accepting; + req->hook.on_read = fpm_request_reading_headers; + req->hook.on_close = fpm_request_finished; +} +/* }}} */ + static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg) /* {{{ */ { int *mode = (int *)arg; @@ -1523,7 +1562,7 @@ int main(int argc, char *argv[]) int max_requests = 500; int requests = 0; int fcgi_fd = 0; - fcgi_request *request; + fcgi_request request; char *fpm_config = NULL; char *fpm_prefix = NULL; char *fpm_pid = NULL; @@ -1553,6 +1592,10 @@ int main(int argc, char *argv[]) cgi_sapi_module.php_ini_path_override = NULL; cgi_sapi_module.php_ini_ignore_cwd = 1; +#ifndef HAVE_ATTRIBUTE_WEAK + fcgi_set_logger(fpm_fcgi_log); +#endif + fcgi_init(); #ifdef PHP_WIN32 @@ -1819,21 +1862,21 @@ consult the installation file that came with this distribution, or visit \n\ php_import_environment_variables = cgi_php_import_environment_variables; /* library is already initialized, now init our request */ - request = fcgi_init_request(fcgi_fd); + fpm_init_request(&request, fcgi_fd); zend_first_try { - while (fcgi_accept_request(request) >= 0) { + while (EXPECTED(fcgi_accept_request(&request) >= 0)) { + char *primary_script = NULL; request_body_fd = -1; - SG(server_context) = (void *) request; + SG(server_context) = (void *) &request; init_request_info(); - char *primary_script = NULL; fpm_request_info(); /* request startup only after we've done all we can to * get path_translated */ - if (php_request_startup() == FAILURE) { - fcgi_finish_request(request, 1); + if (UNEXPECTED(php_request_startup() == FAILURE)) { + fcgi_finish_request(&request, 1); SG(server_context) = NULL; php_module_shutdown(); return FPM_EXIT_SOFTWARE; @@ -1841,16 +1884,16 @@ consult the installation file that came with this distribution, or visit \n\ /* check if request_method has been sent. * if not, it's certainly not an HTTP over fcgi request */ - if (!SG(request_info).request_method) { + if (UNEXPECTED(!SG(request_info).request_method)) { goto fastcgi_request_done; } - if (fpm_status_handle_request()) { + if (UNEXPECTED(fpm_status_handle_request())) { goto fastcgi_request_done; } /* If path_translated is NULL, terminate here with a 404 */ - if (!SG(request_info).path_translated) { + if (UNEXPECTED(!SG(request_info).path_translated)) { zend_try { zlog(ZLOG_DEBUG, "Primary script unknown"); SG(sapi_headers).http_response_code = 404; @@ -1860,7 +1903,7 @@ consult the installation file that came with this distribution, or visit \n\ goto fastcgi_request_done; } - if (fpm_php_limit_extensions(SG(request_info).path_translated)) { + if (UNEXPECTED(fpm_php_limit_extensions(SG(request_info).path_translated))) { SG(sapi_headers).http_response_code = 403; PUTS("Access denied.\n"); goto fastcgi_request_done; @@ -1873,7 +1916,7 @@ consult the installation file that came with this distribution, or visit \n\ primary_script = estrdup(SG(request_info).path_translated); /* path_translated exists, we can continue ! */ - if (php_fopen_primary_script(&file_handle) == FAILURE) { + if (UNEXPECTED(php_fopen_primary_script(&file_handle) == FAILURE)) { zend_try { zlog(ZLOG_ERROR, "Unable to open primary script: %s (%s)", primary_script, strerror(errno)); if (errno == EACCES) { @@ -1897,16 +1940,16 @@ consult the installation file that came with this distribution, or visit \n\ php_execute_script(&file_handle); fastcgi_request_done: - if (primary_script) { + if (EXPECTED(primary_script)) { efree(primary_script); } - if (request_body_fd != -1) { + if (UNEXPECTED(request_body_fd != -1)) { close(request_body_fd); } request_body_fd = -2; - if (EG(exit_status) == 255) { + if (UNEXPECTED(EG(exit_status) == 255)) { if (CGIG(error_header) && *CGIG(error_header)) { sapi_header_line ctr = {0}; @@ -1925,13 +1968,13 @@ fastcgi_request_done: php_request_shutdown((void *) 0); requests++; - if (max_requests && (requests == max_requests)) { - fcgi_finish_request(request, 1); + if (UNEXPECTED(max_requests && (requests == max_requests))) { + fcgi_finish_request(&request, 1); break; } /* end of fastcgi loop */ } - fcgi_destroy_request(request); + fcgi_destroy_request(&request); fcgi_shutdown(); if (cgi_sapi_module.php_ini_path_override) { diff --git a/sapi/fpm/fpm/zlog.c b/sapi/fpm/fpm/zlog.c index 6f17b99133..1659c77efc 100644 --- a/sapi/fpm/fpm/zlog.c +++ b/sapi/fpm/fpm/zlog.c @@ -98,20 +98,20 @@ int zlog_set_level(int new_value) /* {{{ */ } /* }}} */ -void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */ +void vzlog(const char *function, int line, int flags, const char *fmt, va_list args) /* {{{ */ { struct timeval tv; char buf[MAX_LINE_LENGTH]; const size_t buf_size = MAX_LINE_LENGTH; - va_list args; size_t len = 0; int truncated = 0; int saved_errno; if (external_logger) { - va_start(args, fmt); - len = vsnprintf(buf, buf_size, fmt, args); - va_end(args); + va_list ap; + va_copy(ap, args); + len = vsnprintf(buf, buf_size, fmt, ap); + va_end(ap); if (len >= buf_size) { memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1); len = buf_size - 1; @@ -157,9 +157,7 @@ void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* } if (!truncated) { - va_start(args, fmt); len += vsnprintf(buf + len, buf_size - len, fmt, args); - va_end(args); if (len >= buf_size) { truncated = 1; } @@ -197,3 +195,10 @@ void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* } /* }}} */ +void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */ { + va_list args; + va_start(args, fmt); + vzlog(function, line, flags, fmt, args); + va_end(args); +} +/* }}} */ diff --git a/sapi/fpm/fpm/zlog.h b/sapi/fpm/fpm/zlog.h index 1945922da5..c2bf752b93 100644 --- a/sapi/fpm/fpm/zlog.h +++ b/sapi/fpm/fpm/zlog.h @@ -5,6 +5,8 @@ #ifndef ZLOG_H #define ZLOG_H 1 +#include <stdarg.h> + #define zlog(flags,...) zlog_ex(__func__, __LINE__, flags, __VA_ARGS__) struct timeval; @@ -17,6 +19,7 @@ void zlog_set_launched(void); size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len); +void vzlog(const char *function, int line, int flags, const char *fmt, va_list args); void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) __attribute__ ((format(printf,4,5))); @@ -24,6 +27,7 @@ void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) extern const int syslog_priorities[]; #endif +/* keep this same as FCGI_ERROR */ enum { ZLOG_DEBUG = 1, ZLOG_NOTICE = 2, diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 89ef6df3fa..d6d1c107a7 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -3131,10 +3131,20 @@ static int lsapi_initSuEXEC() if ( !s_defaultUid || !s_defaultGid ) { pw = getpwnam( "nobody" ); - if ( !s_defaultUid ) - s_defaultUid = pw->pw_uid; - if ( !s_defaultGid ) - s_defaultGid = pw->pw_gid; + if ( pw ) + { + if ( !s_defaultUid ) + s_defaultUid = pw->pw_uid; + if ( !s_defaultGid ) + s_defaultGid = pw->pw_gid; + } + else + { + if ( !s_defaultUid ) + s_defaultUid = 10000; + if ( !s_defaultGid ) + s_defaultGid = 10000; + } } return 0; } diff --git a/sapi/nsapi/CREDITS b/sapi/nsapi/CREDITS deleted file mode 100644 index 2a05919862..0000000000 --- a/sapi/nsapi/CREDITS +++ /dev/null @@ -1,2 +0,0 @@ -NSAPI -Jayakumar Muthukumarasamy, Uwe Schindler diff --git a/sapi/nsapi/config.m4 b/sapi/nsapi/config.m4 deleted file mode 100644 index 8923f53227..0000000000 --- a/sapi/nsapi/config.m4 +++ /dev/null @@ -1,39 +0,0 @@ -dnl -dnl $Id$ -dnl - -PHP_ARG_WITH(nsapi, for NSAPI support, -[ --with-nsapi=DIR Build PHP as NSAPI module for Netscape/iPlanet/Sun Webserver], no, no) - -if test "$PHP_NSAPI" != "no"; then - if test ! -d $PHP_NSAPI/bin ; then - AC_MSG_ERROR(Please specify the path to the root of your Netscape/iPlanet/Sun Webserver using --with-nsapi=DIR) - fi - AC_MSG_CHECKING([for NSAPI include files]) - if test -d $PHP_NSAPI/include ; then - NSAPI_INC_DIR="$PHP_NSAPI/include" - AC_MSG_RESULT([Netscape 3.x / Sun 7.x style]) - AC_CHECK_HEADERS([$NSAPI_INC_DIR/nsapi.h]) - NSAPI_INCLUDE="-I$NSAPI_INC_DIR" - fi - if test -d $PHP_NSAPI/plugins/include ; then - NSAPI_INC_DIR="$PHP_NSAPI/plugins/include" - AC_MSG_RESULT([iPlanet 4.x / Sun 6.x style]) - AC_CHECK_HEADERS([$NSAPI_INC_DIR/nsapi.h]) - NSAPI_INCLUDE="$NSAPI_INCLUDE -I$NSAPI_INC_DIR" - fi - if test -z "$NSAPI_INCLUDE"; then - AC_MSG_ERROR([Please check you have nsapi.h in either $PHP_NSAPI/include or $PHP_NSAPI/plugins/include]) - fi - - PHP_EVAL_INCLINE($NSAPI_INCLUDE) - PHP_BUILD_THREAD_SAFE - AC_DEFINE(HAVE_NSAPI, 1, [Whether you have a Netscape/iPlanet/Sun Webserver]) - PHP_SELECT_SAPI(nsapi, shared, nsapi.c) - INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PHP_NSAPI/bin/" -fi - - -dnl ## Local Variables: -dnl ## tab-width: 4 -dnl ## End: diff --git a/sapi/nsapi/config.w32 b/sapi/nsapi/config.w32 deleted file mode 100644 index 17b86d20e4..0000000000 --- a/sapi/nsapi/config.w32 +++ /dev/null @@ -1,20 +0,0 @@ -// vim:ft=javascript -// $Id$ - -ARG_ENABLE('nsapi', 'Build NSAPI for Netscape/iPlanet/SunONE webservers', 'no'); - -ARG_WITH('nsapi-includes', 'Where to find NSAPI headers', null); -ARG_WITH('nsapi-libs', 'Where to find NSAPI libraries', null); - -if (PHP_NSAPI != "no") { - if (PHP_ZTS == "no") { - WARNING("NSAPI module requires an --enable-zts build of PHP"); - PHP_NSAPI = "no" - } else if (CHECK_HEADER_ADD_INCLUDE("nsapi.h", "CFLAGS_NSAPI", - PHP_NSAPI + ';' + PHP_NSAPI_INCLUDES) && - CHECK_LIB("ns-httpd*.lib", "nsapi", PHP_NSAPI + ";" + PHP_NSAPI_LIBS)) { - SAPI('nsapi', 'nsapi.c', 'php' + PHP_VERSION + 'nsapi.dll', '/D XP_WIN32 '); - } else { - WARNING("Could not find NSAPI headers/libraries"); - } -} diff --git a/sapi/nsapi/nsapi-readme.txt b/sapi/nsapi/nsapi-readme.txt deleted file mode 100644 index 6bf4afb83d..0000000000 --- a/sapi/nsapi/nsapi-readme.txt +++ /dev/null @@ -1,154 +0,0 @@ -Configuration of your Netscape/iPlanet/Sun Webserver for PHP7 ------------------------------------------------------------------ - -These instructions are targeted at Netscape Enterprise Web Server and -SUN/Netscape Alliance iPlanet Web Server and the new Sun Java System Webserver. -On other web servers your milage may vary. - -Firstly you may need to add some paths to the LD_LIBRARY_PATH -environment for Netscape to find all the shared libs. This is best done -in the start script for your Netscape server. Windows users can -probably skip this step. The start script is located in: - - <path-to-netscape-server>/https-servername/start - - -Netscape/iPlanet/Sun config files are located in: - - <path-to-server>/https-servername/config - - -Add the following line to mime.types (you can do that by the administration server): - - type=magnus-internal/x-httpd-php exts=php - - -Place the following two lines after mime.types init in -<path-to-server>/https-servername/config/obj.conf (for servers < 6) or -for iPlanet/Sun Webserver 6.0 and above however at the end of the -<path-to-server>/https-servername/config/magnus.conf file: - - Init fn="load-modules" funcs="php7_init,php7_execute,php7_auth_trans" shlib="/path/to/phplibrary" - Init fn=php7_init errorString="Failed to initialize PHP!" [php_ini="/path/to/php.ini"] - -The "shlib" will vary depending on your OS: - - Unix: "<path-to-server>/bin/libphp7.so". - Windows: "c:/path/to/php7/php7nsapi.dll" - - -In obj.conf (for virtual server classes [Sun 6.0+] in their vserver.obj.conf): - - <Object name="default"> - . - . - . - # NOTE this next line should happen after all 'ObjectType' and before - # all 'AddLog' lines - # You can modify some entries in php.ini request specific by adding it to the Service - # directive, e.g. doc_root="/path" - # For boolean ini-keys please use 0/1 as value, NOT "On","Off",... (this will not work - # correctly), e.g. zlib.output_compression=1 instead of zlib.output_compression="On" - - Service fn="php7_execute" type="magnus-internal/x-httpd-php" [inikey=value ...] - . - . - . - </Object> - -This is only needed if you want to configure a directory that only consists of -PHP scripts (same like a cgi-bin directory): - - <Object name="x-httpd-php"> - ObjectType fn="force-type" type="magnus-internal/x-httpd-php" - Service fn="php7_execute" [inikey=value ...] - </Object> - -After that you can configure a directory in the Administration server and assign it -the style "x-httpd-php". All files in it will get executed as PHP. This is nice to -hide PHP usage by renaming files to .html - -Note: The stacksize that PHP uses depends on the configuration of the webserver. If you get -crashes with very large PHP scripts, it is recommended to raise it with the Admin Server -(in the section "MAGNUS EDITOR"). - - -Authentication configuration ----------------------------- - -PHP authentication cannot be used with any other authentication. ALL -AUTHENTICATION IS PASSED TO YOUR PHP SCRIPT. To configure PHP -Authentication for the entire server, add the following line: - - <Object name="default"> - AuthTrans fn=php7_auth_trans - . - . - . - . - </Object> - - -To use PHP Authentication on a single directory, add the following: - - <Object ppath="d:\path\to\authenticated\dir\*"> - AuthTrans fn=php7_auth_trans - </Object> - - -Special use for error pages or self-made directory listings ------------------------------------------------------------ - -You can use PHP to generate the error pages for "404 Not Found" -or similar. Add the following line to the object in obj.conf for -every error page you want to overwrite: - - Error fn="php7_execute" code=XXX script="/path/to/script.php" [inikey=value inikey=value...] - -where XXX ist the HTTP error code. Please delete any other Error -directives which could interfere with yours. -If you want to place a page for all errors that could exist, leave -the "code" parameter out. Your script can get the HTTP status code -with $_SERVER['ERROR_TYPE']. - -Another posibility is to generate self-made directory listings. -Just generate a PHP script which displays a directory listing and -replace the corresponding default Service line for -type="magnus-internal/directory" in obj.conf with the following: - - Service fn="php7_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=value inikey=value...] - -For both error and directory listing pages the original URI and -translated URI are in the variables $_SERVER['PATH_INFO'] and -$_SERVER['PATH_TRANSLATED']. - - -Note about nsapi_virtual() and subrequests ------------------------------------------- - -The NSAPI module now supports the nsapi_virtual() function (alias: virtual()) -to make subrequests on the webserver and insert the result in the webpage. -The problem is, that this function uses some undocumented features from -the NSAPI library. - -Under Unix this is not a problem, because the module automatically looks -for the needed functions and uses them if available. If not, nsapi_virtual() -is disabled. - -Under Windows limitations in the DLL handling need the use of a automatic -detection of the most recent ns-httpdXX.dll file. This is tested for servers -till version 6.1. If a newer version of the Sun server is used, the detection -fails and nsapi_virtual() is disabled. - -If this is the case, try the following: -Add the following parameter to php7_init in magnus.conf: - - Init fn=php7_init ... server_lib="ns-httpdXX.dll" - -where XX is the correct DLL version number. To get it, look in the server-root -for the correct DLL name. The DLL with the biggest filesize is the right one. - -But be warned: SUPPORT FOR nsapi_virtual() IS EXPERIMENTAL !!! - - -$Id$ diff --git a/sapi/nsapi/nsapi.c b/sapi/nsapi/nsapi.c deleted file mode 100644 index 4c625794c7..0000000000 --- a/sapi/nsapi/nsapi.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | PHP Version 7 | - +----------------------------------------------------------------------+ - | Copyright (c) 1997-2015 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Author: Jayakumar Muthukumarasamy <jk@kasenna.com> | - | Uwe Schindler <uwe@thetaphi.de> | - +----------------------------------------------------------------------+ -*/ - -/* $Id$ */ - -/* - * PHP includes - */ -#define NSAPI 1 - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_variables.h" -#include "ext/standard/info.h" -#include "php_ini.h" -#include "php_globals.h" -#include "SAPI.h" -#include "php_main.h" -#include "php_version.h" -#include "TSRM.h" -#include "ext/standard/php_standard.h" -#include <sys/types.h> -#include <sys/stat.h> - -#ifndef RTLD_DEFAULT -#define RTLD_DEFAULT NULL -#endif - -/* - * If neither XP_UNIX not XP_WIN32 is defined use PHP_WIN32 - */ -#if !defined(XP_UNIX) && !defined(XP_WIN32) -#ifdef PHP_WIN32 -#define XP_WIN32 -#else -#define XP_UNIX -#endif -#endif - -/* - * The manual define of HPUX is to fix bug #46020, nsapi.h needs this to detect HPUX - */ -#ifdef __hpux -#define HPUX -#endif - -/* - * NSAPI includes - */ -#include "nsapi.h" - -/* fix for gcc4 visibility issue */ -#ifndef PHP_WIN32 -# undef NSAPI_PUBLIC -# define NSAPI_PUBLIC PHPAPI -#endif - -#define NSLS_D struct nsapi_request_context *request_context -#define NSLS_DC , NSLS_D -#define NSLS_C request_context -#define NSLS_CC , NSLS_C -#define NSG(v) (request_context->v) - -/* - * ZTS needs to be defined for NSAPI to work - */ -#if !defined(ZTS) -#error "NSAPI module needs ZTS to be defined" -#endif - -/* - * Structure to encapsulate the NSAPI request in SAPI - */ -typedef struct nsapi_request_context { - pblock *pb; - Session *sn; - Request *rq; - int read_post_bytes; - char *path_info; - int fixed_script; /* 0 if script is from URI, 1 if script is from "script" parameter */ - short http_error; /* 0 in normal mode; for errors the HTTP error code */ -} nsapi_request_context; - -/* - * Mappings between NSAPI names and environment variables. This - * mapping was obtained from the sample programs at the iplanet - * website. - */ -typedef struct nsapi_equiv { - const char *env_var; - const char *nsapi_eq; -} nsapi_equiv; - -static nsapi_equiv nsapi_reqpb[] = { - { "QUERY_STRING", "query" }, - { "REQUEST_LINE", "clf-request" }, - { "REQUEST_METHOD", "method" }, - { "PHP_SELF", "uri" }, - { "SERVER_PROTOCOL", "protocol" } -}; -static size_t nsapi_reqpb_size = sizeof(nsapi_reqpb)/sizeof(nsapi_reqpb[0]); - -static nsapi_equiv nsapi_vars[] = { - { "AUTH_TYPE", "auth-type" }, - { "CLIENT_CERT", "auth-cert" }, - { "REMOTE_USER", "auth-user" } -}; -static size_t nsapi_vars_size = sizeof(nsapi_vars)/sizeof(nsapi_vars[0]); - -static nsapi_equiv nsapi_client[] = { - { "HTTPS_KEYSIZE", "keysize" }, - { "HTTPS_SECRETSIZE", "secret-keysize" }, - { "REMOTE_ADDR", "ip" }, - { "REMOTE_HOST", "ip" } -}; -static size_t nsapi_client_size = sizeof(nsapi_client)/sizeof(nsapi_client[0]); - -/* this parameters to "Service"/"Error" are NSAPI ones which should not be php.ini keys and are excluded */ -static char *nsapi_exclude_from_ini_entries[] = { "fn", "type", "method", "directive", "code", "reason", "script", "bucket", NULL }; - -static void nsapi_free(void *addr) -{ - if (addr != NULL) { - FREE(addr); - } -} - - -/*******************/ -/* PHP module part */ -/*******************/ - -PHP_MINIT_FUNCTION(nsapi); -PHP_MSHUTDOWN_FUNCTION(nsapi); -PHP_RINIT_FUNCTION(nsapi); -PHP_RSHUTDOWN_FUNCTION(nsapi); -PHP_MINFO_FUNCTION(nsapi); - -PHP_FUNCTION(nsapi_virtual); -PHP_FUNCTION(nsapi_request_headers); -PHP_FUNCTION(nsapi_response_headers); - -ZEND_BEGIN_MODULE_GLOBALS(nsapi) - long read_timeout; -ZEND_END_MODULE_GLOBALS(nsapi) - -ZEND_DECLARE_MODULE_GLOBALS(nsapi) - -#define NSAPI_G(v) TSRMG(nsapi_globals_id, zend_nsapi_globals *, v) - - -/* {{{ arginfo */ -ZEND_BEGIN_ARG_INFO_EX(arginfo_nsapi_virtual, 0, 0, 1) - ZEND_ARG_INFO(0, uri) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_nsapi_request_headers, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO(arginfo_nsapi_response_headers, 0) -ZEND_END_ARG_INFO() -/* }}} */ - -/* {{{ nsapi_functions[] - * - * Every user visible function must have an entry in nsapi_functions[]. - */ -const zend_function_entry nsapi_functions[] = { - PHP_FE(nsapi_virtual, arginfo_nsapi_virtual) /* Make subrequest */ - PHP_FALIAS(virtual, nsapi_virtual, arginfo_nsapi_virtual) /* compatibility */ - PHP_FE(nsapi_request_headers, arginfo_nsapi_request_headers) /* get request headers */ - PHP_FALIAS(getallheaders, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */ - PHP_FALIAS(apache_request_headers, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */ - PHP_FE(nsapi_response_headers, arginfo_nsapi_response_headers) /* get response headers */ - PHP_FALIAS(apache_response_headers, nsapi_response_headers, arginfo_nsapi_response_headers) /* compatibility */ - {NULL, NULL, NULL} -}; -/* }}} */ - -/* {{{ nsapi_module_entry - */ -zend_module_entry nsapi_module_entry = { - STANDARD_MODULE_HEADER, - "nsapi", - nsapi_functions, - PHP_MINIT(nsapi), - PHP_MSHUTDOWN(nsapi), - NULL, - NULL, - PHP_MINFO(nsapi), - NO_VERSION_YET, - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -/* {{{ PHP_INI - */ -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("nsapi.read_timeout", "60", PHP_INI_ALL, OnUpdateLong, read_timeout, zend_nsapi_globals, nsapi_globals) -PHP_INI_END() -/* }}} */ - -/* newer servers hide this functions from the programmer so redefine the functions dynamically - thanks to Chris Elving from Sun for the function declarations */ -typedef int (*nsapi_servact_prototype)(Session *sn, Request *rq); -nsapi_servact_prototype nsapi_servact_uri2path = NULL; -nsapi_servact_prototype nsapi_servact_pathchecks = NULL; -nsapi_servact_prototype nsapi_servact_fileinfo = NULL; -nsapi_servact_prototype nsapi_servact_service = NULL; - -#ifdef PHP_WIN32 -/* The following dll-names for nsapi are in use at this time. The undocumented - * servact_* functions are always in the newest one, older ones are supported by - * the server only by wrapping the function table nothing else. So choose - * the newest one found in process space for dynamic linking */ -static char *nsapi_dlls[] = { "ns-httpd40.dll", "ns-httpd36.dll", "ns-httpd35.dll", "ns-httpd30.dll", NULL }; -/* if user specifies an other dll name by server_lib parameter - * it is placed in the following variable and only this DLL is - * checked for the servact_* functions */ -char *nsapi_dll = NULL; -#endif - -/* {{{ php_nsapi_init_dynamic_symbols - */ -static void php_nsapi_init_dynamic_symbols(void) -{ - /* find address of internal NSAPI functions */ -#ifdef PHP_WIN32 - register int i; - DL_HANDLE module = NULL; - if (nsapi_dll) { - /* try user specified server_lib */ - module = GetModuleHandle(nsapi_dll); - if (!module) { - log_error(LOG_WARN, "php7_init", NULL, NULL, "Cannot find DLL specified by server_lib parameter: %s", nsapi_dll); - } - } else { - /* find a LOADED dll module from nsapi_dlls */ - for (i=0; nsapi_dlls[i]; i++) { - if (module = GetModuleHandle(nsapi_dlls[i])) { - break; - } - } - } - if (!module) return; -#else - DL_HANDLE module = RTLD_DEFAULT; -#endif - nsapi_servact_uri2path = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_uri2path"); - nsapi_servact_pathchecks = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_pathchecks"); - nsapi_servact_fileinfo = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_fileinfo"); - nsapi_servact_service = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_service"); - if (!(nsapi_servact_uri2path && nsapi_servact_pathchecks && nsapi_servact_fileinfo && nsapi_servact_service)) { - /* not found - could be cause they are undocumented */ - nsapi_servact_uri2path = NULL; - nsapi_servact_pathchecks = NULL; - nsapi_servact_fileinfo = NULL; - nsapi_servact_service = NULL; - } -} -/* }}} */ - -/* {{{ php_nsapi_init_globals - */ -static void php_nsapi_init_globals(zend_nsapi_globals *nsapi_globals) -{ - nsapi_globals->read_timeout = 60; -} -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(nsapi) -{ - php_nsapi_init_dynamic_symbols(); - ZEND_INIT_MODULE_GLOBALS(nsapi, php_nsapi_init_globals, NULL); - REGISTER_INI_ENTRIES(); - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(nsapi) -{ - UNREGISTER_INI_ENTRIES(); - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(nsapi) -{ - php_info_print_table_start(); - php_info_print_table_row(2, "NSAPI Module Revision", "$Id$"); - php_info_print_table_row(2, "Server Software", system_version()); - php_info_print_table_row(2, "Sub-requests with nsapi_virtual()", - (nsapi_servact_service)?((zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0))?"not supported with zlib.output_compression":"enabled"):"not supported on this platform" ); - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -/* {{{ proto bool nsapi_virtual(string uri) - Perform an NSAPI sub-request */ -/* This function is equivalent to <!--#include virtual...--> - * in SSI. It does an NSAPI sub-request. It is useful - * for including CGI scripts or .shtml files, or anything else - * that you'd parse through webserver. - */ -PHP_FUNCTION(nsapi_virtual) -{ - int uri_len,rv; - char *uri,*value; - Request *rq; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &uri, &uri_len) == FAILURE) { - return; - } - - if (!nsapi_servact_service) { - php_error_docref(NULL, E_WARNING, "Unable to include uri '%s' - Sub-requests not supported on this platform", uri); - RETURN_FALSE; - } else if (zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0)) { - php_error_docref(NULL, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", uri); - RETURN_FALSE; - } else { - php_output_end_all(); - php_header(); - - /* do the sub-request */ - /* thanks to Chris Elving from Sun for this code sniplet */ - if ((rq = request_restart_internal(uri, NULL)) == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to include uri '%s' - Internal request creation failed", uri); - RETURN_FALSE; - } - - /* insert host of current request to get page from same vhost */ - param_free(pblock_remove("host", rq->headers)); - if (value = pblock_findval("host", rc->rq->headers)) { - pblock_nvinsert("host", value, rq->headers); - } - - /* go through the normal request stages as given in obj.conf, - but leave out the logging/error section */ - do { - rv = (*nsapi_servact_uri2path)(rc->sn, rq); - if (rv != REQ_PROCEED) { - continue; - } - - rv = (*nsapi_servact_pathchecks)(rc->sn, rq); - if (rv != REQ_PROCEED) { - continue; - } - - rv = (*nsapi_servact_fileinfo)(rc->sn, rq); - if (rv != REQ_PROCEED) { - continue; - } - - rv = (*nsapi_servact_service)(rc->sn, rq); - } while (rv == REQ_RESTART); - - if (rq->status_num != 200) { - php_error_docref(NULL, E_WARNING, "Unable to include uri '%s' - HTTP status code %d during subrequest", uri, rq->status_num); - request_free(rq); - RETURN_FALSE; - } - - request_free(rq); - - RETURN_TRUE; - } -} -/* }}} */ - -/* {{{ proto array nsapi_request_headers(void) - Get all headers from the request */ -PHP_FUNCTION(nsapi_request_headers) -{ - register int i; - struct pb_entry *entry; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - array_init(return_value); - - for (i=0; i < rc->rq->headers->hsize; i++) { - entry=rc->rq->headers->ht[i]; - while (entry) { - add_assoc_string(return_value, entry->param->name, entry->param->value); - entry=entry->next; - } - } -} -/* }}} */ - -/* {{{ proto array nsapi_response_headers(void) - Get all headers from the response */ -PHP_FUNCTION(nsapi_response_headers) -{ - register int i; - struct pb_entry *entry; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - array_init(return_value); - - for (i=0; i < rc->rq->srvhdrs->hsize; i++) { - entry=rc->rq->srvhdrs->ht[i]; - while (entry) { - add_assoc_string(return_value, entry->param->name, entry->param->value); - entry=entry->next; - } - } -} -/* }}} */ - - -/*************/ -/* SAPI part */ -/*************/ - -static int sapi_nsapi_ub_write(const char *str, unsigned int str_length) -{ - int retval; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (!SG(headers_sent)) { - sapi_send_headers(); - } - - retval = net_write(rc->sn->csd, (char *)str, str_length); - if (retval == IO_ERROR /* -1 */ || retval == IO_EOF /* 0 */) { - php_handle_aborted_connection(); - } - return retval; -} - -/* modified version of apache2 */ -static void sapi_nsapi_flush(void *server_context) -{ - nsapi_request_context *rc = (nsapi_request_context *)server_context; - - if (!rc) { - /* we have no context, so no flushing needed. This fixes a SIGSEGV on shutdown */ - return; - } - - if (!SG(headers_sent)) { - sapi_send_headers(); - } - - /* flushing is only supported in iPlanet servers from version 6.1 on, make it conditional */ -#if NSAPI_VERSION >= 302 - if (net_flush(rc->sn->csd) < 0) { - php_handle_aborted_connection(); - } -#endif -} - -/* callback for zend_llist_apply on SAPI_HEADER_DELETE_ALL operation */ -static int php_nsapi_remove_header(sapi_header_struct *sapi_header) -{ - char *header_name, *p; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */ - header_name = pool_strdup(rc->sn->pool, sapi_header->header); - - /* extract name, this works, if only the header without ':' is given, too */ - if (p = strchr(header_name, ':')) { - *p = 0; - } - - /* header_name to lower case because NSAPI reformats the headers and wants lowercase */ - for (p=header_name; *p; p++) { - *p=tolower(*p); - } - - /* remove the header */ - param_free(pblock_remove(header_name, rc->rq->srvhdrs)); - pool_free(rc->sn->pool, header_name); - - return ZEND_HASH_APPLY_KEEP; -} - -static int sapi_nsapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) -{ - char *header_name, *header_content, *p; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - switch(op) { - case SAPI_HEADER_DELETE_ALL: - /* this only deletes headers set or overwritten by PHP, headers previously set by NSAPI are left intact */ - zend_llist_apply(&sapi_headers->headers, (llist_apply_func_t) php_nsapi_remove_header); - return 0; - - case SAPI_HEADER_DELETE: - /* reuse the zend_llist_apply callback function for this, too */ - php_nsapi_remove_header(sapi_header); - return 0; - - case SAPI_HEADER_ADD: - case SAPI_HEADER_REPLACE: - /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */ - header_name = pool_strdup(rc->sn->pool, sapi_header->header); - - /* split header and align pointer for content */ - header_content = strchr(header_name, ':'); - if (header_content) { - *header_content = 0; - do { - header_content++; - } while (*header_content==' '); - - /* header_name to lower case because NSAPI reformats the headers and wants lowercase */ - for (p=header_name; *p; p++) { - *p=tolower(*p); - } - - /* if REPLACE, remove first. "Content-type" is always removed, as SAPI has a bug according to this */ - if (op==SAPI_HEADER_REPLACE || strcmp(header_name, "content-type")==0) { - param_free(pblock_remove(header_name, rc->rq->srvhdrs)); - } - /* ADD header to nsapi table */ - pblock_nvinsert(header_name, header_content, rc->rq->srvhdrs); - } - - pool_free(rc->sn->pool, header_name); - return SAPI_HEADER_ADD; - - default: - return 0; - } -} - -static int sapi_nsapi_send_headers(sapi_headers_struct *sapi_headers) -{ - int retval; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (SG(sapi_headers).send_default_content_type) { - char *hd; - param_free(pblock_remove("content-type", rc->rq->srvhdrs)); - hd = sapi_get_default_content_type(); - pblock_nvinsert("content-type", hd, rc->rq->srvhdrs); - efree(hd); - } - - protocol_status(rc->sn, rc->rq, SG(sapi_headers).http_response_code, NULL); - retval = protocol_start_response(rc->sn, rc->rq); - - if (retval == REQ_PROCEED || retval == REQ_NOACTION) { - return SAPI_HEADER_SENT_SUCCESSFULLY; - } else { - return SAPI_HEADER_SEND_FAILED; - } -} - -static int sapi_nsapi_read_post(char *buffer, uint count_bytes) -{ - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - char *read_ptr = buffer, *content_length_str = NULL; - uint bytes_read = 0; - int length, content_length = 0; - netbuf *nbuf = rc->sn->inbuf; - - /* - * Yesss! - */ - count_bytes = MIN(count_bytes, SG(request_info).content_length-rc->read_post_bytes); - content_length = SG(request_info).content_length; - - if (content_length <= 0) { - return 0; - } - - /* - * Gobble any pending data in the netbuf. - */ - length = nbuf->cursize - nbuf->pos; - length = MIN(count_bytes, length); - if (length > 0) { - memcpy(read_ptr, nbuf->inbuf + nbuf->pos, length); - bytes_read += length; - read_ptr += length; - content_length -= length; - nbuf->pos += length; - } - - /* - * Read the remaining from the socket. - */ - while (content_length > 0 && bytes_read < count_bytes) { - int bytes_to_read = count_bytes - bytes_read; - - if (content_length < bytes_to_read) { - bytes_to_read = content_length; - } - - length = net_read(rc->sn->csd, read_ptr, bytes_to_read, NSAPI_G(read_timeout)); - - if (length == IO_ERROR || length == IO_EOF) { - break; - } - - bytes_read += length; - read_ptr += length; - content_length -= length; - } - - if ( bytes_read > 0 ) { - rc->read_post_bytes += bytes_read; - } - return bytes_read; -} - -static char *sapi_nsapi_read_cookies(void) -{ - char *cookie_string; - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - cookie_string = pblock_findval("cookie", rc->rq->headers); - return cookie_string; -} - -static void sapi_nsapi_register_server_variables(zval *track_vars_array) -{ - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - register size_t i; - int pos; - char *value,*p; - char buf[32]; - struct pb_entry *entry; - - for (i = 0; i < nsapi_reqpb_size; i++) { - value = pblock_findval(nsapi_reqpb[i].nsapi_eq, rc->rq->reqpb); - if (value) { - php_register_variable((char *)nsapi_reqpb[i].env_var, value, track_vars_array); - } - } - - for (i=0; i < rc->rq->headers->hsize; i++) { - entry=rc->rq->headers->ht[i]; - while (entry) { - if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) { - value=estrdup(entry->param->name); - pos = 0; - } else { - spprintf(&value, 0, "HTTP_%s", entry->param->name); - pos = 5; - } - if (value) { - for(p = value + pos; *p; p++) { - *p = toupper(*p); - if (!isalnum(*p)) { - *p = '_'; - } - } - php_register_variable(value, entry->param->value, track_vars_array); - efree(value); - } - entry=entry->next; - } - } - - for (i = 0; i < nsapi_vars_size; i++) { - value = pblock_findval(nsapi_vars[i].nsapi_eq, rc->rq->vars); - if (value) { - php_register_variable((char *)nsapi_vars[i].env_var, value, track_vars_array); - } - } - - for (i = 0; i < nsapi_client_size; i++) { - value = pblock_findval(nsapi_client[i].nsapi_eq, rc->sn->client); - if (value) { - php_register_variable((char *)nsapi_client[i].env_var, value, track_vars_array); - } - } - - if (value = session_dns(rc->sn)) { - php_register_variable("REMOTE_HOST", value, track_vars_array); - nsapi_free(value); - } - - slprintf(buf, sizeof(buf), "%d", conf_getglobals()->Vport); - php_register_variable("SERVER_PORT", buf, track_vars_array); - php_register_variable("SERVER_NAME", conf_getglobals()->Vserver_hostname, track_vars_array); - - value = http_uri2url_dynamic("", "", rc->sn, rc->rq); - php_register_variable("SERVER_URL", value, track_vars_array); - nsapi_free(value); - - php_register_variable("SERVER_SOFTWARE", system_version(), track_vars_array); - if (security_active) { - php_register_variable("HTTPS", "ON", track_vars_array); - } - php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array); - - /* DOCUMENT_ROOT */ - if (value = request_translate_uri("/", rc->sn)) { - pos = strlen(value); - php_register_variable_safe("DOCUMENT_ROOT", value, pos-1, track_vars_array); - nsapi_free(value); - } - - /* PATH_INFO / PATH_TRANSLATED */ - if (rc->path_info) { - if (value = request_translate_uri(rc->path_info, rc->sn)) { - php_register_variable("PATH_TRANSLATED", value, track_vars_array); - nsapi_free(value); - } - php_register_variable("PATH_INFO", rc->path_info, track_vars_array); - } - - /* Create full Request-URI & Script-Name */ - if (SG(request_info).request_uri) { - pos = strlen(SG(request_info).request_uri); - - if (SG(request_info).query_string) { - spprintf(&value, 0, "%s?%s", SG(request_info).request_uri, SG(request_info).query_string); - if (value) { - php_register_variable("REQUEST_URI", value, track_vars_array); - efree(value); - } - } else { - php_register_variable_safe("REQUEST_URI", SG(request_info).request_uri, pos, track_vars_array); - } - - if (rc->path_info) { - pos -= strlen(rc->path_info); - if (pos<0) { - pos = 0; - } - } - php_register_variable_safe("SCRIPT_NAME", SG(request_info).request_uri, pos, track_vars_array); - } - php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array); - - /* special variables in error mode */ - if (rc->http_error) { - slprintf(buf, sizeof(buf), "%d", rc->http_error); - php_register_variable("ERROR_TYPE", buf, track_vars_array); - } -} - -static void nsapi_log_message(char *message) -{ - nsapi_request_context *rc = (nsapi_request_context *)SG(server_context); - - if (rc) { - log_error(LOG_INFORM, pblock_findval("fn", rc->pb), rc->sn, rc->rq, "%s", message); - } else { - log_error(LOG_INFORM, "php7", NULL, NULL, "%s", message); - } -} - -static double sapi_nsapi_get_request_time(void) -{ - return REQ_TIME( ((nsapi_request_context *)SG(server_context))->rq ); -} - -static int php_nsapi_startup(sapi_module_struct *sapi_module) -{ - if (php_module_startup(sapi_module, &nsapi_module_entry, 1)==FAILURE) { - return FAILURE; - } - return SUCCESS; -} - -static struct stat* sapi_nsapi_get_stat(void) -{ - return request_stat_path( - SG(request_info).path_translated, - ((nsapi_request_context *)SG(server_context))->rq - ); -} - -static sapi_module_struct nsapi_sapi_module = { - "nsapi", /* name */ - "NSAPI", /* pretty name */ - - php_nsapi_startup, /* startup */ - php_module_shutdown_wrapper, /* shutdown */ - - NULL, /* activate */ - NULL, /* deactivate */ - - sapi_nsapi_ub_write, /* unbuffered write */ - sapi_nsapi_flush, /* flush */ - sapi_nsapi_get_stat, /* get uid/stat */ - NULL, /* getenv */ - - php_error, /* error handler */ - - sapi_nsapi_header_handler, /* header handler */ - sapi_nsapi_send_headers, /* send headers handler */ - NULL, /* send header handler */ - - sapi_nsapi_read_post, /* read POST data */ - sapi_nsapi_read_cookies, /* read Cookies */ - - sapi_nsapi_register_server_variables, /* register server variables */ - nsapi_log_message, /* Log message */ - sapi_nsapi_get_request_time, /* Get request time */ - NULL, /* Child terminate */ - - NULL, /* Block interruptions */ - NULL, /* Unblock interruptions */ - - STANDARD_SAPI_MODULE_PROPERTIES -}; - -static void nsapi_php_ini_entries(NSLS_D) -{ - struct pb_entry *entry; - register int i,j,ok; - - for (i=0; i < NSG(pb)->hsize; i++) { - entry=NSG(pb)->ht[i]; - while (entry) { - /* exclude standard entries given to "Service" which should not go into ini entries */ - ok=1; - for (j=0; nsapi_exclude_from_ini_entries[j]; j++) { - ok&=(strcasecmp(entry->param->name, nsapi_exclude_from_ini_entries[j])!=0); - } - - if (ok) { - /* change the ini entry */ - if (zend_alter_ini_entry(entry->param->name, strlen(entry->param->name)+1, - entry->param->value, strlen(entry->param->value), - PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE)==FAILURE) { - log_error(LOG_WARN, pblock_findval("fn", NSG(pb)), NSG(sn), NSG(rq), "Cannot change php.ini key \"%s\" to \"%s\"", entry->param->name, entry->param->value); - } - } - entry=entry->next; - } - } -} - -void NSAPI_PUBLIC php7_close(void *vparam) -{ - if (nsapi_sapi_module.shutdown) { - nsapi_sapi_module.shutdown(&nsapi_sapi_module); - } - - if (nsapi_sapi_module.php_ini_path_override) { - free(nsapi_sapi_module.php_ini_path_override); - } - -#ifdef PHP_WIN32 - if (nsapi_dll) { - free(nsapi_dll); - nsapi_dll = NULL; - } -#endif - - sapi_shutdown(); - tsrm_shutdown(); - - log_error(LOG_INFORM, "php7_close", NULL, NULL, "Shutdown PHP Module"); -} - -/********************************************************* -/ init SAF -/ -/ Init fn="php7_init" [php_ini="/path/to/php.ini"] [server_lib="ns-httpdXX.dll"] -/ Initialize the NSAPI module in magnus.conf -/ -/ php_ini: gives path to php.ini file -/ server_lib: (only Win32) gives name of DLL (without path) to look for -/ servact_* functions -/ -/*********************************************************/ -int NSAPI_PUBLIC php7_init(pblock *pb, Session *sn, Request *rq) -{ - php_core_globals *core_globals; - char *strval; - int threads=128; /* default for server */ - - /* fetch max threads from NSAPI and initialize TSRM with it */ - threads=conf_getglobals()->Vpool_maxthreads; - if (threads<1) { - threads=128; /* default for server */ - } - tsrm_startup(threads, 1, 0, NULL); - - core_globals = ts_resource(core_globals_id); - - /* look if php_ini parameter is given to php7_init */ - if (strval = pblock_findval("php_ini", pb)) { - nsapi_sapi_module.php_ini_path_override = strdup(strval); - } - -#ifdef PHP_WIN32 - /* look if server_lib parameter is given to php7_init - * (this disables the automatic search for the newest ns-httpdXX.dll) */ - if (strval = pblock_findval("server_lib", pb)) { - nsapi_dll = strdup(strval); - } -#endif - - /* start SAPI */ - sapi_startup(&nsapi_sapi_module); - nsapi_sapi_module.startup(&nsapi_sapi_module); - - daemon_atrestart(&php7_close, NULL); - - log_error(LOG_INFORM, pblock_findval("fn", pb), sn, rq, "Initialized PHP Module (%d threads expected)", threads); - return REQ_PROCEED; -} - -/********************************************************* -/ normal use in Service directive: -/ -/ Service fn="php7_execute" type=... method=... [inikey=inivalue inikey=inivalue...] -/ -/ use in Service for a directory to supply a php-made directory listing instead of server default: -/ -/ Service fn="php7_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...] -/ -/ use in Error SAF to display php script as error page: -/ -/ Error fn="php7_execute" code=XXX script="/path/to/script.php" [inikey=inivalue inikey=inivalue...] -/ Error fn="php7_execute" reason="Reason" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...] -/ -/*********************************************************/ -int NSAPI_PUBLIC php7_execute(pblock *pb, Session *sn, Request *rq) -{ - int retval; - nsapi_request_context *request_context; - zend_file_handle file_handle = {0}; - struct stat *fst; - - char *path_info; - char *query_string = pblock_findval("query", rq->reqpb); - char *uri = pblock_findval("uri", rq->reqpb); - char *request_method = pblock_findval("method", rq->reqpb); - char *content_type = pblock_findval("content-type", rq->headers); - char *content_length = pblock_findval("content-length", rq->headers); - char *directive = pblock_findval("Directive", pb); - int error_directive = (directive && !strcasecmp(directive, "error")); - int fixed_script = 1; - - /* try to use script parameter -> Error or Service for directory listing */ - char *path_translated = pblock_findval("script", pb); - - - /* if script parameter is missing: normal use as Service SAF */ - if (!path_translated) { - path_translated = pblock_findval("path", rq->vars); - path_info = pblock_findval("path-info", rq->vars); - fixed_script = 0; - if (error_directive) { - /* go to next error directive if script parameter is missing */ - log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Missing 'script' parameter"); - return REQ_NOACTION; - } - } else { - /* in error the path_info is the uri to the requested page */ - path_info = pblock_findval("uri", rq->reqpb); - } - - /* check if this uri was included in an other PHP script with nsapi_virtual() - by looking for a request context in the current thread */ - if (SG(server_context)) { - /* send 500 internal server error */ - log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot make nesting PHP requests with nsapi_virtual()"); - if (error_directive) { - return REQ_NOACTION; - } else { - protocol_status(sn, rq, 500, NULL); - return REQ_ABORTED; - } - } - - request_context = (nsapi_request_context *)pool_malloc(sn->pool, sizeof(nsapi_request_context)); - if (!request_context) { - log_error(LOG_CATASTROPHE, pblock_findval("fn", pb), sn, rq, "Insufficient memory to process PHP request!"); - return REQ_ABORTED; - } - request_context->pb = pb; - request_context->sn = sn; - request_context->rq = rq; - request_context->read_post_bytes = 0; - request_context->fixed_script = fixed_script; - request_context->http_error = (error_directive) ? rq->status_num : 0; - request_context->path_info = path_info; - - SG(server_context) = request_context; - SG(request_info).query_string = query_string; - SG(request_info).request_uri = uri; - SG(request_info).request_method = request_method; - SG(request_info).path_translated = path_translated; - SG(request_info).content_type = content_type; - SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0); - SG(sapi_headers).http_response_code = (error_directive) ? rq->status_num : 200; - - nsapi_php_ini_entries(NSLS_C); - - php_handle_auth_data(pblock_findval("authorization", rq->headers)); - - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = SG(request_info).path_translated; - file_handle.free_filename = 0; - file_handle.opened_path = NULL; - - fst = request_stat_path(SG(request_info).path_translated, rq); - if (fst && S_ISREG(fst->st_mode)) { - if (php_request_startup() == SUCCESS) { - php_execute_script(&file_handle); - php_request_shutdown(NULL); - retval=REQ_PROCEED; - } else { - /* send 500 internal server error */ - log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot prepare PHP engine!"); - if (error_directive) { - retval=REQ_NOACTION; - } else { - protocol_status(sn, rq, 500, NULL); - retval=REQ_ABORTED; - } - } - } else { - /* send 404 because file not found */ - log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot execute PHP script: %s (File not found)", SG(request_info).path_translated); - if (error_directive) { - retval=REQ_NOACTION; - } else { - protocol_status(sn, rq, 404, NULL); - retval=REQ_ABORTED; - } - } - - pool_free(sn->pool, request_context); - SG(server_context) = NULL; - - return retval; -} - -/********************************************************* -/ authentication -/ -/ we have to make a 'fake' authenticator for netscape so it -/ will pass authentication through to php, and allow us to -/ check authentication with our scripts. -/ -/ php7_auth_trans -/ main function called from netscape server to authenticate -/ a line in obj.conf: -/ funcs=php7_auth_trans shlib="path/to/this/phpnsapi.dll" -/ and: -/ <Object ppath="path/to/be/authenticated/by/php/*"> -/ AuthTrans fn="php7_auth_trans" -/*********************************************************/ -int NSAPI_PUBLIC php7_auth_trans(pblock * pb, Session * sn, Request * rq) -{ - /* This is a DO NOTHING function that allows authentication - * information - * to be passed through to PHP scripts. - */ - return REQ_PROCEED; -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c index c8017a39d3..2e40e49d9b 100644 --- a/sapi/phpdbg/phpdbg_bp.c +++ b/sapi/phpdbg/phpdbg_bp.c @@ -240,7 +240,7 @@ PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num) /* { } path_len = strlen(path); - phpdbg_debug("file path: %s, resolved path: %s, was compiled: %d\n", original_path, path, zend_hash_exists(&PHPDBG_G(file_sources), path, path_len)); + phpdbg_debug("file path: %s, resolved path: %s, was compiled: %d\n", original_path, path, zend_hash_str_exists(&PHPDBG_G(file_sources), path, path_len)); if (!zend_hash_str_exists(&PHPDBG_G(file_sources), path, path_len)) { if (php_stream_stat_path(path, &ssb) == FAILURE) { @@ -308,7 +308,7 @@ PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num) /* { PHPDBG_API HashTable *phpdbg_resolve_pending_file_break_ex(const char *file, uint filelen, zend_string *cur, HashTable *fileht) /* {{{ */ { - phpdbg_debug("file: %s, filelen: %u, cur: %s, curlen %u, pos: %c, memcmp: %d\n", file, filelen, cur, curlen, filelen > curlen ? file[filelen - curlen - 1] : '?', filelen > curlen ? memcmp(file + filelen - curlen, cur, curlen) : 0); + phpdbg_debug("file: %s, filelen: %u, cur: %s, curlen %u, pos: %c, memcmp: %d\n", file, filelen, cur->val, cur->len, filelen > cur->len ? file[filelen - cur->len - 1] : '?', filelen > cur->len ? memcmp(file + filelen - cur->len, cur->val, cur->len) : 0); if (((cur->len < filelen && file[filelen - cur->len - 1] == '/') || filelen == cur->len) && !memcmp(file + filelen - cur->len, cur->val, cur->len)) { phpdbg_breakfile_t *brake, new_brake; diff --git a/sapi/phpdbg/phpdbg_io.c b/sapi/phpdbg/phpdbg_io.c index 1f004fbae1..70709eadc1 100644 --- a/sapi/phpdbg/phpdbg_io.c +++ b/sapi/phpdbg/phpdbg_io.c @@ -178,11 +178,17 @@ PHPDBG_API int phpdbg_send_bytes(int sock, const char *ptr, int len) { PHPDBG_API int phpdbg_mixed_read(int sock, char *ptr, int len, int tmo) { + int ret; + if (PHPDBG_G(flags) & PHPDBG_IS_REMOTE) { return phpdbg_consume_bytes(sock, ptr, len, tmo); } - return read(sock, ptr, len); + do { + ret = read(sock, ptr, len); + } while (ret == -1 && errno == EINTR); + + return ret; } diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index 7dadc3f4b9..cff756a5ed 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -106,8 +106,7 @@ static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t } break; case IS_UNUSED: - asprintf(&decode, "<unused>"); - break; + return NULL; } return decode; } /* }}} */ @@ -116,43 +115,73 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars) /*{ { char *decode[4] = {NULL, NULL, NULL, NULL}; + /* OP1 */ switch (op->opcode) { case ZEND_JMP: case ZEND_GOTO: case ZEND_FAST_CALL: asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes); - goto format; + break; - case ZEND_JMPZNZ: + case ZEND_INIT_FCALL: + case ZEND_RECV: + case ZEND_RECV_INIT: + case ZEND_RECV_VARIADIC: + asprintf(&decode[1], "%" PRIu32, op->op1.num); + break; + + default: decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars); + break; + } + + /* OP2 */ + switch (op->opcode) { + /* TODO: ZEND_FAST_CALL, ZEND_FAST_RET op2 */ + case ZEND_JMPZNZ: asprintf(&decode[2], "J%u or J%" PRIu32, op->op2.opline_num, op->extended_value); - goto result; + break; case ZEND_JMPZ: case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: case ZEND_JMP_SET: - decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars); + case ZEND_ASSERT_CHECK: asprintf(&decode[2], "J%ld", OP_JMP_ADDR(op, op->op2) - ops->opcodes); - goto result; + break; - case ZEND_RECV_INIT: - goto result; + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_REF: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_USER: + asprintf(&decode[2], "%" PRIu32, op->op2.num); + break; default: - decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type, vars); decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type, vars); -result: + break; + } + + /* RESULT */ + switch (op->opcode) { + case ZEND_CATCH: + asprintf(&decode[2], "%" PRIu32, op->result.num); + break; + default: decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type, vars); -format: - asprintf(&decode[0], - "%-20s %-20s %-20s", - decode[1] ? decode[1] : "", - decode[2] ? decode[2] : "", - decode[3] ? decode[3] : ""); + break; } + asprintf(&decode[0], + "%-20s %-20s %-20s", + decode[1] ? decode[1] : "", + decode[2] ? decode[2] : "", + decode[3] ? decode[3] : ""); + if (decode[1]) free(decode[1]); if (decode[2]) diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c index 11bfdf5a81..4013c0fd88 100644 --- a/sapi/phpdbg/phpdbg_print.c +++ b/sapi/phpdbg/phpdbg_print.c @@ -282,12 +282,13 @@ void phpdbg_print_opcodes_function(const char *function, size_t len) { } void phpdbg_print_opcodes_method(const char *class, const char *function) { - zend_class_entry *ce = zend_hash_str_find_ptr(EG(class_table), class, strlen(class)); + zend_class_entry *ce; zend_function *func; - if (!ce) { + if (phpdbg_safe_class_lookup(class, strlen(class), &ce) != SUCCESS) { return; } + if (ce->type != ZEND_USER_CLASS) { phpdbg_out("function name: %s::%s (internal)\n", class, function); return; @@ -348,7 +349,6 @@ void phpdbg_print_opcodes_class(const char *class) { PHPDBG_API void phpdbg_print_opcodes(char *function) { char *method_name; - strtok(function, ":"); if (function == NULL) { @@ -376,7 +376,7 @@ PHPDBG_API void phpdbg_print_opcodes(char *function) } ZEND_HASH_FOREACH_END(); } else if ((method_name = strtok(NULL, ":")) == NULL) { phpdbg_print_opcodes_function(function, strlen(function)); - } else if (++method_name == NULL || ++method_name == NULL) { + } else if ((method_name + 1) == NULL) { phpdbg_print_opcodes_class(function); } else { phpdbg_print_opcodes_method(function, method_name); diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c index 9d63fcf727..4cd8ce2782 100644 --- a/sapi/phpdbg/phpdbg_utils.c +++ b/sapi/phpdbg/phpdbg_utils.c @@ -384,7 +384,7 @@ int phpdbg_safe_class_lookup(const char *name, int name_length, zend_class_entry efree(str_name); } - return ce ? SUCCESS : FAILURE; + return *ce ? SUCCESS : FAILURE; } char *phpdbg_get_property_key(char *key) { diff --git a/sapi/phpdbg/tests/commands/0102_print.test b/sapi/phpdbg/tests/commands/0102_print.test index 6ca60dbbad..7078b13ea2 100644 --- a/sapi/phpdbg/tests/commands/0102_print.test +++ b/sapi/phpdbg/tests/commands/0102_print.test @@ -6,14 +6,14 @@ ################################################# #[User Class: test (3 methods)] #L%d-%d test::testMethod() %s - 0x%s + 1 ops -# L%d #0 RETURN null <unused> <unused> +# L%d #0 RETURN null #L%d-%d test::testPrivateMethod() %s - 0x%s + 1 ops -# L%d #0 RETURN null <unused> <unused> +# L%d #0 RETURN null #L%d-%d test::testProtectedMethod() %s - 0x%s + 1 ops -# L%d #0 RETURN null <unused> <unused> +# L%d #0 RETURN null #[User Method testMethod (1 ops)] #L%d-%d test::testMethod() %s - 0x%s + 1 ops -# L%d #0 RETURN null <unused> <unused> +# L%d #0 RETURN null ################################################# <: class test { diff --git a/sapi/tests/test005.phpt b/sapi/tests/test005.phpt index 7415b66a0a..85143d5228 100644 --- a/sapi/tests/test005.phpt +++ b/sapi/tests/test005.phpt @@ -22,6 +22,6 @@ END; echo "HELLO"; ?> --EXPECTHEADERS-- -Status: 404 +Status: 404 Not Found --EXPECT-- -No input file specified.
\ No newline at end of file +No input file specified. diff --git a/sapi/tests/test006.phpt b/sapi/tests/test006.phpt index 45e37811ef..16d1b2b840 100644 --- a/sapi/tests/test006.phpt +++ b/sapi/tests/test006.phpt @@ -67,7 +67,7 @@ Array [type] => application/octet-stream [tmp_name] => %s [error] => 0 - [size] => 21 + [size] => 19 ) ) diff --git a/tests/basic/bug51709_1.phpt b/tests/basic/bug51709_1.phpt deleted file mode 100644 index 3f2d544e54..0000000000 --- a/tests/basic/bug51709_1.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -Bug #51709 (Can't use keywords as method names) ---FILE-- -<?php - -class foo { - static function for() { - echo "1"; - } -} - -?> -===DONE=== -<?php exit(0); ?> ---EXPECTF-- -Parse error: syntax error, unexpected %s, expecting %s in %sbug51709_1.php on line %d diff --git a/tests/basic/bug51709_2.phpt b/tests/basic/bug51709_2.phpt deleted file mode 100644 index bb1f91cc4c..0000000000 --- a/tests/basic/bug51709_2.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -Bug #51709 (Can't use keywords as method names) ---FILE-- -<?php - -class foo { - static function goto() { - echo "1"; - } -} - -?> -===DONE=== -<?php exit(0); ?> ---EXPECTF-- -Parse error: syntax error, unexpected %s, expecting %s in %sbug51709_2.php on line %d diff --git a/tests/classes/autoload_009.phpt b/tests/classes/autoload_009.phpt index 51d3e8f791..150f3ec201 100644 --- a/tests/classes/autoload_009.phpt +++ b/tests/classes/autoload_009.phpt @@ -16,6 +16,6 @@ Ensure type hints for unknown types do not trigger autoload. --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to f() must be an instance of UndefClass, instance of stdClass given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): f() +#0 %s(%d): f(Object(stdClass)) #1 {main} thrown in %s on line %d diff --git a/tests/classes/type_hinting_001.phpt b/tests/classes/type_hinting_001.phpt index d9412293e9..28d1280b79 100644 --- a/tests/classes/type_hinting_001.phpt +++ b/tests/classes/type_hinting_001.phpt @@ -37,6 +37,6 @@ $a->b($b); Fatal error: Uncaught TypeError: Argument 1 passed to FooBar::a() must implement interface Foo, instance of Blort given, called in %s on line 27 and defined in %s:12 Stack trace: -#0 %s(%d): FooBar->a() +#0 %s(%d): FooBar->a(Object(Blort)) #1 {main} thrown in %s on line 12 diff --git a/tests/classes/type_hinting_002.phpt b/tests/classes/type_hinting_002.phpt index 7486824d50..6fefcc8dd4 100644 --- a/tests/classes/type_hinting_002.phpt +++ b/tests/classes/type_hinting_002.phpt @@ -15,6 +15,6 @@ $o->a($o); --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to Foo::a() must be an instance of NonExisting, instance of Foo given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): Foo->a() +#0 %s(%d): Foo->a(Object(Foo)) #1 {main} thrown in %s on line %d diff --git a/tests/classes/type_hinting_003.phpt b/tests/classes/type_hinting_003.phpt index 6038f25a50..50de31e44b 100644 --- a/tests/classes/type_hinting_003.phpt +++ b/tests/classes/type_hinting_003.phpt @@ -59,6 +59,6 @@ array(1) { Fatal error: Uncaught TypeError: Argument 1 passed to Test::f1() must be of the type array, integer given, called in %s on line %d and defined in %s:%d Stack trace: -#0 %s(%d): Test::f1() +#0 %s(%d): Test::f1(1) #1 {main} thrown in %s on line %d diff --git a/tests/lang/bug24658.phpt b/tests/lang/bug24658.phpt index 6229d52a64..b089569d91 100644 --- a/tests/lang/bug24658.phpt +++ b/tests/lang/bug24658.phpt @@ -55,7 +55,7 @@ object(foo)#%d (0) { Fatal error: Uncaught TypeError: Argument 1 passed to typehint() must be an instance of foo, integer given in %s:%d Stack trace: -#0 [internal function]: typehint(1) +#0 [internal function]: typehint(1, 1) #1 %s(%d): array_walk(Array, 'typehint') #2 {main} thrown in %s on line %d diff --git a/tests/lang/catchable_error_001.phpt b/tests/lang/catchable_error_001.phpt index e63e0d7ccc..b11a7c8d96 100644 --- a/tests/lang/catchable_error_001.phpt +++ b/tests/lang/catchable_error_001.phpt @@ -21,6 +21,6 @@ Catchable fatal error [1] --EXPECTF-- Fatal error: Uncaught TypeError: Argument 1 passed to blah() must be an instance of Foo, instance of stdClass given, called in %scatchable_error_001.php on line 15 and defined in %scatchable_error_001.php:5 Stack trace: -#0 %s(%d): blah() +#0 %s(%d): blah(Object(stdClass)) #1 {main} thrown in %scatchable_error_001.php on line 5 diff --git a/tests/lang/string/unicode_escape_incomplete.phpt b/tests/lang/string/unicode_escape_incomplete.phpt index b5baeffd6e..169e6a278c 100644 --- a/tests/lang/string/unicode_escape_incomplete.phpt +++ b/tests/lang/string/unicode_escape_incomplete.phpt @@ -3,6 +3,6 @@ Invalid Unicode escape sequence: Incomplete --FILE-- <?php -var_dunp("\u{blah"); +var_dump("\u{blah"); --EXPECTF-- Parse error: Invalid UTF-8 codepoint escape sequence in %s on line %d diff --git a/tests/lang/type_hints_001.phpt b/tests/lang/type_hints_001.phpt index d487a86a79..e8a2ffa9e3 100644 --- a/tests/lang/type_hints_001.phpt +++ b/tests/lang/type_hints_001.phpt @@ -25,6 +25,6 @@ type_hint_foo($bar); Fatal error: Uncaught TypeError: Argument 1 passed to type_hint_foo() must be an instance of Foo, instance of Bar given, called in %s on line 16 and defined in %s:9 Stack trace: -#0 %s(%d): type_hint_foo() +#0 %s(%d): type_hint_foo(Object(Bar)) #1 {main} thrown in %s on line 9 diff --git a/win32/build/Makefile b/win32/build/Makefile index c24bd70ba6..74fd97b3b2 100644 --- a/win32/build/Makefile +++ b/win32/build/Makefile @@ -89,6 +89,7 @@ $(PHPDLL_RES): win32\build\template.rc win32\build\template.rc $(BUILD_DIR)\$(PHPDLL): generated_files $(PHPDEF) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(PHPDLL_RES) $(MCFILE) + @copy win32\build\default.manifest $(BUILD_DIR)\$(PHPDLL).manifest # @$(CC) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(STATIC_EXT_LIBS) $(LIBS) $(PHPDLL_RES) /link /out:$(BUILD_DIR)\$(PHPDLL) $(PHP7_PGD_OPTION) $(PHP_LDFLAGS) $(LDFLAGS) $(STATIC_EXT_LDFLAGS) @"$(LINK)" $(PHP_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(LIBS) $(PHPDLL_RES) /out:$(BUILD_DIR)\$(PHPDLL) $(PHP7_PGD_OPTION) $(PHP_LDFLAGS) $(LDFLAGS) $(STATIC_EXT_LDFLAGS) -@$(_VC_MANIFEST_EMBED_DLL) @@ -116,7 +117,7 @@ clean: clean-sapi @cd $(BUILD_DIR) @for %D in (_x $(BUILD_DIRS_SUB)) do @if exist %D @del /F /Q %D\*.* > NUL @cd "$(PHP_SRC_DIR)" - -@del /F /Q $(BUILD_DIR)\*.res $(BUILD_DIR)\*.lib $(BUILD_DIR)\*.ilk $(BUILD_DIR)\*.pdb $(BUILD_DIR)\*.exp $(PHPDEF) $(BUILD_DIR)\php-$(PHP_VERSION_STRING)-Win32.zip $(BUILD_DIR)\pecl-$(PHP_VERSION_STRING)-Win32.zip > NUL + -@del /F /Q $(BUILD_DIR)\*.res $(BUILD_DIR)\*.manifest $(BUILD_DIR)\*.lib $(BUILD_DIR)\*.ilk $(BUILD_DIR)\*.pdb $(BUILD_DIR)\*.exp $(PHPDEF) $(BUILD_DIR)\php-$(PHP_VERSION_STRING)-Win32.zip $(BUILD_DIR)\pecl-$(PHP_VERSION_STRING)-Win32.zip > NUL -rd /s /q $(BUILD_DIR)\php-$(PHP_VERSION_STRING) clean-pecl: @@ -192,6 +193,7 @@ build-devel: build-headers build-lib @copy win32\build\Makefile.phpize $(BUILD_DIR_DEV)\script\ /y >nul @copy win32\build\phpize.bat $(BUILD_DIR_DEV)\ /y >nul @copy win32\build\template.rc $(BUILD_DIR_DEV)\build\ /y >nul + @copy win32\build\default.manifest $(BUILD_DIR_DEV)\build\ /y >nul @copy $(BUILD_DIR)\devel\config.phpize.js $(BUILD_DIR_DEV)\script\ /y >nul @copy $(BUILD_DIR)\devel\phpize.js $(BUILD_DIR_DEV)\script\ /y >nul @copy $(BUILD_DIR)\devel\ext_deps.js $(BUILD_DIR_DEV)\script\ /y >nul diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 3c2ff7a0a9..b7e5e49220 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -159,8 +159,8 @@ ADD_SOURCES("main/streams", "streams.c cast.c memory.c filter.c plain_wrapper.c userspace.c transports.c xp_socket.c mmap.c glob_wrapper.c"); ADD_FLAG("CFLAGS_BD_MAIN_STREAMS", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); -ADD_SOURCES("win32", "glob.c readdir.c \ - registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c"); +ADD_SOURCES("win32", "dllmain.c glob.c readdir.c \ + registry.c select.c sendmail.c time.c winutil.c wsyslog.c globals.c getrusage.c"); ADD_FLAG("CFLAGS_BD_WIN32", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); diff --git a/win32/build/config.w32.h.in b/win32/build/config.w32.h.in index 180f470021..3276fea705 100644 --- a/win32/build/config.w32.h.in +++ b/win32/build/config.w32.h.in @@ -181,3 +181,5 @@ #define HAVE_HUGE_VAL_INF 1 +#define HAVE_GETRUSAGE + diff --git a/win32/build/confutils.js b/win32/build/confutils.js index 2005571f36..121f0c2998 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -99,11 +99,11 @@ if (typeof(CWD) == "undefined") { } /* defaults; we pick up the precise versions from configure.in */ -var PHP_VERSION = 5; +var PHP_VERSION = 7; var PHP_MINOR_VERSION = 0; var PHP_RELEASE_VERSION = 0; var PHP_EXTRA_VERSION = ""; -var PHP_VERSION_STRING = "5.0.0"; +var PHP_VERSION_STRING = "7.0.0"; /* Get version numbers and DEFINE as a string */ function get_version_numbers() @@ -936,6 +936,22 @@ function CHECK_HEADER_ADD_INCLUDE(header_name, flag_name, path_to_check, use_env return p; } +/* XXX check whether some manifest was originally supplied, otherwise keep using the default. */ +function generate_version_info_manifest(makefiletarget) +{ + var manifest_name = makefiletarget + ".manifest"; + + if (MODE_PHPIZE) { + MFO.WriteLine("$(BUILD_DIR)\\" + manifest_name + ": " + PHP_DIR + "\\build\\default.manifest"); + MFO.WriteLine("\t@copy " + PHP_DIR + "\\build\\default.manifest $(BUILD_DIR)\\" + makefiletarget + ".manifest"); + } else { + MFO.WriteLine("$(BUILD_DIR)\\" + manifest_name + ": win32\\build\\default.manifest"); + MFO.WriteLine("\t@copy $(PHP_SRC_DIR)\\win32\\build\\default.manifest $(BUILD_DIR)\\" + makefiletarget + ".manifest"); + } + + return manifest_name; +} + /* Emits rule to generate version info for a SAPI * or extension. Returns the name of the .res file * that will be generated */ @@ -1086,13 +1102,15 @@ function SAPI(sapiname, file_list, makefiletarget, cflags, obj_dir) /* generate a .res file containing version information */ resname = generate_version_info_resource(makefiletarget, sapiname, configure_module_dirname, true); + + manifest_name = generate_version_info_manifest(makefiletarget); MFO.WriteLine(makefiletarget + ": $(BUILD_DIR)\\" + makefiletarget); MFO.WriteLine("\t@echo SAPI " + sapiname_for_printing + " build complete"); if (MODE_PHPIZE) { - MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(PHPLIB) $(BUILD_DIR)\\" + resname); + MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); } else { - MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(BUILD_DIR)\\$(PHPLIB) $(BUILD_DIR)\\" + resname); + MFO.WriteLine("$(BUILD_DIR)\\" + makefiletarget + ": $(DEPS_" + SAPI + ") $(" + SAPI + "_GLOBAL_OBJS) $(BUILD_DIR)\\$(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); } if (makefiletarget.match(new RegExp("\\.dll$"))) { @@ -1303,6 +1321,7 @@ function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir) var resname = generate_version_info_resource(dllname, extname, configure_module_dirname, false); var ld = '@"$(LINK)"'; + var manifest_name = generate_version_info_manifest(dllname); ldflags = ""; if (is_pgo_desired(extname) && (PHP_PGI == "yes" || PHP_PGO != "no")) { @@ -1322,10 +1341,10 @@ function EXTENSION(extname, file_list, shared, cflags, dllname, obj_dir) MFO.WriteLine("$(BUILD_DIR)\\" + libname + ": $(BUILD_DIR)\\" + dllname); MFO.WriteBlankLines(1); if (MODE_PHPIZE) { - MFO.WriteLine("$(BUILD_DIR)\\" + dllname + ": $(DEPS_" + EXT + ") $(" + EXT + "_GLOBAL_OBJS) $(PHPLIB) $(BUILD_DIR)\\" + resname); + MFO.WriteLine("$(BUILD_DIR)\\" + dllname + ": $(DEPS_" + EXT + ") $(" + EXT + "_GLOBAL_OBJS) $(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); MFO.WriteLine("\t" + ld + " $(" + EXT + "_GLOBAL_OBJS_RESP) $(PHPLIB) $(LIBS_" + EXT + ") $(LIBS) $(BUILD_DIR)\\" + resname + " /out:$(BUILD_DIR)\\" + dllname + " $(DLL_LDFLAGS) $(LDFLAGS) $(LDFLAGS_" + EXT + ")"); } else { - MFO.WriteLine("$(BUILD_DIR)\\" + dllname + ": $(DEPS_" + EXT + ") $(" + EXT + "_GLOBAL_OBJS) $(BUILD_DIR)\\$(PHPLIB) $(BUILD_DIR)\\" + resname); + MFO.WriteLine("$(BUILD_DIR)\\" + dllname + ": $(DEPS_" + EXT + ") $(" + EXT + "_GLOBAL_OBJS) $(BUILD_DIR)\\$(PHPLIB) $(BUILD_DIR)\\" + resname + " $(BUILD_DIR)\\" + manifest_name); MFO.WriteLine("\t" + ld + " $(" + EXT + "_GLOBAL_OBJS_RESP) $(BUILD_DIR)\\$(PHPLIB) $(LIBS_" + EXT + ") $(LIBS) $(BUILD_DIR)\\" + resname + " /out:$(BUILD_DIR)\\" + dllname + ldflags + " $(DLL_LDFLAGS) $(LDFLAGS) $(LDFLAGS_" + EXT + ")"); } MFO.WriteLine("\t-@$(_VC_MANIFEST_EMBED_DLL)"); @@ -2654,7 +2673,7 @@ function toolset_setup_common_ldlags() function toolset_setup_common_libs() { // urlmon.lib ole32.lib oleaut32.lib uuid.lib gdi32.lib winspool.lib comdlg32.lib - DEFINE("LIBS", "kernel32.lib ole32.lib user32.lib advapi32.lib shell32.lib ws2_32.lib Dnsapi.lib"); + DEFINE("LIBS", "kernel32.lib ole32.lib user32.lib advapi32.lib shell32.lib ws2_32.lib Dnsapi.lib psapi.lib"); } function toolset_setup_build_mode() diff --git a/win32/build/default.manifest b/win32/build/default.manifest new file mode 100644 index 0000000000..77b2a2165a --- /dev/null +++ b/win32/build/default.manifest @@ -0,0 +1,17 @@ +<?xml version='1.0' encoding='UTF-8' standalone='yes'?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + </application> + </compatibility> +</assembly> diff --git a/win32/build/deplister.c b/win32/build/deplister.c index eec014ca69..5b8c60cee9 100644 --- a/win32/build/deplister.c +++ b/win32/build/deplister.c @@ -22,6 +22,7 @@ * module to it's stdout for use by distro/installer building tools */ #include <windows.h> +#include <stdio.h> #include <imagehlp.h> BOOL CALLBACK StatusRoutine(IMAGEHLP_STATUS_REASON reason, diff --git a/win32/build/libs_version.txt b/win32/build/libs_version.txt index 5c598fa68a..7c86290e75 100644 --- a/win32/build/libs_version.txt +++ b/win32/build/libs_version.txt @@ -1,7 +1,7 @@ bz2-1.0.6 cclient-2007f freetype-2.5.5 -icu-54.1 +icu-55.1 jpeglib-9a libcurl-7.40.0 libiconv-1.14 @@ -11,6 +11,6 @@ libpng-1.5.18 libpq-9.4.1 libssh2-1.5.0 libtidy-20090406 -libxslt-1.1.27 +libxslt-1.1.28 libxml-2.9.2 -openssl-1.0.1m +openssl-1.0.2a diff --git a/win32/build/mkdist.php b/win32/build/mkdist.php index 9e4422addc..7b4c057530 100644 --- a/win32/build/mkdist.php +++ b/win32/build/mkdist.php @@ -342,6 +342,22 @@ foreach ($ENCHANT_DLLS as $dll) { } } +$SASL_DLLS = $php_build_dir . "/bin/sasl2/sasl*.dll"; +$fls = glob($SASL_DLLS); +if (!empty($fls)) { + $sasl_dest_dir = "$dist_dir/sasl2"; + if (!file_exists($sasl_dest_dir) || !is_dir($sasl_dest_dir)) { + if (!mkdir("$sasl_dest_dir", 0777, true)) { + echo "WARNING: couldn't create '$sasl_dest_dir' for SASL2 auth plugins "; + } + } + foreach ($fls as $fl) { + if (!copy($fl, "$sasl_dest_dir/" . basename($fl))) { + echo "WARNING: couldn't copy $fl into the $sasl_dest_dir"; + } + } +} + /* and those for pecl */ foreach ($pecl_dll_deps as $dll) { if (in_array($dll, $extra_dll_deps)) { diff --git a/win32/dllmain.c b/win32/dllmain.c new file mode 100644 index 0000000000..92773a0b58 --- /dev/null +++ b/win32/dllmain.c @@ -0,0 +1,74 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Anatol Belski <ab@php.net> | + +----------------------------------------------------------------------+ + */ + +#include <config.w32.h> + +#include <win32/time.h> +#include <php.h> + +#ifdef HAVE_LIBXML +#include <libxml/threads.h> +#endif + +/* TODO this file, or part of it, could be machine generated, to + allow extensions and SAPIs adding their own init stuff. + However expected is that MINIT is enough in most cases. + This file is only useful for some really internal stuff, + eq. initializing something before the DLL even is + available to be called. */ + +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID dummy) +{ + BOOL ret = TRUE; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + ret = ret && php_win32_init_gettimeofday(); + if (!ret) { + fprintf(stderr, "gettimeofday() initialization failed"); + return ret; + } + break; +#if 0 /* prepared */ + case DLL_PROCESS_DETACH: + /* pass */ + break; + + case DLL_THREAD_ATTACH: + /* pass */ + break; + + case DLL_THREAD_DETACH: + /* pass */ + break; +#endif + } + +#ifdef HAVE_LIBXML + /* This imply that only LIBXML_STATIC_FOR_DLL is supported ATM. + If that changes, this place will need some rework. + TODO Also this should be revisited as no initialization + might be needed for TS build (libxml build with TLS + support. */ + ret = ret && xmlDllMain(inst, reason, dummy); +#endif + + return ret; +} + diff --git a/win32/fnmatch.c b/win32/fnmatch.c index 590d23f5f9..c3bc5bc5e2 100644 --- a/win32/fnmatch.c +++ b/win32/fnmatch.c @@ -196,3 +196,12 @@ rangematch(const char *pattern, char test, int flags) } return (ok == negate ? NULL : pattern); } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/getrusage.c b/win32/getrusage.c new file mode 100644 index 0000000000..fea9089fe6 --- /dev/null +++ b/win32/getrusage.c @@ -0,0 +1,84 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Kalle Sommer Nielsen <kalle@php.net> | + +----------------------------------------------------------------------+ + */ + +#include <php.h> +#include <psapi.h> +#include "getrusage.h" + +/* + * Parts of this file is based on code from the OpenVSwitch project, that + * is released under the Apache 2.0 license and is copyright 2014 Nicira, Inc. + * and have been modified to work with PHP. + */ + +static void usage_to_timeval(FILETIME *ft, struct timeval *tv) +{ + ULARGE_INTEGER time; + + time.LowPart = ft->dwLowDateTime; + time.HighPart = ft->dwHighDateTime; + + tv->tv_sec = (zend_long) (time.QuadPart / 10000000); + tv->tv_usec = (zend_long) ((time.QuadPart % 10000000) / 10); +} + +PHPAPI int getrusage(int who, struct rusage *usage) +{ + FILETIME ctime, etime, stime, utime; + + memset(usage, 0, sizeof(struct rusage)); + + if (who == RUSAGE_SELF) { + PROCESS_MEMORY_COUNTERS pmc; + HANDLE proc = GetCurrentProcess(); + + if (!GetProcessTimes(proc, &ctime, &etime, &stime, &utime)) { + return -1; + } else if(!GetProcessMemoryInfo(proc, &pmc, sizeof(pmc))) { + return -1; + } + + usage_to_timeval(&stime, &usage->ru_stime); + usage_to_timeval(&utime, &usage->ru_utime); + + usage->ru_majflt = pmc.PageFaultCount; + usage->ru_maxrss = pmc.PeakWorkingSetSize / 1024; + + return 0; + } else if (who == RUSAGE_THREAD) { + if (!GetThreadTimes(GetCurrentThread(), &ctime, &etime, &stime, &utime)) { + return -1; + } + + usage_to_timeval(&stime, &usage->ru_stime); + usage_to_timeval(&utime, &usage->ru_utime); + + return 0; + } else { + return -1; + } +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/getrusage.h b/win32/getrusage.h new file mode 100644 index 0000000000..8a6b9a7510 --- /dev/null +++ b/win32/getrusage.h @@ -0,0 +1,117 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Kalle Sommer Nielsen <kalle@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef HAVE_GETRUSAGE_H +# define HAVE_GETRUSAGE_H + +/* + * Note + * + * RUSAGE_CHILDREN is not implemented, and the RUSAGE_THREAD will + * therefore instead be used instead to emulate the behavior. + */ + +# define RUSAGE_SELF 0 +# define RUSAGE_CHILDREN 1 + +# define RUSAGE_THREAD RUSAGE_CHILDREN + +/* + * Implementation support + * + * RUSAGE_SELF + * ru_utime + * ru_stime + * ru_majflt + * ru_maxrss + * + * RUSAGE_THREAD + * ru_utime + * ru_stime + * + * Not implemented: + * ru_ixrss (unused) + * ru_idrss (unused) + * ru_isrss (unused) + * ru_minflt + * ru_nswap (unused) + * ru_inblock + * ru_oublock + * ru_msgsnd (unused) + * ru_msgrcv (unused) + * ru_nsignals (unused) + * ru_nvcsw + * ru_nivcsw + */ + +struct rusage +{ + /* User time used */ + struct timeval ru_utime; + + /* System time used */ + struct timeval ru_stime; + + /* Integral max resident set size */ + zend_long ru_maxrss; + + /* Page faults */ + zend_long ru_majflt; +#if 0 + /* Integral shared text memory size */ + zend_long ru_ixrss; + + /* Integral unshared data size */ + zend_long ru_idrss; + + /* Integral unshared stack size */ + zend_long ru_isrss; + + /* Page reclaims */ + zend_long ru_minflt; + + /* Swaps */ + zend_long ru_nswap; + + /* Block input operations */ + zend_long ru_inblock; + + /* Block output operations */ + zend_long ru_oublock; + + /* Messages sent */ + zend_long ru_msgsnd; + + /* Messages received */ + zend_long ru_msgrcv; + + /* Signals received */ + zend_long ru_nsignals; + + /* Voluntary context switches */ + zend_long ru_nvcsw; + + /* Involuntary context switches */ + zend_long ru_nivcsw; +#endif +}; + +PHPAPI int getrusage(int who, struct rusage *usage); + +#endif + diff --git a/win32/glob.c b/win32/glob.c index 43884509ce..8a40f7f506 100644 --- a/win32/glob.c +++ b/win32/glob.c @@ -71,12 +71,6 @@ # define ARG_MAX 14500 # endif #endif -#ifndef S_ISDIR -#define S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR) -#endif -#ifndef S_ISLNK -#define S_ISLNK(m) (0) -#endif #endif #include "php.h" @@ -293,17 +287,19 @@ globexp2(ptr, pattern, pglob, rv) } for (i = 0, pl = pm = ptr; pm <= pe; pm++) { + const Char *pb; + switch (*pm) { case LBRACKET: /* Ignore everything between [] */ - for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++) + for (pb = pm++; *pm != RBRACKET && *pm != EOS; pm++) ; if (*pm == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE */ - pm = pl; + pm = pb; } break; @@ -929,3 +925,12 @@ qprintf(str, s) (void)printf("\n"); } #endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/globals.c b/win32/globals.c index 485f0f31bb..37a55bde46 100644 --- a/win32/globals.c +++ b/win32/globals.c @@ -78,3 +78,11 @@ PHP_RSHUTDOWN_FUNCTION(win32_core_globals) return SUCCESS; } +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/inet.c b/win32/inet.c index 686cf1265c..c0967f6572 100644 --- a/win32/inet.c +++ b/win32/inet.c @@ -1,3 +1,21 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Pierre Joye <pierre@php.net> | + +----------------------------------------------------------------------+ + */ + #include "inet.h" int inet_aton(const char *cp, struct in_addr *inp) { @@ -9,3 +27,12 @@ int inet_aton(const char *cp, struct in_addr *inp) { return 1; } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/inet.h b/win32/inet.h index d71723759f..f2256427ec 100644 --- a/win32/inet.h +++ b/win32/inet.h @@ -1,3 +1,22 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Pierre Joye <pierre@php.net> | + +----------------------------------------------------------------------+ + */ + + #include <php.h> #include <Winsock2.h> diff --git a/win32/install.txt b/win32/install.txt index 3c91aeafd7..d6940b9354 100644 --- a/win32/install.txt +++ b/win32/install.txt @@ -6,7 +6,6 @@ Installing PHP 1. General Installation Considerations 2. Installation on Windows systems - Windows Installer Manual Installation Steps ActiveScript Microsoft IIS @@ -107,9 +106,6 @@ Chapter 2. Installation on Windows systems refer to the supported Windows platforms as Win32. Windows 95 is no longer supported as of PHP 4.3.0. - There are two main ways to install PHP for Windows: either manually or - by using the installer. - If you have Microsoft Visual Studio, you can also build PHP from the original source code. @@ -124,129 +120,6 @@ Chapter 2. Installation on Windows systems optimised. __________________________________________________________________ -Windows Installer (PHP 5.2 and later) - - The Windows PHP installer for later versions of PHP is built using MSI - technology using the Wix Toolkit (http://wix.sourceforge.net/). It will - install and configure PHP and all the built-in and PECL extensions, as - well as configure many of the popular web servers such as IIS, Apache, - and Xitami. - - First, install your selected HTTP (web) server on your system, and make - sure that it works. Then proceed with one of the following install - types. - __________________________________________________________________ - -Normal Install - - Run the MSI installer and follow the instructions provided by the - installation wizard. You will be prompted to select the Web Server you - wish to configure first, along with any configuration details needed. - - You will then be prompted to select which features and extensions you - wish to install and enable. By selecting "Will be installed on local - hard drive" in the drop-down menu for each item you can trigger whether - to install the feature or not. By selecting "Entire feature will be - installed on local hard drive", you will be able to install all - sub-features of the included feature ( for example by selecting this - options for the feature "PDO" you will install all PDO Drivers ). - - Warning - - It is not recommended to install all extensions by default, since many - other them require dependencies from outside PHP in order to function - properly. Instead, use the Installation Repair Mode that can be - triggered thru the 'Add/Remove Programs' control panel to enable or - disable extensions and features after installation. - - The installer then sets up PHP to be used in Windows and the php.ini - file, and configures certain web servers to use PHP. The installer will - currently configure IIS (CGI mode only), Apache, Xitami, and Sambar - Server; if you are using a different web server you'll need to - configure it manually. - __________________________________________________________________ - -Silent Install - - The installer also supports a silent mode, which is helpful for Systems - Administrators to deploy PHP easily. To use silent mode: - msiexec.exe /i php-VERSION-win32-install.msi /q - - You can control the install directory by passing it as a parameter to - the install. For example, to install to e:\php: - msiexec.exe /i php-VERSION-win32-install.msi /q INSTALLDIR=e:\php - - You can also use the same syntax to specify the Apache Configuration - Directory (APACHEDIR), the Sambar Server directory (SAMBARDIR), and the - Xitami Server directory (XITAMIDIR). - - You can also specify what features to install. For example, to install - the mysqli extension and the CGI executable: - msiexec.exe /i php-VERSION-win32-install.msi /q ADDLOCAL=cgi,ext_php_mysqli - - The current list of Features to install is as follows: -MainExecutable - php.exe executable -ScriptExecutable - php-win.exe executable -ext_php_* - the various extensions ( for example: ext_php_mysql for MySQL ) -apache13 - Apache 1.3 module -apache20 - Apache 2.0 module -apache22 - Apache 2,2 module -apacheCGI - Apache CGI executable -iis4ISAPI - IIS ISAPI module -iis4CGI - IIS CGI executable -NSAPI - Sun/iPlanet/Netscape server module -Xitami - Xitami CGI executable -Sambar - Sambar Server ISAPI module -CGI - php-cgi.exe executable -PEAR - PEAR installer -Manual - PHP Manual in CHM Format - - For more information on installing MSI installers from the command - line, visit - http://msdn.microsoft.com/library/en-us/msi/setup/command_line_options. - asp - __________________________________________________________________ - -Windows Installer (PHP 5.1.0 and earlier) - - The Windows PHP installer is available from the downloads page at - http://www.php.net/downloads.php. This installs the CGI version of PHP - and for IIS and Xitami, it configures the web server as well. The - installer does not include any extra external PHP extensions - (php_*.dll) as you'll only find those in the Windows Zip Package and - PECL downloads. - - Note: While the Windows installer is an easy way to make PHP work, - it is restricted in many aspects as, for example, the automatic - setup of extensions is not supported. Use of the installer isn't the - preferred method for installing PHP. - - First, install your selected HTTP (web) server on your system, and make - sure that it works. - - Run the executable installer and follow the instructions provided by - the installation wizard. Two types of installation are supported - - standard, which provides sensible defaults for all the settings it can, - and advanced, which asks questions as it goes along. - - The installation wizard gathers enough information to set up the - php.ini file, and configure certain web servers to use PHP. One of the - web servers the PHP installer does not configure for is Apache, so - you'll need to configure it manually. - - Once the installation has completed, the installer will inform you if - you need to restart your system, restart the server, or just start - using PHP. - - Warning - - Be aware, that this setup of PHP is not secure. If you would like to - have a secure PHP setup, you'd better go on the manual way, and set - every option carefully. This automatically working setup gives you an - instantly working PHP installation, but it is not meant to be used on - online servers. - __________________________________________________________________ - Manual Installation Steps This install guide will help you manually install and configure PHP @@ -254,11 +127,10 @@ Manual Installation Steps download the zip binary distribution from the downloads page at http://www.php.net/downloads.php. - Although there are many all-in-one installation kits, and we also - distribute a PHP installer for Microsoft Windows, we recommend you take - the time to setup PHP yourself as this will provide you with a better - understanding of the system, and enables you to install PHP extensions - easily when needed. + Although there are many all-in-one installation kits, we recommend you + take the time to setup PHP yourself as this will provide you with a + better understanding of the system, and enables you to install PHP + extensions easily when needed. Upgrading from a previous PHP version: Previous editions of the manual suggest moving various ini and DLL files into your SYSTEM @@ -533,10 +405,10 @@ General considerations for all installations of PHP with IIS extensions_dir value is "c:\php\ext" and an example IIS doc_root value is "c:\Inetpub\wwwroot". * PHP extension DLL files, such as php_mysql.dll and php_curl.dll, - are found in the zip package of the PHP download (not the PHP - installer). In PHP 7, many extensions are part of PECL and can be - downloaded in the "Collection of PECL modules" package. Files such - as php_zip.dll and php_ssh2.dll. Download PHP files here. + are found in the zip package of the PHP download. In PHP 7, many + extensions are part of PECL and can be downloaded in the + "Collection of PECL modules" package. Files such as php_zip.dll and + php_ssh2.dll. Download PHP files here. * When defining the executable, the 'check that file exists' box may also be checked. For a small performance penalty, the IIS will check that the script file exists and sort out authentication diff --git a/win32/php_registry.h b/win32/php_registry.h index bce2fe0978..f8a57dbac7 100644 --- a/win32/php_registry.h +++ b/win32/php_registry.h @@ -1,3 +1,21 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Zeev Suraski <zeev@zend.com> | + +----------------------------------------------------------------------+ + */ + #ifndef PHP_REGISTRY_H #define PHP_REGISTRY_H diff --git a/win32/readdir.c b/win32/readdir.c index b94b570cf4..fc2c4357ff 100644 --- a/win32/readdir.c +++ b/win32/readdir.c @@ -166,3 +166,12 @@ int rewinddir(DIR *dp) return 0; } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/registry.c b/win32/registry.c index 016781776c..be16e1880f 100644 --- a/win32/registry.c +++ b/win32/registry.c @@ -1,3 +1,21 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Zeev Suraski <zeev@zend.com> | + +----------------------------------------------------------------------+ + */ + #include "php.h" #include "php_ini.h" #include "php_win32_globals.h" @@ -281,3 +299,12 @@ char *GetIniPathFromRegistry() } return reg_location; } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/sendmail.c b/win32/sendmail.c index b8d18d195e..e6be028091 100644 --- a/win32/sendmail.c +++ b/win32/sendmail.c @@ -977,3 +977,12 @@ static int FormatEmailAddress(char* Buf, char* EmailAddress, char* FormatString) } return snprintf(Buf, MAIL_BUFFER_SIZE , FormatString , EmailAddress ); } /* end FormatEmailAddress() */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/sockets.c b/win32/sockets.c index 4ad84ab946..e56836468a 100644 --- a/win32/sockets.c +++ b/win32/sockets.c @@ -90,3 +90,12 @@ error: WSASetLastError(WSAECONNABORTED); return -1; } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/time.c b/win32/time.c index b65a92c771..dfcc46a59b 100644 --- a/win32/time.c +++ b/win32/time.c @@ -29,10 +29,11 @@ typedef VOID (WINAPI *MyGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTi static MyGetSystemTimeAsFileTime timefunc = NULL; +#ifdef PHP_EXPORTS static zend_always_inline MyGetSystemTimeAsFileTime get_time_func(void) { MyGetSystemTimeAsFileTime timefunc = NULL; - HMODULE hMod = LoadLibrary("kernel32.dll"); + HMODULE hMod = GetModuleHandle("kernel32.dll"); if (hMod) { /* Max possible resolution <1us, win8/server2012 */ @@ -47,15 +48,20 @@ static zend_always_inline MyGetSystemTimeAsFileTime get_time_func(void) return timefunc; } +BOOL php_win32_init_gettimeofday(void) +{ + timefunc = get_time_func(); + + return (NULL != timefunc); +} +#endif + static zend_always_inline int getfilesystemtime(struct timeval *tv) { FILETIME ft; unsigned __int64 ff = 0; ULARGE_INTEGER fft; - if (!timefunc) { - timefunc = get_time_func(); - } timefunc(&ft); /* @@ -116,87 +122,11 @@ PHPAPI int nanosleep( const struct timespec * rqtp, struct timespec * rmtp ) return usleep( rqtp->tv_sec * 1000000 + rqtp->tv_nsec / 1000 ); } -#if 0 /* looks pretty ropey in here */ -#ifdef HAVE_SETITIMER - - -#ifndef THREAD_SAFE -unsigned int proftimer, virttimer, realtimer; -extern LPMSG phpmsg; -#endif - -struct timer_msg { - int signal; - unsigned int threadid; -}; - - -LPTIMECALLBACK setitimer_timeout(UINT uTimerID, UINT info, DWORD dwUser, DWORD dw1, DWORD dw2) -{ - struct timer_msg *msg = (struct timer_msg *) info; - - if (msg) { - raise((int) msg->signal); - PostThreadMessage(msg->threadid, - WM_NOTIFY, msg->signal, 0); - free(msg); - } - return 0; -} - -PHPAPI int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue) -{ - int timeout = value->it_value.tv_sec * 1000 + value->it_value.tv_usec; - int repeat = TIME_ONESHOT; - - /*make sure the message queue is initialized */ - PeekMessage(phpmsg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - if (timeout > 0) { - struct timer_msg *msg = malloc(sizeof(struct timer_msg)); - msg->threadid = GetCurrentThreadId(); - if (!ovalue) { - repeat = TIME_PERIODIC; - } - switch (which) { - case ITIMER_REAL: - msg->signal = SIGALRM; - realtimer = timeSetEvent(timeout, 100, (LPTIMECALLBACK) setitimer_timeout, (UINT) msg, repeat); - break; - case ITIMER_VIRT: - msg->signal = SIGVTALRM; - virttimer = timeSetEvent(timeout, 100, (LPTIMECALLBACK) setitimer_timeout, (UINT) msg, repeat); - break; - case ITIMER_PROF: - msg->signal = SIGPROF; - proftimer = timeSetEvent(timeout, 100, (LPTIMECALLBACK) setitimer_timeout, (UINT) msg, repeat); - break; - default: - errno = EINVAL; - return -1; - break; - } - } else { - switch (which) { - case ITIMER_REAL: - timeKillEvent(realtimer); - break; - case ITIMER_VIRT: - timeKillEvent(virttimer); - break; - case ITIMER_PROF: - timeKillEvent(proftimer); - break; - default: - errno = EINVAL; - return -1; - break; - } - } - - - return 0; -} - -#endif -#endif - +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/time.h b/win32/time.h index 8f39c0481c..b46c1675ca 100644 --- a/win32/time.h +++ b/win32/time.h @@ -52,4 +52,10 @@ PHPAPI int nanosleep( const struct timespec * rqtp, struct timespec * rmtp ); PHPAPI int usleep(unsigned int useconds); +#ifdef PHP_EXPORTS +/* This symbols are needed only for the DllMain, but should not be exported + or be available when used with PHP binaries. */ +BOOL php_win32_init_gettimeofday(void); +#endif + #endif diff --git a/win32/winutil.c b/win32/winutil.c index 488c8cbc57..2faa0d9e7d 100644 --- a/win32/winutil.c +++ b/win32/winutil.c @@ -123,3 +123,11 @@ PHPAPI int php_win32_get_random_bytes(unsigned char *buf, size_t size) { /* {{{ } /* }}} */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/win32/wsyslog.c b/win32/wsyslog.c index 8244fa12f7..6b0f03e8ea 100644 --- a/win32/wsyslog.c +++ b/win32/wsyslog.c @@ -127,3 +127,12 @@ void openlog(const char *ident, int logopt, int facility) PW32G(log_source) = RegisterEventSource(NULL, "PHP-" PHP_VERSION); spprintf(&PW32G(log_header), 0, (logopt & LOG_PID) ? "%s[%d]" : "%s", ident, getpid()); } + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ |